]>
Commit | Line | Data |
---|---|---|
6e34b187 IS |
1 | /* |
2 | * Driver for Linear Technology LTC4245 I2C Multiple Supply Hot Swap Controller | |
3 | * | |
4 | * Copyright (C) 2008 Ira W. Snyder <[email protected]> | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation; version 2 of the License. | |
9 | * | |
10 | * This driver is based on the ds1621 and ina209 drivers. | |
11 | * | |
12 | * Datasheet: | |
13 | * http://www.linear.com/pc/downloadDocument.do?navId=H0,C1,C1003,C1006,C1140,P19392,D13517 | |
14 | */ | |
15 | ||
16 | #include <linux/kernel.h> | |
17 | #include <linux/module.h> | |
18 | #include <linux/init.h> | |
a584287c | 19 | #include <linux/bitops.h> |
6e34b187 IS |
20 | #include <linux/err.h> |
21 | #include <linux/slab.h> | |
22 | #include <linux/i2c.h> | |
23 | #include <linux/hwmon.h> | |
24 | #include <linux/hwmon-sysfs.h> | |
dcd8f392 | 25 | #include <linux/jiffies.h> |
8116e8dd | 26 | #include <linux/platform_data/ltc4245.h> |
6e34b187 | 27 | |
6e34b187 IS |
28 | /* Here are names of the chip's registers (a.k.a. commands) */ |
29 | enum ltc4245_cmd { | |
30 | LTC4245_STATUS = 0x00, /* readonly */ | |
31 | LTC4245_ALERT = 0x01, | |
32 | LTC4245_CONTROL = 0x02, | |
33 | LTC4245_ON = 0x03, | |
34 | LTC4245_FAULT1 = 0x04, | |
35 | LTC4245_FAULT2 = 0x05, | |
36 | LTC4245_GPIO = 0x06, | |
37 | LTC4245_ADCADR = 0x07, | |
38 | ||
39 | LTC4245_12VIN = 0x10, | |
40 | LTC4245_12VSENSE = 0x11, | |
41 | LTC4245_12VOUT = 0x12, | |
42 | LTC4245_5VIN = 0x13, | |
43 | LTC4245_5VSENSE = 0x14, | |
44 | LTC4245_5VOUT = 0x15, | |
45 | LTC4245_3VIN = 0x16, | |
46 | LTC4245_3VSENSE = 0x17, | |
47 | LTC4245_3VOUT = 0x18, | |
48 | LTC4245_VEEIN = 0x19, | |
49 | LTC4245_VEESENSE = 0x1a, | |
50 | LTC4245_VEEOUT = 0x1b, | |
df16dd53 | 51 | LTC4245_GPIOADC = 0x1c, |
6e34b187 IS |
52 | }; |
53 | ||
54 | struct ltc4245_data { | |
9a85d53c GR |
55 | struct i2c_client *client; |
56 | ||
6e34b187 IS |
57 | struct mutex update_lock; |
58 | bool valid; | |
59 | unsigned long last_updated; /* in jiffies */ | |
60 | ||
61 | /* Control registers */ | |
62 | u8 cregs[0x08]; | |
63 | ||
64 | /* Voltage registers */ | |
df16dd53 | 65 | u8 vregs[0x0d]; |
5950ec8d IS |
66 | |
67 | /* GPIO ADC registers */ | |
68 | bool use_extra_gpios; | |
69 | int gpios[3]; | |
6e34b187 IS |
70 | }; |
71 | ||
5950ec8d IS |
72 | /* |
73 | * Update the readings from the GPIO pins. If the driver has been configured to | |
74 | * sample all GPIO's as analog voltages, a round-robin sampling method is used. | |
75 | * Otherwise, only the configured GPIO pin is sampled. | |
76 | * | |
77 | * LOCKING: must hold data->update_lock | |
78 | */ | |
79 | static void ltc4245_update_gpios(struct device *dev) | |
80 | { | |
9a85d53c GR |
81 | struct ltc4245_data *data = dev_get_drvdata(dev); |
82 | struct i2c_client *client = data->client; | |
5950ec8d IS |
83 | u8 gpio_curr, gpio_next, gpio_reg; |
84 | int i; | |
85 | ||
86 | /* no extra gpio support, we're basically done */ | |
87 | if (!data->use_extra_gpios) { | |
88 | data->gpios[0] = data->vregs[LTC4245_GPIOADC - 0x10]; | |
89 | return; | |
90 | } | |
91 | ||
92 | /* | |
93 | * If the last reading was too long ago, then we mark all old GPIO | |
94 | * readings as stale by setting them to -EAGAIN | |
95 | */ | |
96 | if (time_after(jiffies, data->last_updated + 5 * HZ)) { | |
5950ec8d IS |
97 | for (i = 0; i < ARRAY_SIZE(data->gpios); i++) |
98 | data->gpios[i] = -EAGAIN; | |
99 | } | |
100 | ||
101 | /* | |
102 | * Get the current GPIO pin | |
103 | * | |
104 | * The datasheet calls these GPIO[1-3], but we'll calculate the zero | |
105 | * based array index instead, and call them GPIO[0-2]. This is much | |
106 | * easier to think about. | |
107 | */ | |
108 | gpio_curr = (data->cregs[LTC4245_GPIO] & 0xc0) >> 6; | |
109 | if (gpio_curr > 0) | |
110 | gpio_curr -= 1; | |
111 | ||
112 | /* Read the GPIO voltage from the GPIOADC register */ | |
113 | data->gpios[gpio_curr] = data->vregs[LTC4245_GPIOADC - 0x10]; | |
114 | ||
115 | /* Find the next GPIO pin to read */ | |
116 | gpio_next = (gpio_curr + 1) % ARRAY_SIZE(data->gpios); | |
117 | ||
118 | /* | |
119 | * Calculate the correct setting for the GPIO register so it will | |
120 | * sample the next GPIO pin | |
121 | */ | |
122 | gpio_reg = (data->cregs[LTC4245_GPIO] & 0x3f) | ((gpio_next + 1) << 6); | |
123 | ||
124 | /* Update the GPIO register */ | |
125 | i2c_smbus_write_byte_data(client, LTC4245_GPIO, gpio_reg); | |
126 | ||
127 | /* Update saved data */ | |
128 | data->cregs[LTC4245_GPIO] = gpio_reg; | |
129 | } | |
130 | ||
6e34b187 IS |
131 | static struct ltc4245_data *ltc4245_update_device(struct device *dev) |
132 | { | |
9a85d53c GR |
133 | struct ltc4245_data *data = dev_get_drvdata(dev); |
134 | struct i2c_client *client = data->client; | |
6e34b187 IS |
135 | s32 val; |
136 | int i; | |
137 | ||
138 | mutex_lock(&data->update_lock); | |
139 | ||
140 | if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { | |
141 | ||
6e34b187 IS |
142 | /* Read control registers -- 0x00 to 0x07 */ |
143 | for (i = 0; i < ARRAY_SIZE(data->cregs); i++) { | |
144 | val = i2c_smbus_read_byte_data(client, i); | |
145 | if (unlikely(val < 0)) | |
146 | data->cregs[i] = 0; | |
147 | else | |
148 | data->cregs[i] = val; | |
149 | } | |
150 | ||
df16dd53 | 151 | /* Read voltage registers -- 0x10 to 0x1c */ |
6e34b187 IS |
152 | for (i = 0; i < ARRAY_SIZE(data->vregs); i++) { |
153 | val = i2c_smbus_read_byte_data(client, i+0x10); | |
154 | if (unlikely(val < 0)) | |
155 | data->vregs[i] = 0; | |
156 | else | |
157 | data->vregs[i] = val; | |
158 | } | |
159 | ||
5950ec8d IS |
160 | /* Update GPIO readings */ |
161 | ltc4245_update_gpios(dev); | |
162 | ||
6e34b187 | 163 | data->last_updated = jiffies; |
a584287c | 164 | data->valid = true; |
6e34b187 IS |
165 | } |
166 | ||
167 | mutex_unlock(&data->update_lock); | |
168 | ||
169 | return data; | |
170 | } | |
171 | ||
172 | /* Return the voltage from the given register in millivolts */ | |
173 | static int ltc4245_get_voltage(struct device *dev, u8 reg) | |
174 | { | |
175 | struct ltc4245_data *data = ltc4245_update_device(dev); | |
176 | const u8 regval = data->vregs[reg - 0x10]; | |
177 | u32 voltage = 0; | |
178 | ||
179 | switch (reg) { | |
180 | case LTC4245_12VIN: | |
181 | case LTC4245_12VOUT: | |
182 | voltage = regval * 55; | |
183 | break; | |
184 | case LTC4245_5VIN: | |
185 | case LTC4245_5VOUT: | |
186 | voltage = regval * 22; | |
187 | break; | |
188 | case LTC4245_3VIN: | |
189 | case LTC4245_3VOUT: | |
190 | voltage = regval * 15; | |
191 | break; | |
192 | case LTC4245_VEEIN: | |
193 | case LTC4245_VEEOUT: | |
194 | voltage = regval * -55; | |
195 | break; | |
df16dd53 | 196 | case LTC4245_GPIOADC: |
6e34b187 IS |
197 | voltage = regval * 10; |
198 | break; | |
199 | default: | |
200 | /* If we get here, the developer messed up */ | |
201 | WARN_ON_ONCE(1); | |
202 | break; | |
203 | } | |
204 | ||
205 | return voltage; | |
206 | } | |
207 | ||
208 | /* Return the current in the given sense register in milliAmperes */ | |
209 | static unsigned int ltc4245_get_current(struct device *dev, u8 reg) | |
210 | { | |
211 | struct ltc4245_data *data = ltc4245_update_device(dev); | |
212 | const u8 regval = data->vregs[reg - 0x10]; | |
213 | unsigned int voltage; | |
214 | unsigned int curr; | |
215 | ||
430b4fcd GR |
216 | /* |
217 | * The strange looking conversions that follow are fixed-point | |
6e34b187 IS |
218 | * math, since we cannot do floating point in the kernel. |
219 | * | |
220 | * Step 1: convert sense register to microVolts | |
221 | * Step 2: convert voltage to milliAmperes | |
222 | * | |
223 | * If you play around with the V=IR equation, you come up with | |
224 | * the following: X uV / Y mOhm == Z mA | |
225 | * | |
226 | * With the resistors that are fractions of a milliOhm, we multiply | |
227 | * the voltage and resistance by 10, to shift the decimal point. | |
228 | * Now we can use the normal division operator again. | |
229 | */ | |
230 | ||
231 | switch (reg) { | |
232 | case LTC4245_12VSENSE: | |
233 | voltage = regval * 250; /* voltage in uV */ | |
234 | curr = voltage / 50; /* sense resistor 50 mOhm */ | |
235 | break; | |
236 | case LTC4245_5VSENSE: | |
237 | voltage = regval * 125; /* voltage in uV */ | |
238 | curr = (voltage * 10) / 35; /* sense resistor 3.5 mOhm */ | |
239 | break; | |
240 | case LTC4245_3VSENSE: | |
241 | voltage = regval * 125; /* voltage in uV */ | |
242 | curr = (voltage * 10) / 25; /* sense resistor 2.5 mOhm */ | |
243 | break; | |
244 | case LTC4245_VEESENSE: | |
245 | voltage = regval * 250; /* voltage in uV */ | |
246 | curr = voltage / 100; /* sense resistor 100 mOhm */ | |
247 | break; | |
248 | default: | |
249 | /* If we get here, the developer messed up */ | |
250 | WARN_ON_ONCE(1); | |
251 | curr = 0; | |
252 | break; | |
253 | } | |
254 | ||
255 | return curr; | |
256 | } | |
257 | ||
a584287c | 258 | /* Map from voltage channel index to voltage register */ |
6e34b187 | 259 | |
a584287c GR |
260 | static const s8 ltc4245_in_regs[] = { |
261 | LTC4245_12VIN, LTC4245_5VIN, LTC4245_3VIN, LTC4245_VEEIN, | |
262 | LTC4245_12VOUT, LTC4245_5VOUT, LTC4245_3VOUT, LTC4245_VEEOUT, | |
263 | }; | |
264 | ||
265 | /* Map from current channel index to current register */ | |
6e34b187 | 266 | |
a584287c GR |
267 | static const s8 ltc4245_curr_regs[] = { |
268 | LTC4245_12VSENSE, LTC4245_5VSENSE, LTC4245_3VSENSE, LTC4245_VEESENSE, | |
269 | }; | |
270 | ||
271 | static int ltc4245_read_curr(struct device *dev, u32 attr, int channel, | |
272 | long *val) | |
6e34b187 | 273 | { |
a584287c | 274 | struct ltc4245_data *data = ltc4245_update_device(dev); |
6e34b187 | 275 | |
a584287c GR |
276 | switch (attr) { |
277 | case hwmon_curr_input: | |
278 | *val = ltc4245_get_current(dev, ltc4245_curr_regs[channel]); | |
279 | return 0; | |
280 | case hwmon_curr_max_alarm: | |
281 | *val = !!(data->cregs[LTC4245_FAULT1] & BIT(channel + 4)); | |
282 | return 0; | |
283 | default: | |
284 | return -EOPNOTSUPP; | |
285 | } | |
6e34b187 IS |
286 | } |
287 | ||
a584287c | 288 | static int ltc4245_read_in(struct device *dev, u32 attr, int channel, long *val) |
6e34b187 | 289 | { |
a584287c | 290 | struct ltc4245_data *data = ltc4245_update_device(dev); |
6e34b187 | 291 | |
a584287c GR |
292 | switch (attr) { |
293 | case hwmon_in_input: | |
294 | if (channel < 8) { | |
295 | *val = ltc4245_get_voltage(dev, | |
296 | ltc4245_in_regs[channel]); | |
297 | } else { | |
298 | int regval = data->gpios[channel - 8]; | |
299 | ||
300 | if (regval < 0) | |
301 | return regval; | |
302 | *val = regval * 10; | |
303 | } | |
304 | return 0; | |
305 | case hwmon_in_min_alarm: | |
306 | if (channel < 4) | |
307 | *val = !!(data->cregs[LTC4245_FAULT1] & BIT(channel)); | |
308 | else | |
309 | *val = !!(data->cregs[LTC4245_FAULT2] & | |
310 | BIT(channel - 4)); | |
311 | return 0; | |
312 | default: | |
313 | return -EOPNOTSUPP; | |
314 | } | |
315 | } | |
6e34b187 | 316 | |
a584287c GR |
317 | static int ltc4245_read_power(struct device *dev, u32 attr, int channel, |
318 | long *val) | |
319 | { | |
320 | unsigned long curr; | |
321 | long voltage; | |
322 | ||
323 | switch (attr) { | |
324 | case hwmon_power_input: | |
325 | (void)ltc4245_update_device(dev); | |
326 | curr = ltc4245_get_current(dev, ltc4245_curr_regs[channel]); | |
327 | voltage = ltc4245_get_voltage(dev, ltc4245_in_regs[channel]); | |
328 | *val = abs(curr * voltage); | |
329 | return 0; | |
330 | default: | |
331 | return -EOPNOTSUPP; | |
332 | } | |
6e34b187 IS |
333 | } |
334 | ||
a584287c GR |
335 | static int ltc4245_read(struct device *dev, enum hwmon_sensor_types type, |
336 | u32 attr, int channel, long *val) | |
6e34b187 | 337 | { |
6e34b187 | 338 | |
a584287c GR |
339 | switch (type) { |
340 | case hwmon_curr: | |
341 | return ltc4245_read_curr(dev, attr, channel, val); | |
342 | case hwmon_power: | |
343 | return ltc4245_read_power(dev, attr, channel, val); | |
344 | case hwmon_in: | |
345 | return ltc4245_read_in(dev, attr, channel - 1, val); | |
346 | default: | |
347 | return -EOPNOTSUPP; | |
348 | } | |
6e34b187 IS |
349 | } |
350 | ||
a584287c GR |
351 | static umode_t ltc4245_is_visible(const void *_data, |
352 | enum hwmon_sensor_types type, | |
353 | u32 attr, int channel) | |
5950ec8d | 354 | { |
a584287c GR |
355 | const struct ltc4245_data *data = _data; |
356 | ||
357 | switch (type) { | |
358 | case hwmon_in: | |
359 | if (channel == 0) | |
360 | return 0; | |
361 | switch (attr) { | |
362 | case hwmon_in_input: | |
363 | if (channel > 9 && !data->use_extra_gpios) | |
364 | return 0; | |
365 | return S_IRUGO; | |
366 | case hwmon_in_min_alarm: | |
367 | if (channel > 8) | |
368 | return 0; | |
369 | return S_IRUGO; | |
370 | default: | |
371 | return 0; | |
372 | } | |
373 | case hwmon_curr: | |
374 | switch (attr) { | |
375 | case hwmon_curr_input: | |
376 | case hwmon_curr_max_alarm: | |
377 | return S_IRUGO; | |
378 | default: | |
379 | return 0; | |
380 | } | |
381 | case hwmon_power: | |
382 | switch (attr) { | |
383 | case hwmon_power_input: | |
384 | return S_IRUGO; | |
385 | default: | |
386 | return 0; | |
387 | } | |
388 | default: | |
389 | return 0; | |
390 | } | |
391 | } | |
5950ec8d | 392 | |
a584287c GR |
393 | static const u32 ltc4245_in_config[] = { |
394 | HWMON_I_INPUT, /* dummy, skipped in is_visible */ | |
395 | HWMON_I_INPUT | HWMON_I_MIN_ALARM, | |
396 | HWMON_I_INPUT | HWMON_I_MIN_ALARM, | |
397 | HWMON_I_INPUT | HWMON_I_MIN_ALARM, | |
398 | HWMON_I_INPUT | HWMON_I_MIN_ALARM, | |
399 | HWMON_I_INPUT | HWMON_I_MIN_ALARM, | |
400 | HWMON_I_INPUT | HWMON_I_MIN_ALARM, | |
401 | HWMON_I_INPUT | HWMON_I_MIN_ALARM, | |
402 | HWMON_I_INPUT | HWMON_I_MIN_ALARM, | |
403 | HWMON_I_INPUT, | |
404 | HWMON_I_INPUT, | |
405 | HWMON_I_INPUT, | |
406 | 0 | |
407 | }; | |
5950ec8d | 408 | |
a584287c GR |
409 | static const struct hwmon_channel_info ltc4245_in = { |
410 | .type = hwmon_in, | |
411 | .config = ltc4245_in_config, | |
412 | }; | |
5950ec8d | 413 | |
a584287c GR |
414 | static const u32 ltc4245_curr_config[] = { |
415 | HWMON_C_INPUT | HWMON_C_MAX_ALARM, | |
416 | HWMON_C_INPUT | HWMON_C_MAX_ALARM, | |
417 | HWMON_C_INPUT | HWMON_C_MAX_ALARM, | |
418 | HWMON_C_INPUT | HWMON_C_MAX_ALARM, | |
419 | 0 | |
420 | }; | |
6e34b187 | 421 | |
a584287c GR |
422 | static const struct hwmon_channel_info ltc4245_curr = { |
423 | .type = hwmon_curr, | |
424 | .config = ltc4245_curr_config, | |
6e34b187 IS |
425 | }; |
426 | ||
a584287c GR |
427 | static const u32 ltc4245_power_config[] = { |
428 | HWMON_P_INPUT, | |
429 | HWMON_P_INPUT, | |
430 | HWMON_P_INPUT, | |
431 | HWMON_P_INPUT, | |
432 | 0 | |
5950ec8d IS |
433 | }; |
434 | ||
a584287c GR |
435 | static const struct hwmon_channel_info ltc4245_power = { |
436 | .type = hwmon_power, | |
437 | .config = ltc4245_power_config, | |
5950ec8d IS |
438 | }; |
439 | ||
a584287c GR |
440 | static const struct hwmon_channel_info *ltc4245_info[] = { |
441 | <c4245_in, | |
442 | <c4245_curr, | |
443 | <c4245_power, | |
444 | NULL | |
6e34b187 IS |
445 | }; |
446 | ||
a584287c GR |
447 | static const struct hwmon_ops ltc4245_hwmon_ops = { |
448 | .is_visible = ltc4245_is_visible, | |
449 | .read = ltc4245_read, | |
450 | }; | |
5950ec8d | 451 | |
a584287c GR |
452 | static const struct hwmon_chip_info ltc4245_chip_info = { |
453 | .ops = <c4245_hwmon_ops, | |
454 | .info = ltc4245_info, | |
455 | }; | |
5950ec8d IS |
456 | |
457 | static bool ltc4245_use_extra_gpios(struct i2c_client *client) | |
458 | { | |
459 | struct ltc4245_platform_data *pdata = dev_get_platdata(&client->dev); | |
5950ec8d | 460 | struct device_node *np = client->dev.of_node; |
5950ec8d IS |
461 | |
462 | /* prefer platform data */ | |
463 | if (pdata) | |
464 | return pdata->use_extra_gpios; | |
465 | ||
5950ec8d IS |
466 | /* fallback on OF */ |
467 | if (of_find_property(np, "ltc4245,use-extra-gpios", NULL)) | |
468 | return true; | |
5950ec8d IS |
469 | |
470 | return false; | |
471 | } | |
472 | ||
6e34b187 IS |
473 | static int ltc4245_probe(struct i2c_client *client, |
474 | const struct i2c_device_id *id) | |
475 | { | |
2d2a7cff | 476 | struct i2c_adapter *adapter = client->adapter; |
6e34b187 | 477 | struct ltc4245_data *data; |
9a85d53c | 478 | struct device *hwmon_dev; |
6e34b187 | 479 | |
2d2a7cff JD |
480 | if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) |
481 | return -ENODEV; | |
482 | ||
effb8ab1 GR |
483 | data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); |
484 | if (!data) | |
485 | return -ENOMEM; | |
6e34b187 | 486 | |
9a85d53c | 487 | data->client = client; |
6e34b187 | 488 | mutex_init(&data->update_lock); |
5950ec8d | 489 | data->use_extra_gpios = ltc4245_use_extra_gpios(client); |
6e34b187 IS |
490 | |
491 | /* Initialize the LTC4245 chip */ | |
58f055e5 IS |
492 | i2c_smbus_write_byte_data(client, LTC4245_FAULT1, 0x00); |
493 | i2c_smbus_write_byte_data(client, LTC4245_FAULT2, 0x00); | |
6e34b187 | 494 | |
a584287c GR |
495 | hwmon_dev = devm_hwmon_device_register_with_info(&client->dev, |
496 | client->name, data, | |
497 | <c4245_chip_info, | |
498 | NULL); | |
94c42cf9 | 499 | return PTR_ERR_OR_ZERO(hwmon_dev); |
6e34b187 IS |
500 | } |
501 | ||
6e34b187 | 502 | static const struct i2c_device_id ltc4245_id[] = { |
2d2a7cff | 503 | { "ltc4245", 0 }, |
6e34b187 IS |
504 | { } |
505 | }; | |
506 | MODULE_DEVICE_TABLE(i2c, ltc4245_id); | |
507 | ||
508 | /* This is the driver that will be inserted */ | |
509 | static struct i2c_driver ltc4245_driver = { | |
6e34b187 IS |
510 | .driver = { |
511 | .name = "ltc4245", | |
512 | }, | |
513 | .probe = ltc4245_probe, | |
6e34b187 | 514 | .id_table = ltc4245_id, |
6e34b187 IS |
515 | }; |
516 | ||
f0967eea | 517 | module_i2c_driver(ltc4245_driver); |
6e34b187 IS |
518 | |
519 | MODULE_AUTHOR("Ira W. Snyder <[email protected]>"); | |
520 | MODULE_DESCRIPTION("LTC4245 driver"); | |
521 | MODULE_LICENSE("GPL"); |