Commit | Line | Data |
---|---|---|
c942fddf | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
f4ff4155 SK |
2 | /* |
3 | * powr1220.c - Driver for the Lattice POWR1220 programmable power supply | |
4 | * and monitor. Users can read all ADC inputs along with their labels | |
5 | * using the sysfs nodes. | |
6 | * | |
ad736c1a | 7 | * Copyright (c) 2014 Echo360 https://www.echo360.com |
f4ff4155 | 8 | * Scott Kanowitz <skanowitz@echo360.com> <scott.kanowitz@gmail.com> |
f4ff4155 SK |
9 | */ |
10 | ||
11 | #include <linux/module.h> | |
12 | #include <linux/init.h> | |
13 | #include <linux/slab.h> | |
14 | #include <linux/jiffies.h> | |
15 | #include <linux/i2c.h> | |
16 | #include <linux/hwmon.h> | |
17 | #include <linux/hwmon-sysfs.h> | |
18 | #include <linux/err.h> | |
19 | #include <linux/mutex.h> | |
20 | #include <linux/delay.h> | |
21 | ||
22 | #define ADC_STEP_MV 2 | |
23 | #define ADC_MAX_LOW_MEASUREMENT_MV 2000 | |
24 | ||
9f93aa10 MS |
25 | enum powr1xxx_chips { powr1014, powr1220 }; |
26 | ||
f4ff4155 SK |
27 | enum powr1220_regs { |
28 | VMON_STATUS0, | |
29 | VMON_STATUS1, | |
30 | VMON_STATUS2, | |
31 | OUTPUT_STATUS0, | |
32 | OUTPUT_STATUS1, | |
33 | OUTPUT_STATUS2, | |
34 | INPUT_STATUS, | |
35 | ADC_VALUE_LOW, | |
36 | ADC_VALUE_HIGH, | |
37 | ADC_MUX, | |
38 | UES_BYTE0, | |
39 | UES_BYTE1, | |
40 | UES_BYTE2, | |
41 | UES_BYTE3, | |
42 | GP_OUTPUT1, | |
43 | GP_OUTPUT2, | |
44 | GP_OUTPUT3, | |
45 | INPUT_VALUE, | |
46 | RESET, | |
47 | TRIM1_TRIM, | |
48 | TRIM2_TRIM, | |
49 | TRIM3_TRIM, | |
50 | TRIM4_TRIM, | |
51 | TRIM5_TRIM, | |
52 | TRIM6_TRIM, | |
53 | TRIM7_TRIM, | |
54 | TRIM8_TRIM, | |
55 | MAX_POWR1220_REGS | |
56 | }; | |
57 | ||
58 | enum powr1220_adc_values { | |
59 | VMON1, | |
60 | VMON2, | |
61 | VMON3, | |
62 | VMON4, | |
63 | VMON5, | |
64 | VMON6, | |
65 | VMON7, | |
66 | VMON8, | |
67 | VMON9, | |
68 | VMON10, | |
69 | VMON11, | |
70 | VMON12, | |
71 | VCCA, | |
72 | VCCINP, | |
73 | MAX_POWR1220_ADC_VALUES | |
74 | }; | |
75 | ||
76 | struct powr1220_data { | |
77 | struct i2c_client *client; | |
78 | struct mutex update_lock; | |
9f93aa10 | 79 | u8 max_channels; |
f4ff4155 SK |
80 | bool adc_valid[MAX_POWR1220_ADC_VALUES]; |
81 | /* the next value is in jiffies */ | |
82 | unsigned long adc_last_updated[MAX_POWR1220_ADC_VALUES]; | |
83 | ||
84 | /* values */ | |
85 | int adc_maxes[MAX_POWR1220_ADC_VALUES]; | |
86 | int adc_values[MAX_POWR1220_ADC_VALUES]; | |
87 | }; | |
88 | ||
89 | static const char * const input_names[] = { | |
90 | [VMON1] = "vmon1", | |
91 | [VMON2] = "vmon2", | |
92 | [VMON3] = "vmon3", | |
93 | [VMON4] = "vmon4", | |
94 | [VMON5] = "vmon5", | |
95 | [VMON6] = "vmon6", | |
96 | [VMON7] = "vmon7", | |
97 | [VMON8] = "vmon8", | |
98 | [VMON9] = "vmon9", | |
99 | [VMON10] = "vmon10", | |
100 | [VMON11] = "vmon11", | |
101 | [VMON12] = "vmon12", | |
102 | [VCCA] = "vcca", | |
103 | [VCCINP] = "vccinp", | |
104 | }; | |
105 | ||
106 | /* Reads the specified ADC channel */ | |
107 | static int powr1220_read_adc(struct device *dev, int ch_num) | |
108 | { | |
109 | struct powr1220_data *data = dev_get_drvdata(dev); | |
110 | int reading; | |
111 | int result; | |
112 | int adc_range = 0; | |
113 | ||
114 | mutex_lock(&data->update_lock); | |
115 | ||
116 | if (time_after(jiffies, data->adc_last_updated[ch_num] + HZ) || | |
15b1c188 | 117 | !data->adc_valid[ch_num]) { |
f4ff4155 SK |
118 | /* |
119 | * figure out if we need to use the attenuator for | |
120 | * high inputs or inputs that we don't yet have a measurement | |
121 | * for. We dynamically set the attenuator depending on the | |
122 | * max reading. | |
123 | */ | |
124 | if (data->adc_maxes[ch_num] > ADC_MAX_LOW_MEASUREMENT_MV || | |
15b1c188 | 125 | data->adc_maxes[ch_num] == 0) |
f4ff4155 SK |
126 | adc_range = 1 << 4; |
127 | ||
128 | /* set the attenuator and mux */ | |
129 | result = i2c_smbus_write_byte_data(data->client, ADC_MUX, | |
15b1c188 | 130 | adc_range | ch_num); |
f4ff4155 SK |
131 | if (result) |
132 | goto exit; | |
133 | ||
134 | /* | |
135 | * wait at least Tconvert time (200 us) for the | |
136 | * conversion to complete | |
137 | */ | |
138 | udelay(200); | |
139 | ||
140 | /* get the ADC reading */ | |
141 | result = i2c_smbus_read_byte_data(data->client, ADC_VALUE_LOW); | |
142 | if (result < 0) | |
143 | goto exit; | |
144 | ||
145 | reading = result >> 4; | |
146 | ||
147 | /* get the upper half of the reading */ | |
148 | result = i2c_smbus_read_byte_data(data->client, ADC_VALUE_HIGH); | |
149 | if (result < 0) | |
150 | goto exit; | |
151 | ||
152 | reading |= result << 4; | |
153 | ||
154 | /* now convert the reading to a voltage */ | |
155 | reading *= ADC_STEP_MV; | |
156 | data->adc_values[ch_num] = reading; | |
157 | data->adc_valid[ch_num] = true; | |
158 | data->adc_last_updated[ch_num] = jiffies; | |
159 | result = reading; | |
160 | ||
161 | if (reading > data->adc_maxes[ch_num]) | |
162 | data->adc_maxes[ch_num] = reading; | |
163 | } else { | |
164 | result = data->adc_values[ch_num]; | |
165 | } | |
166 | ||
167 | exit: | |
168 | mutex_unlock(&data->update_lock); | |
169 | ||
170 | return result; | |
171 | } | |
172 | ||
915d4664 MS |
173 | static umode_t |
174 | powr1220_is_visible(const void *data, enum hwmon_sensor_types type, u32 | |
175 | attr, int channel) | |
f4ff4155 | 176 | { |
9f93aa10 MS |
177 | struct powr1220_data *chip_data = (struct powr1220_data *)data; |
178 | ||
179 | if (channel >= chip_data->max_channels) | |
180 | return 0; | |
181 | ||
915d4664 MS |
182 | switch (type) { |
183 | case hwmon_in: | |
184 | switch (attr) { | |
185 | case hwmon_in_input: | |
186 | case hwmon_in_highest: | |
187 | case hwmon_in_label: | |
188 | return 0444; | |
189 | default: | |
190 | break; | |
191 | } | |
192 | break; | |
193 | default: | |
194 | break; | |
195 | } | |
f4ff4155 | 196 | |
915d4664 | 197 | return 0; |
f4ff4155 SK |
198 | } |
199 | ||
915d4664 MS |
200 | static int |
201 | powr1220_read_string(struct device *dev, enum hwmon_sensor_types type, u32 attr, | |
202 | int channel, const char **str) | |
f4ff4155 | 203 | { |
915d4664 MS |
204 | switch (type) { |
205 | case hwmon_in: | |
206 | switch (attr) { | |
207 | case hwmon_in_label: | |
208 | *str = input_names[channel]; | |
209 | return 0; | |
210 | default: | |
211 | return -EOPNOTSUPP; | |
212 | } | |
213 | break; | |
214 | default: | |
215 | return -EOPNOTSUPP; | |
216 | } | |
f4ff4155 | 217 | |
915d4664 | 218 | return -EOPNOTSUPP; |
f4ff4155 SK |
219 | } |
220 | ||
915d4664 MS |
221 | static int |
222 | powr1220_read(struct device *dev, enum hwmon_sensor_types type, u32 | |
223 | attr, int channel, long *val) | |
f4ff4155 | 224 | { |
915d4664 MS |
225 | struct powr1220_data *data = dev_get_drvdata(dev); |
226 | int ret; | |
227 | ||
228 | switch (type) { | |
229 | case hwmon_in: | |
230 | switch (attr) { | |
231 | case hwmon_in_input: | |
232 | ret = powr1220_read_adc(dev, channel); | |
233 | if (ret < 0) | |
234 | return ret; | |
235 | *val = ret; | |
236 | break; | |
237 | case hwmon_in_highest: | |
238 | *val = data->adc_maxes[channel]; | |
239 | break; | |
240 | default: | |
241 | return -EOPNOTSUPP; | |
242 | } | |
243 | break; | |
244 | default: | |
245 | return -EOPNOTSUPP; | |
246 | } | |
f4ff4155 | 247 | |
915d4664 | 248 | return 0; |
f4ff4155 SK |
249 | } |
250 | ||
42d273bc | 251 | static const struct hwmon_channel_info * const powr1220_info[] = { |
915d4664 MS |
252 | HWMON_CHANNEL_INFO(in, |
253 | HWMON_I_INPUT | HWMON_I_HIGHEST | HWMON_I_LABEL, | |
254 | HWMON_I_INPUT | HWMON_I_HIGHEST | HWMON_I_LABEL, | |
255 | HWMON_I_INPUT | HWMON_I_HIGHEST | HWMON_I_LABEL, | |
256 | HWMON_I_INPUT | HWMON_I_HIGHEST | HWMON_I_LABEL, | |
257 | HWMON_I_INPUT | HWMON_I_HIGHEST | HWMON_I_LABEL, | |
258 | HWMON_I_INPUT | HWMON_I_HIGHEST | HWMON_I_LABEL, | |
259 | HWMON_I_INPUT | HWMON_I_HIGHEST | HWMON_I_LABEL, | |
260 | HWMON_I_INPUT | HWMON_I_HIGHEST | HWMON_I_LABEL, | |
261 | HWMON_I_INPUT | HWMON_I_HIGHEST | HWMON_I_LABEL, | |
262 | HWMON_I_INPUT | HWMON_I_HIGHEST | HWMON_I_LABEL, | |
263 | HWMON_I_INPUT | HWMON_I_HIGHEST | HWMON_I_LABEL, | |
264 | HWMON_I_INPUT | HWMON_I_HIGHEST | HWMON_I_LABEL, | |
265 | HWMON_I_INPUT | HWMON_I_HIGHEST | HWMON_I_LABEL, | |
266 | HWMON_I_INPUT | HWMON_I_HIGHEST | HWMON_I_LABEL), | |
f4ff4155 SK |
267 | |
268 | NULL | |
269 | }; | |
270 | ||
915d4664 MS |
271 | static const struct hwmon_ops powr1220_hwmon_ops = { |
272 | .read = powr1220_read, | |
273 | .read_string = powr1220_read_string, | |
274 | .is_visible = powr1220_is_visible, | |
275 | }; | |
276 | ||
277 | static const struct hwmon_chip_info powr1220_chip_info = { | |
278 | .ops = &powr1220_hwmon_ops, | |
279 | .info = powr1220_info, | |
280 | }; | |
f4ff4155 | 281 | |
9f93aa10 MS |
282 | static const struct i2c_device_id powr1220_ids[]; |
283 | ||
67487038 | 284 | static int powr1220_probe(struct i2c_client *client) |
f4ff4155 SK |
285 | { |
286 | struct powr1220_data *data; | |
287 | struct device *hwmon_dev; | |
288 | ||
289 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) | |
290 | return -ENODEV; | |
291 | ||
292 | data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); | |
293 | if (!data) | |
294 | return -ENOMEM; | |
295 | ||
9f93aa10 MS |
296 | switch (i2c_match_id(powr1220_ids, client)->driver_data) { |
297 | case powr1014: | |
298 | data->max_channels = 10; | |
299 | break; | |
300 | default: | |
301 | data->max_channels = 12; | |
302 | break; | |
303 | } | |
304 | ||
f4ff4155 SK |
305 | mutex_init(&data->update_lock); |
306 | data->client = client; | |
307 | ||
915d4664 MS |
308 | hwmon_dev = devm_hwmon_device_register_with_info(&client->dev, |
309 | client->name, | |
310 | data, | |
311 | &powr1220_chip_info, | |
312 | NULL); | |
f4ff4155 SK |
313 | |
314 | return PTR_ERR_OR_ZERO(hwmon_dev); | |
315 | } | |
316 | ||
317 | static const struct i2c_device_id powr1220_ids[] = { | |
9f93aa10 MS |
318 | { "powr1014", powr1014, }, |
319 | { "powr1220", powr1220, }, | |
f4ff4155 SK |
320 | { } |
321 | }; | |
322 | ||
323 | MODULE_DEVICE_TABLE(i2c, powr1220_ids); | |
324 | ||
325 | static struct i2c_driver powr1220_driver = { | |
326 | .class = I2C_CLASS_HWMON, | |
327 | .driver = { | |
328 | .name = "powr1220", | |
329 | }, | |
1975d167 | 330 | .probe = powr1220_probe, |
f4ff4155 SK |
331 | .id_table = powr1220_ids, |
332 | }; | |
333 | ||
334 | module_i2c_driver(powr1220_driver); | |
335 | ||
336 | MODULE_AUTHOR("Scott Kanowitz"); | |
337 | MODULE_DESCRIPTION("POWR1220 driver"); | |
338 | MODULE_LICENSE("GPL"); |