diff options
Diffstat (limited to 'drivers/power/ds2781_battery.c')
-rw-r--r-- | drivers/power/ds2781_battery.c | 874 |
1 files changed, 874 insertions, 0 deletions
diff --git a/drivers/power/ds2781_battery.c b/drivers/power/ds2781_battery.c new file mode 100644 index 000000000000..ca0d653d0a7a --- /dev/null +++ b/drivers/power/ds2781_battery.c | |||
@@ -0,0 +1,874 @@ | |||
1 | /* | ||
2 | * 1-wire client/driver for the Maxim/Dallas DS2781 Stand-Alone Fuel Gauge IC | ||
3 | * | ||
4 | * Author: Renata Sayakhova <renata@oktetlabs.ru> | ||
5 | * | ||
6 | * Based on ds2780_battery drivers | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | * | ||
12 | */ | ||
13 | |||
14 | #include <linux/module.h> | ||
15 | #include <linux/slab.h> | ||
16 | #include <linux/param.h> | ||
17 | #include <linux/pm.h> | ||
18 | #include <linux/platform_device.h> | ||
19 | #include <linux/power_supply.h> | ||
20 | #include <linux/idr.h> | ||
21 | |||
22 | #include "../w1/w1.h" | ||
23 | #include "../w1/slaves/w1_ds2781.h" | ||
24 | |||
25 | /* Current unit measurement in uA for a 1 milli-ohm sense resistor */ | ||
26 | #define DS2781_CURRENT_UNITS 1563 | ||
27 | /* Charge unit measurement in uAh for a 1 milli-ohm sense resistor */ | ||
28 | #define DS2781_CHARGE_UNITS 6250 | ||
29 | /* Number of bytes in user EEPROM space */ | ||
30 | #define DS2781_USER_EEPROM_SIZE (DS2781_EEPROM_BLOCK0_END - \ | ||
31 | DS2781_EEPROM_BLOCK0_START + 1) | ||
32 | /* Number of bytes in parameter EEPROM space */ | ||
33 | #define DS2781_PARAM_EEPROM_SIZE (DS2781_EEPROM_BLOCK1_END - \ | ||
34 | DS2781_EEPROM_BLOCK1_START + 1) | ||
35 | |||
36 | struct ds2781_device_info { | ||
37 | struct device *dev; | ||
38 | struct power_supply bat; | ||
39 | struct device *w1_dev; | ||
40 | struct task_struct *mutex_holder; | ||
41 | }; | ||
42 | |||
43 | enum current_types { | ||
44 | CURRENT_NOW, | ||
45 | CURRENT_AVG, | ||
46 | }; | ||
47 | |||
48 | static const char model[] = "DS2781"; | ||
49 | static const char manufacturer[] = "Maxim/Dallas"; | ||
50 | |||
51 | static inline struct ds2781_device_info * | ||
52 | to_ds2781_device_info(struct power_supply *psy) | ||
53 | { | ||
54 | return container_of(psy, struct ds2781_device_info, bat); | ||
55 | } | ||
56 | |||
57 | static inline struct power_supply *to_power_supply(struct device *dev) | ||
58 | { | ||
59 | return dev_get_drvdata(dev); | ||
60 | } | ||
61 | |||
62 | static inline int ds2781_battery_io(struct ds2781_device_info *dev_info, | ||
63 | char *buf, int addr, size_t count, int io) | ||
64 | { | ||
65 | if (dev_info->mutex_holder == current) | ||
66 | return w1_ds2781_io_nolock(dev_info->w1_dev, buf, addr, | ||
67 | count, io); | ||
68 | else | ||
69 | return w1_ds2781_io(dev_info->w1_dev, buf, addr, count, io); | ||
70 | } | ||
71 | |||
72 | int w1_ds2781_read(struct ds2781_device_info *dev_info, char *buf, | ||
73 | int addr, size_t count) | ||
74 | { | ||
75 | return ds2781_battery_io(dev_info, buf, addr, count, 0); | ||
76 | } | ||
77 | |||
78 | static inline int ds2781_read8(struct ds2781_device_info *dev_info, u8 *val, | ||
79 | int addr) | ||
80 | { | ||
81 | return ds2781_battery_io(dev_info, val, addr, sizeof(u8), 0); | ||
82 | } | ||
83 | |||
84 | static int ds2781_read16(struct ds2781_device_info *dev_info, s16 *val, | ||
85 | int addr) | ||
86 | { | ||
87 | int ret; | ||
88 | u8 raw[2]; | ||
89 | |||
90 | ret = ds2781_battery_io(dev_info, raw, addr, sizeof(raw), 0); | ||
91 | if (ret < 0) | ||
92 | return ret; | ||
93 | |||
94 | *val = (raw[0] << 8) | raw[1]; | ||
95 | |||
96 | return 0; | ||
97 | } | ||
98 | |||
99 | static inline int ds2781_read_block(struct ds2781_device_info *dev_info, | ||
100 | u8 *val, int addr, size_t count) | ||
101 | { | ||
102 | return ds2781_battery_io(dev_info, val, addr, count, 0); | ||
103 | } | ||
104 | |||
105 | static inline int ds2781_write(struct ds2781_device_info *dev_info, u8 *val, | ||
106 | int addr, size_t count) | ||
107 | { | ||
108 | return ds2781_battery_io(dev_info, val, addr, count, 1); | ||
109 | } | ||
110 | |||
111 | static inline int ds2781_store_eeprom(struct device *dev, int addr) | ||
112 | { | ||
113 | return w1_ds2781_eeprom_cmd(dev, addr, W1_DS2781_COPY_DATA); | ||
114 | } | ||
115 | |||
116 | static inline int ds2781_recall_eeprom(struct device *dev, int addr) | ||
117 | { | ||
118 | return w1_ds2781_eeprom_cmd(dev, addr, W1_DS2781_RECALL_DATA); | ||
119 | } | ||
120 | |||
121 | static int ds2781_save_eeprom(struct ds2781_device_info *dev_info, int reg) | ||
122 | { | ||
123 | int ret; | ||
124 | |||
125 | ret = ds2781_store_eeprom(dev_info->w1_dev, reg); | ||
126 | if (ret < 0) | ||
127 | return ret; | ||
128 | |||
129 | ret = ds2781_recall_eeprom(dev_info->w1_dev, reg); | ||
130 | if (ret < 0) | ||
131 | return ret; | ||
132 | |||
133 | return 0; | ||
134 | } | ||
135 | |||
136 | /* Set sense resistor value in mhos */ | ||
137 | static int ds2781_set_sense_register(struct ds2781_device_info *dev_info, | ||
138 | u8 conductance) | ||
139 | { | ||
140 | int ret; | ||
141 | |||
142 | ret = ds2781_write(dev_info, &conductance, | ||
143 | DS2781_RSNSP, sizeof(u8)); | ||
144 | if (ret < 0) | ||
145 | return ret; | ||
146 | |||
147 | return ds2781_save_eeprom(dev_info, DS2781_RSNSP); | ||
148 | } | ||
149 | |||
150 | /* Get RSGAIN value from 0 to 1.999 in steps of 0.001 */ | ||
151 | static int ds2781_get_rsgain_register(struct ds2781_device_info *dev_info, | ||
152 | u16 *rsgain) | ||
153 | { | ||
154 | return ds2781_read16(dev_info, rsgain, DS2781_RSGAIN_MSB); | ||
155 | } | ||
156 | |||
157 | /* Set RSGAIN value from 0 to 1.999 in steps of 0.001 */ | ||
158 | static int ds2781_set_rsgain_register(struct ds2781_device_info *dev_info, | ||
159 | u16 rsgain) | ||
160 | { | ||
161 | int ret; | ||
162 | u8 raw[] = {rsgain >> 8, rsgain & 0xFF}; | ||
163 | |||
164 | ret = ds2781_write(dev_info, raw, | ||
165 | DS2781_RSGAIN_MSB, sizeof(raw)); | ||
166 | if (ret < 0) | ||
167 | return ret; | ||
168 | |||
169 | return ds2781_save_eeprom(dev_info, DS2781_RSGAIN_MSB); | ||
170 | } | ||
171 | |||
172 | static int ds2781_get_voltage(struct ds2781_device_info *dev_info, | ||
173 | int *voltage_uV) | ||
174 | { | ||
175 | int ret; | ||
176 | char val[2]; | ||
177 | int voltage_raw; | ||
178 | |||
179 | ret = w1_ds2781_read(dev_info, val, DS2781_VOLT_MSB, 2 * sizeof(u8)); | ||
180 | if (ret < 0) | ||
181 | return ret; | ||
182 | /* | ||
183 | * The voltage value is located in 10 bits across the voltage MSB | ||
184 | * and LSB registers in two's compliment form | ||
185 | * Sign bit of the voltage value is in bit 7 of the voltage MSB register | ||
186 | * Bits 9 - 3 of the voltage value are in bits 6 - 0 of the | ||
187 | * voltage MSB register | ||
188 | * Bits 2 - 0 of the voltage value are in bits 7 - 5 of the | ||
189 | * voltage LSB register | ||
190 | */ | ||
191 | voltage_raw = (val[0] << 3) | | ||
192 | (val[1] >> 5); | ||
193 | |||
194 | /* DS2781 reports voltage in units of 9.76mV, but the battery class | ||
195 | * reports in units of uV, so convert by multiplying by 9760. */ | ||
196 | *voltage_uV = voltage_raw * 9760; | ||
197 | |||
198 | return 0; | ||
199 | } | ||
200 | |||
201 | static int ds2781_get_temperature(struct ds2781_device_info *dev_info, | ||
202 | int *temp) | ||
203 | { | ||
204 | int ret; | ||
205 | char val[2]; | ||
206 | int temp_raw; | ||
207 | |||
208 | ret = w1_ds2781_read(dev_info, val, DS2781_TEMP_MSB, 2 * sizeof(u8)); | ||
209 | if (ret < 0) | ||
210 | return ret; | ||
211 | /* | ||
212 | * The temperature value is located in 10 bits across the temperature | ||
213 | * MSB and LSB registers in two's compliment form | ||
214 | * Sign bit of the temperature value is in bit 7 of the temperature | ||
215 | * MSB register | ||
216 | * Bits 9 - 3 of the temperature value are in bits 6 - 0 of the | ||
217 | * temperature MSB register | ||
218 | * Bits 2 - 0 of the temperature value are in bits 7 - 5 of the | ||
219 | * temperature LSB register | ||
220 | */ | ||
221 | temp_raw = ((val[0]) << 3) | | ||
222 | (val[1] >> 5); | ||
223 | *temp = temp_raw + (temp_raw / 4); | ||
224 | |||
225 | return 0; | ||
226 | } | ||
227 | |||
228 | static int ds2781_get_current(struct ds2781_device_info *dev_info, | ||
229 | enum current_types type, int *current_uA) | ||
230 | { | ||
231 | int ret, sense_res; | ||
232 | s16 current_raw; | ||
233 | u8 sense_res_raw, reg_msb; | ||
234 | |||
235 | /* | ||
236 | * The units of measurement for current are dependent on the value of | ||
237 | * the sense resistor. | ||
238 | */ | ||
239 | ret = ds2781_read8(dev_info, &sense_res_raw, DS2781_RSNSP); | ||
240 | if (ret < 0) | ||
241 | return ret; | ||
242 | |||
243 | if (sense_res_raw == 0) { | ||
244 | dev_err(dev_info->dev, "sense resistor value is 0\n"); | ||
245 | return -EINVAL; | ||
246 | } | ||
247 | sense_res = 1000 / sense_res_raw; | ||
248 | |||
249 | if (type == CURRENT_NOW) | ||
250 | reg_msb = DS2781_CURRENT_MSB; | ||
251 | else if (type == CURRENT_AVG) | ||
252 | reg_msb = DS2781_IAVG_MSB; | ||
253 | else | ||
254 | return -EINVAL; | ||
255 | |||
256 | /* | ||
257 | * The current value is located in 16 bits across the current MSB | ||
258 | * and LSB registers in two's compliment form | ||
259 | * Sign bit of the current value is in bit 7 of the current MSB register | ||
260 | * Bits 14 - 8 of the current value are in bits 6 - 0 of the current | ||
261 | * MSB register | ||
262 | * Bits 7 - 0 of the current value are in bits 7 - 0 of the current | ||
263 | * LSB register | ||
264 | */ | ||
265 | ret = ds2781_read16(dev_info, ¤t_raw, reg_msb); | ||
266 | if (ret < 0) | ||
267 | return ret; | ||
268 | |||
269 | *current_uA = current_raw * (DS2781_CURRENT_UNITS / sense_res); | ||
270 | return 0; | ||
271 | } | ||
272 | |||
273 | static int ds2781_get_accumulated_current(struct ds2781_device_info *dev_info, | ||
274 | int *accumulated_current) | ||
275 | { | ||
276 | int ret, sense_res; | ||
277 | s16 current_raw; | ||
278 | u8 sense_res_raw; | ||
279 | |||
280 | /* | ||
281 | * The units of measurement for accumulated current are dependent on | ||
282 | * the value of the sense resistor. | ||
283 | */ | ||
284 | ret = ds2781_read8(dev_info, &sense_res_raw, DS2781_RSNSP); | ||
285 | if (ret < 0) | ||
286 | return ret; | ||
287 | |||
288 | if (sense_res_raw == 0) { | ||
289 | dev_err(dev_info->dev, "sense resistor value is 0\n"); | ||
290 | return -EINVAL; | ||
291 | } | ||
292 | sense_res = 1000 / sense_res_raw; | ||
293 | |||
294 | /* | ||
295 | * The ACR value is located in 16 bits across the ACR MSB and | ||
296 | * LSB registers | ||
297 | * Bits 15 - 8 of the ACR value are in bits 7 - 0 of the ACR | ||
298 | * MSB register | ||
299 | * Bits 7 - 0 of the ACR value are in bits 7 - 0 of the ACR | ||
300 | * LSB register | ||
301 | */ | ||
302 | ret = ds2781_read16(dev_info, ¤t_raw, DS2781_ACR_MSB); | ||
303 | if (ret < 0) | ||
304 | return ret; | ||
305 | |||
306 | *accumulated_current = current_raw * (DS2781_CHARGE_UNITS / sense_res); | ||
307 | return 0; | ||
308 | } | ||
309 | |||
310 | static int ds2781_get_capacity(struct ds2781_device_info *dev_info, | ||
311 | int *capacity) | ||
312 | { | ||
313 | int ret; | ||
314 | u8 raw; | ||
315 | |||
316 | ret = ds2781_read8(dev_info, &raw, DS2781_RARC); | ||
317 | if (ret < 0) | ||
318 | return ret; | ||
319 | |||
320 | *capacity = raw; | ||
321 | return 0; | ||
322 | } | ||
323 | |||
324 | static int ds2781_get_status(struct ds2781_device_info *dev_info, int *status) | ||
325 | { | ||
326 | int ret, current_uA, capacity; | ||
327 | |||
328 | ret = ds2781_get_current(dev_info, CURRENT_NOW, ¤t_uA); | ||
329 | if (ret < 0) | ||
330 | return ret; | ||
331 | |||
332 | ret = ds2781_get_capacity(dev_info, &capacity); | ||
333 | if (ret < 0) | ||
334 | return ret; | ||
335 | |||
336 | if (power_supply_am_i_supplied(&dev_info->bat)) { | ||
337 | if (capacity == 100) | ||
338 | *status = POWER_SUPPLY_STATUS_FULL; | ||
339 | else if (current_uA > 50000) | ||
340 | *status = POWER_SUPPLY_STATUS_CHARGING; | ||
341 | else | ||
342 | *status = POWER_SUPPLY_STATUS_NOT_CHARGING; | ||
343 | } else { | ||
344 | *status = POWER_SUPPLY_STATUS_DISCHARGING; | ||
345 | } | ||
346 | return 0; | ||
347 | } | ||
348 | |||
349 | static int ds2781_get_charge_now(struct ds2781_device_info *dev_info, | ||
350 | int *charge_now) | ||
351 | { | ||
352 | int ret; | ||
353 | u16 charge_raw; | ||
354 | |||
355 | /* | ||
356 | * The RAAC value is located in 16 bits across the RAAC MSB and | ||
357 | * LSB registers | ||
358 | * Bits 15 - 8 of the RAAC value are in bits 7 - 0 of the RAAC | ||
359 | * MSB register | ||
360 | * Bits 7 - 0 of the RAAC value are in bits 7 - 0 of the RAAC | ||
361 | * LSB register | ||
362 | */ | ||
363 | ret = ds2781_read16(dev_info, &charge_raw, DS2781_RAAC_MSB); | ||
364 | if (ret < 0) | ||
365 | return ret; | ||
366 | |||
367 | *charge_now = charge_raw * 1600; | ||
368 | return 0; | ||
369 | } | ||
370 | |||
371 | static int ds2781_get_control_register(struct ds2781_device_info *dev_info, | ||
372 | u8 *control_reg) | ||
373 | { | ||
374 | return ds2781_read8(dev_info, control_reg, DS2781_CONTROL); | ||
375 | } | ||
376 | |||
377 | static int ds2781_set_control_register(struct ds2781_device_info *dev_info, | ||
378 | u8 control_reg) | ||
379 | { | ||
380 | int ret; | ||
381 | |||
382 | ret = ds2781_write(dev_info, &control_reg, | ||
383 | DS2781_CONTROL, sizeof(u8)); | ||
384 | if (ret < 0) | ||
385 | return ret; | ||
386 | |||
387 | return ds2781_save_eeprom(dev_info, DS2781_CONTROL); | ||
388 | } | ||
389 | |||
390 | static int ds2781_battery_get_property(struct power_supply *psy, | ||
391 | enum power_supply_property psp, | ||
392 | union power_supply_propval *val) | ||
393 | { | ||
394 | int ret = 0; | ||
395 | struct ds2781_device_info *dev_info = to_ds2781_device_info(psy); | ||
396 | |||
397 | switch (psp) { | ||
398 | case POWER_SUPPLY_PROP_VOLTAGE_NOW: | ||
399 | ret = ds2781_get_voltage(dev_info, &val->intval); | ||
400 | break; | ||
401 | |||
402 | case POWER_SUPPLY_PROP_TEMP: | ||
403 | ret = ds2781_get_temperature(dev_info, &val->intval); | ||
404 | break; | ||
405 | |||
406 | case POWER_SUPPLY_PROP_MODEL_NAME: | ||
407 | val->strval = model; | ||
408 | break; | ||
409 | |||
410 | case POWER_SUPPLY_PROP_MANUFACTURER: | ||
411 | val->strval = manufacturer; | ||
412 | break; | ||
413 | |||
414 | case POWER_SUPPLY_PROP_CURRENT_NOW: | ||
415 | ret = ds2781_get_current(dev_info, CURRENT_NOW, &val->intval); | ||
416 | break; | ||
417 | |||
418 | case POWER_SUPPLY_PROP_CURRENT_AVG: | ||
419 | ret = ds2781_get_current(dev_info, CURRENT_AVG, &val->intval); | ||
420 | break; | ||
421 | |||
422 | case POWER_SUPPLY_PROP_STATUS: | ||
423 | ret = ds2781_get_status(dev_info, &val->intval); | ||
424 | break; | ||
425 | |||
426 | case POWER_SUPPLY_PROP_CAPACITY: | ||
427 | ret = ds2781_get_capacity(dev_info, &val->intval); | ||
428 | break; | ||
429 | |||
430 | case POWER_SUPPLY_PROP_CHARGE_COUNTER: | ||
431 | ret = ds2781_get_accumulated_current(dev_info, &val->intval); | ||
432 | break; | ||
433 | |||
434 | case POWER_SUPPLY_PROP_CHARGE_NOW: | ||
435 | ret = ds2781_get_charge_now(dev_info, &val->intval); | ||
436 | break; | ||
437 | |||
438 | default: | ||
439 | ret = -EINVAL; | ||
440 | } | ||
441 | |||
442 | return ret; | ||
443 | } | ||
444 | |||
445 | static enum power_supply_property ds2781_battery_props[] = { | ||
446 | POWER_SUPPLY_PROP_STATUS, | ||
447 | POWER_SUPPLY_PROP_VOLTAGE_NOW, | ||
448 | POWER_SUPPLY_PROP_TEMP, | ||
449 | POWER_SUPPLY_PROP_MODEL_NAME, | ||
450 | POWER_SUPPLY_PROP_MANUFACTURER, | ||
451 | POWER_SUPPLY_PROP_CURRENT_NOW, | ||
452 | POWER_SUPPLY_PROP_CURRENT_AVG, | ||
453 | POWER_SUPPLY_PROP_CAPACITY, | ||
454 | POWER_SUPPLY_PROP_CHARGE_COUNTER, | ||
455 | POWER_SUPPLY_PROP_CHARGE_NOW, | ||
456 | }; | ||
457 | |||
458 | static ssize_t ds2781_get_pmod_enabled(struct device *dev, | ||
459 | struct device_attribute *attr, | ||
460 | char *buf) | ||
461 | { | ||
462 | int ret; | ||
463 | u8 control_reg; | ||
464 | struct power_supply *psy = to_power_supply(dev); | ||
465 | struct ds2781_device_info *dev_info = to_ds2781_device_info(psy); | ||
466 | |||
467 | /* Get power mode */ | ||
468 | ret = ds2781_get_control_register(dev_info, &control_reg); | ||
469 | if (ret < 0) | ||
470 | return ret; | ||
471 | |||
472 | return sprintf(buf, "%d\n", | ||
473 | !!(control_reg & DS2781_CONTROL_PMOD)); | ||
474 | } | ||
475 | |||
476 | static ssize_t ds2781_set_pmod_enabled(struct device *dev, | ||
477 | struct device_attribute *attr, | ||
478 | const char *buf, | ||
479 | size_t count) | ||
480 | { | ||
481 | int ret; | ||
482 | u8 control_reg, new_setting; | ||
483 | struct power_supply *psy = to_power_supply(dev); | ||
484 | struct ds2781_device_info *dev_info = to_ds2781_device_info(psy); | ||
485 | |||
486 | /* Set power mode */ | ||
487 | ret = ds2781_get_control_register(dev_info, &control_reg); | ||
488 | if (ret < 0) | ||
489 | return ret; | ||
490 | |||
491 | ret = kstrtou8(buf, 0, &new_setting); | ||
492 | if (ret < 0) | ||
493 | return ret; | ||
494 | |||
495 | if ((new_setting != 0) && (new_setting != 1)) { | ||
496 | dev_err(dev_info->dev, "Invalid pmod setting (0 or 1)\n"); | ||
497 | return -EINVAL; | ||
498 | } | ||
499 | |||
500 | if (new_setting) | ||
501 | control_reg |= DS2781_CONTROL_PMOD; | ||
502 | else | ||
503 | control_reg &= ~DS2781_CONTROL_PMOD; | ||
504 | |||
505 | ret = ds2781_set_control_register(dev_info, control_reg); | ||
506 | if (ret < 0) | ||
507 | return ret; | ||
508 | |||
509 | return count; | ||
510 | } | ||
511 | |||
512 | static ssize_t ds2781_get_sense_resistor_value(struct device *dev, | ||
513 | struct device_attribute *attr, | ||
514 | char *buf) | ||
515 | { | ||
516 | int ret; | ||
517 | u8 sense_resistor; | ||
518 | struct power_supply *psy = to_power_supply(dev); | ||
519 | struct ds2781_device_info *dev_info = to_ds2781_device_info(psy); | ||
520 | |||
521 | ret = ds2781_read8(dev_info, &sense_resistor, DS2781_RSNSP); | ||
522 | if (ret < 0) | ||
523 | return ret; | ||
524 | |||
525 | ret = sprintf(buf, "%d\n", sense_resistor); | ||
526 | return ret; | ||
527 | } | ||
528 | |||
529 | static ssize_t ds2781_set_sense_resistor_value(struct device *dev, | ||
530 | struct device_attribute *attr, | ||
531 | const char *buf, | ||
532 | size_t count) | ||
533 | { | ||
534 | int ret; | ||
535 | u8 new_setting; | ||
536 | struct power_supply *psy = to_power_supply(dev); | ||
537 | struct ds2781_device_info *dev_info = to_ds2781_device_info(psy); | ||
538 | |||
539 | ret = kstrtou8(buf, 0, &new_setting); | ||
540 | if (ret < 0) | ||
541 | return ret; | ||
542 | |||
543 | ret = ds2781_set_sense_register(dev_info, new_setting); | ||
544 | if (ret < 0) | ||
545 | return ret; | ||
546 | |||
547 | return count; | ||
548 | } | ||
549 | |||
550 | static ssize_t ds2781_get_rsgain_setting(struct device *dev, | ||
551 | struct device_attribute *attr, | ||
552 | char *buf) | ||
553 | { | ||
554 | int ret; | ||
555 | u16 rsgain; | ||
556 | struct power_supply *psy = to_power_supply(dev); | ||
557 | struct ds2781_device_info *dev_info = to_ds2781_device_info(psy); | ||
558 | |||
559 | ret = ds2781_get_rsgain_register(dev_info, &rsgain); | ||
560 | if (ret < 0) | ||
561 | return ret; | ||
562 | |||
563 | return sprintf(buf, "%d\n", rsgain); | ||
564 | } | ||
565 | |||
566 | static ssize_t ds2781_set_rsgain_setting(struct device *dev, | ||
567 | struct device_attribute *attr, | ||
568 | const char *buf, | ||
569 | size_t count) | ||
570 | { | ||
571 | int ret; | ||
572 | u16 new_setting; | ||
573 | struct power_supply *psy = to_power_supply(dev); | ||
574 | struct ds2781_device_info *dev_info = to_ds2781_device_info(psy); | ||
575 | |||
576 | ret = kstrtou16(buf, 0, &new_setting); | ||
577 | if (ret < 0) | ||
578 | return ret; | ||
579 | |||
580 | /* Gain can only be from 0 to 1.999 in steps of .001 */ | ||
581 | if (new_setting > 1999) { | ||
582 | dev_err(dev_info->dev, "Invalid rsgain setting (0 - 1999)\n"); | ||
583 | return -EINVAL; | ||
584 | } | ||
585 | |||
586 | ret = ds2781_set_rsgain_register(dev_info, new_setting); | ||
587 | if (ret < 0) | ||
588 | return ret; | ||
589 | |||
590 | return count; | ||
591 | } | ||
592 | |||
593 | static ssize_t ds2781_get_pio_pin(struct device *dev, | ||
594 | struct device_attribute *attr, | ||
595 | char *buf) | ||
596 | { | ||
597 | int ret; | ||
598 | u8 sfr; | ||
599 | struct power_supply *psy = to_power_supply(dev); | ||
600 | struct ds2781_device_info *dev_info = to_ds2781_device_info(psy); | ||
601 | |||
602 | ret = ds2781_read8(dev_info, &sfr, DS2781_SFR); | ||
603 | if (ret < 0) | ||
604 | return ret; | ||
605 | |||
606 | ret = sprintf(buf, "%d\n", sfr & DS2781_SFR_PIOSC); | ||
607 | return ret; | ||
608 | } | ||
609 | |||
610 | static ssize_t ds2781_set_pio_pin(struct device *dev, | ||
611 | struct device_attribute *attr, | ||
612 | const char *buf, | ||
613 | size_t count) | ||
614 | { | ||
615 | int ret; | ||
616 | u8 new_setting; | ||
617 | struct power_supply *psy = to_power_supply(dev); | ||
618 | struct ds2781_device_info *dev_info = to_ds2781_device_info(psy); | ||
619 | |||
620 | ret = kstrtou8(buf, 0, &new_setting); | ||
621 | if (ret < 0) | ||
622 | return ret; | ||
623 | |||
624 | if ((new_setting != 0) && (new_setting != 1)) { | ||
625 | dev_err(dev_info->dev, "Invalid pio_pin setting (0 or 1)\n"); | ||
626 | return -EINVAL; | ||
627 | } | ||
628 | |||
629 | ret = ds2781_write(dev_info, &new_setting, | ||
630 | DS2781_SFR, sizeof(u8)); | ||
631 | if (ret < 0) | ||
632 | return ret; | ||
633 | |||
634 | return count; | ||
635 | } | ||
636 | |||
637 | static ssize_t ds2781_read_param_eeprom_bin(struct file *filp, | ||
638 | struct kobject *kobj, | ||
639 | struct bin_attribute *bin_attr, | ||
640 | char *buf, loff_t off, size_t count) | ||
641 | { | ||
642 | struct device *dev = container_of(kobj, struct device, kobj); | ||
643 | struct power_supply *psy = to_power_supply(dev); | ||
644 | struct ds2781_device_info *dev_info = to_ds2781_device_info(psy); | ||
645 | |||
646 | count = min_t(loff_t, count, | ||
647 | DS2781_EEPROM_BLOCK1_END - | ||
648 | DS2781_EEPROM_BLOCK1_START + 1 - off); | ||
649 | |||
650 | return ds2781_read_block(dev_info, buf, | ||
651 | DS2781_EEPROM_BLOCK1_START + off, count); | ||
652 | } | ||
653 | |||
654 | static ssize_t ds2781_write_param_eeprom_bin(struct file *filp, | ||
655 | struct kobject *kobj, | ||
656 | struct bin_attribute *bin_attr, | ||
657 | char *buf, loff_t off, size_t count) | ||
658 | { | ||
659 | struct device *dev = container_of(kobj, struct device, kobj); | ||
660 | struct power_supply *psy = to_power_supply(dev); | ||
661 | struct ds2781_device_info *dev_info = to_ds2781_device_info(psy); | ||
662 | int ret; | ||
663 | |||
664 | count = min_t(loff_t, count, | ||
665 | DS2781_EEPROM_BLOCK1_END - | ||
666 | DS2781_EEPROM_BLOCK1_START + 1 - off); | ||
667 | |||
668 | ret = ds2781_write(dev_info, buf, | ||
669 | DS2781_EEPROM_BLOCK1_START + off, count); | ||
670 | if (ret < 0) | ||
671 | return ret; | ||
672 | |||
673 | ret = ds2781_save_eeprom(dev_info, DS2781_EEPROM_BLOCK1_START); | ||
674 | if (ret < 0) | ||
675 | return ret; | ||
676 | |||
677 | return count; | ||
678 | } | ||
679 | |||
680 | static struct bin_attribute ds2781_param_eeprom_bin_attr = { | ||
681 | .attr = { | ||
682 | .name = "param_eeprom", | ||
683 | .mode = S_IRUGO | S_IWUSR, | ||
684 | }, | ||
685 | .size = DS2781_EEPROM_BLOCK1_END - DS2781_EEPROM_BLOCK1_START + 1, | ||
686 | .read = ds2781_read_param_eeprom_bin, | ||
687 | .write = ds2781_write_param_eeprom_bin, | ||
688 | }; | ||
689 | |||
690 | static ssize_t ds2781_read_user_eeprom_bin(struct file *filp, | ||
691 | struct kobject *kobj, | ||
692 | struct bin_attribute *bin_attr, | ||
693 | char *buf, loff_t off, size_t count) | ||
694 | { | ||
695 | struct device *dev = container_of(kobj, struct device, kobj); | ||
696 | struct power_supply *psy = to_power_supply(dev); | ||
697 | struct ds2781_device_info *dev_info = to_ds2781_device_info(psy); | ||
698 | |||
699 | count = min_t(loff_t, count, | ||
700 | DS2781_EEPROM_BLOCK0_END - | ||
701 | DS2781_EEPROM_BLOCK0_START + 1 - off); | ||
702 | |||
703 | return ds2781_read_block(dev_info, buf, | ||
704 | DS2781_EEPROM_BLOCK0_START + off, count); | ||
705 | |||
706 | } | ||
707 | |||
708 | static ssize_t ds2781_write_user_eeprom_bin(struct file *filp, | ||
709 | struct kobject *kobj, | ||
710 | struct bin_attribute *bin_attr, | ||
711 | char *buf, loff_t off, size_t count) | ||
712 | { | ||
713 | struct device *dev = container_of(kobj, struct device, kobj); | ||
714 | struct power_supply *psy = to_power_supply(dev); | ||
715 | struct ds2781_device_info *dev_info = to_ds2781_device_info(psy); | ||
716 | int ret; | ||
717 | |||
718 | count = min_t(loff_t, count, | ||
719 | DS2781_EEPROM_BLOCK0_END - | ||
720 | DS2781_EEPROM_BLOCK0_START + 1 - off); | ||
721 | |||
722 | ret = ds2781_write(dev_info, buf, | ||
723 | DS2781_EEPROM_BLOCK0_START + off, count); | ||
724 | if (ret < 0) | ||
725 | return ret; | ||
726 | |||
727 | ret = ds2781_save_eeprom(dev_info, DS2781_EEPROM_BLOCK0_START); | ||
728 | if (ret < 0) | ||
729 | return ret; | ||
730 | |||
731 | return count; | ||
732 | } | ||
733 | |||
734 | static struct bin_attribute ds2781_user_eeprom_bin_attr = { | ||
735 | .attr = { | ||
736 | .name = "user_eeprom", | ||
737 | .mode = S_IRUGO | S_IWUSR, | ||
738 | }, | ||
739 | .size = DS2781_EEPROM_BLOCK0_END - DS2781_EEPROM_BLOCK0_START + 1, | ||
740 | .read = ds2781_read_user_eeprom_bin, | ||
741 | .write = ds2781_write_user_eeprom_bin, | ||
742 | }; | ||
743 | |||
744 | static DEVICE_ATTR(pmod_enabled, S_IRUGO | S_IWUSR, ds2781_get_pmod_enabled, | ||
745 | ds2781_set_pmod_enabled); | ||
746 | static DEVICE_ATTR(sense_resistor_value, S_IRUGO | S_IWUSR, | ||
747 | ds2781_get_sense_resistor_value, ds2781_set_sense_resistor_value); | ||
748 | static DEVICE_ATTR(rsgain_setting, S_IRUGO | S_IWUSR, ds2781_get_rsgain_setting, | ||
749 | ds2781_set_rsgain_setting); | ||
750 | static DEVICE_ATTR(pio_pin, S_IRUGO | S_IWUSR, ds2781_get_pio_pin, | ||
751 | ds2781_set_pio_pin); | ||
752 | |||
753 | |||
754 | static struct attribute *ds2781_attributes[] = { | ||
755 | &dev_attr_pmod_enabled.attr, | ||
756 | &dev_attr_sense_resistor_value.attr, | ||
757 | &dev_attr_rsgain_setting.attr, | ||
758 | &dev_attr_pio_pin.attr, | ||
759 | NULL | ||
760 | }; | ||
761 | |||
762 | static const struct attribute_group ds2781_attr_group = { | ||
763 | .attrs = ds2781_attributes, | ||
764 | }; | ||
765 | |||
766 | static int __devinit ds2781_battery_probe(struct platform_device *pdev) | ||
767 | { | ||
768 | int ret = 0; | ||
769 | struct ds2781_device_info *dev_info; | ||
770 | |||
771 | dev_info = kzalloc(sizeof(*dev_info), GFP_KERNEL); | ||
772 | if (!dev_info) { | ||
773 | ret = -ENOMEM; | ||
774 | goto fail; | ||
775 | } | ||
776 | |||
777 | platform_set_drvdata(pdev, dev_info); | ||
778 | |||
779 | dev_info->dev = &pdev->dev; | ||
780 | dev_info->w1_dev = pdev->dev.parent; | ||
781 | dev_info->bat.name = dev_name(&pdev->dev); | ||
782 | dev_info->bat.type = POWER_SUPPLY_TYPE_BATTERY; | ||
783 | dev_info->bat.properties = ds2781_battery_props; | ||
784 | dev_info->bat.num_properties = ARRAY_SIZE(ds2781_battery_props); | ||
785 | dev_info->bat.get_property = ds2781_battery_get_property; | ||
786 | dev_info->mutex_holder = current; | ||
787 | |||
788 | ret = power_supply_register(&pdev->dev, &dev_info->bat); | ||
789 | if (ret) { | ||
790 | dev_err(dev_info->dev, "failed to register battery\n"); | ||
791 | goto fail_free_info; | ||
792 | } | ||
793 | |||
794 | ret = sysfs_create_group(&dev_info->bat.dev->kobj, &ds2781_attr_group); | ||
795 | if (ret) { | ||
796 | dev_err(dev_info->dev, "failed to create sysfs group\n"); | ||
797 | goto fail_unregister; | ||
798 | } | ||
799 | |||
800 | ret = sysfs_create_bin_file(&dev_info->bat.dev->kobj, | ||
801 | &ds2781_param_eeprom_bin_attr); | ||
802 | if (ret) { | ||
803 | dev_err(dev_info->dev, | ||
804 | "failed to create param eeprom bin file"); | ||
805 | goto fail_remove_group; | ||
806 | } | ||
807 | |||
808 | ret = sysfs_create_bin_file(&dev_info->bat.dev->kobj, | ||
809 | &ds2781_user_eeprom_bin_attr); | ||
810 | if (ret) { | ||
811 | dev_err(dev_info->dev, | ||
812 | "failed to create user eeprom bin file"); | ||
813 | goto fail_remove_bin_file; | ||
814 | } | ||
815 | |||
816 | dev_info->mutex_holder = NULL; | ||
817 | |||
818 | return 0; | ||
819 | |||
820 | fail_remove_bin_file: | ||
821 | sysfs_remove_bin_file(&dev_info->bat.dev->kobj, | ||
822 | &ds2781_param_eeprom_bin_attr); | ||
823 | fail_remove_group: | ||
824 | sysfs_remove_group(&dev_info->bat.dev->kobj, &ds2781_attr_group); | ||
825 | fail_unregister: | ||
826 | power_supply_unregister(&dev_info->bat); | ||
827 | fail_free_info: | ||
828 | kfree(dev_info); | ||
829 | fail: | ||
830 | return ret; | ||
831 | } | ||
832 | |||
833 | static int __devexit ds2781_battery_remove(struct platform_device *pdev) | ||
834 | { | ||
835 | struct ds2781_device_info *dev_info = platform_get_drvdata(pdev); | ||
836 | |||
837 | dev_info->mutex_holder = current; | ||
838 | |||
839 | /* remove attributes */ | ||
840 | sysfs_remove_group(&dev_info->bat.dev->kobj, &ds2781_attr_group); | ||
841 | |||
842 | power_supply_unregister(&dev_info->bat); | ||
843 | |||
844 | kfree(dev_info); | ||
845 | return 0; | ||
846 | } | ||
847 | |||
848 | static struct platform_driver ds2781_battery_driver = { | ||
849 | .driver = { | ||
850 | .name = "ds2781-battery", | ||
851 | }, | ||
852 | .probe = ds2781_battery_probe, | ||
853 | .remove = __devexit_p(ds2781_battery_remove), | ||
854 | }; | ||
855 | |||
856 | static int __init ds2781_battery_init(void) | ||
857 | { | ||
858 | return platform_driver_register(&ds2781_battery_driver); | ||
859 | } | ||
860 | |||
861 | static void __exit ds2781_battery_exit(void) | ||
862 | { | ||
863 | platform_driver_unregister(&ds2781_battery_driver); | ||
864 | } | ||
865 | |||
866 | module_init(ds2781_battery_init); | ||
867 | module_exit(ds2781_battery_exit); | ||
868 | |||
869 | |||
870 | MODULE_LICENSE("GPL"); | ||
871 | MODULE_AUTHOR("Renata Sayakhova <renata@oktetlabs.ru>"); | ||
872 | MODULE_DESCRIPTION("Maxim/Dallas DS2781 Stand-Alone Fuel Gauage IC driver"); | ||
873 | MODULE_ALIAS("platform:ds2781-battery"); | ||
874 | |||