]>
Commit | Line | Data |
---|---|---|
87c2d906 KK |
1 | /* |
2 | * max77693_charger.c - Battery charger driver for the Maxim 77693 | |
3 | * | |
4 | * Copyright (C) 2014 Samsung Electronics | |
1c4593ed | 5 | * Krzysztof Kozlowski <krzk@kernel.org> |
87c2d906 KK |
6 | * |
7 | * This program is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License as published by | |
9 | * the Free Software Foundation; either version 2 of the License, or | |
10 | * (at your option) any later version. | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, | |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | * GNU General Public License for more details. | |
16 | */ | |
17 | ||
18 | #include <linux/module.h> | |
19 | #include <linux/platform_device.h> | |
20 | #include <linux/power_supply.h> | |
21 | #include <linux/regmap.h> | |
22 | #include <linux/mfd/max77693.h> | |
61b305cd | 23 | #include <linux/mfd/max77693-common.h> |
87c2d906 KK |
24 | #include <linux/mfd/max77693-private.h> |
25 | ||
297d716f | 26 | #define MAX77693_CHARGER_NAME "max77693-charger" |
87c2d906 KK |
27 | static const char *max77693_charger_model = "MAX77693"; |
28 | static const char *max77693_charger_manufacturer = "Maxim Integrated"; | |
29 | ||
30 | struct max77693_charger { | |
31 | struct device *dev; | |
32 | struct max77693_dev *max77693; | |
297d716f | 33 | struct power_supply *charger; |
87c2d906 KK |
34 | |
35 | u32 constant_volt; | |
36 | u32 min_system_volt; | |
37 | u32 thermal_regulation_temp; | |
38 | u32 batttery_overcurrent; | |
39 | u32 charge_input_threshold_volt; | |
40 | }; | |
41 | ||
1ed522b3 | 42 | static int max77693_get_charger_state(struct regmap *regmap, int *val) |
87c2d906 | 43 | { |
1ed522b3 | 44 | int ret; |
87c2d906 KK |
45 | unsigned int data; |
46 | ||
1ed522b3 KK |
47 | ret = regmap_read(regmap, MAX77693_CHG_REG_CHG_DETAILS_01, &data); |
48 | if (ret < 0) | |
49 | return ret; | |
87c2d906 KK |
50 | |
51 | data &= CHG_DETAILS_01_CHG_MASK; | |
52 | data >>= CHG_DETAILS_01_CHG_SHIFT; | |
53 | ||
54 | switch (data) { | |
55 | case MAX77693_CHARGING_PREQUALIFICATION: | |
56 | case MAX77693_CHARGING_FAST_CONST_CURRENT: | |
57 | case MAX77693_CHARGING_FAST_CONST_VOLTAGE: | |
58 | case MAX77693_CHARGING_TOP_OFF: | |
59 | /* In high temp the charging current is reduced, but still charging */ | |
60 | case MAX77693_CHARGING_HIGH_TEMP: | |
1ed522b3 | 61 | *val = POWER_SUPPLY_STATUS_CHARGING; |
87c2d906 KK |
62 | break; |
63 | case MAX77693_CHARGING_DONE: | |
1ed522b3 | 64 | *val = POWER_SUPPLY_STATUS_FULL; |
87c2d906 KK |
65 | break; |
66 | case MAX77693_CHARGING_TIMER_EXPIRED: | |
67 | case MAX77693_CHARGING_THERMISTOR_SUSPEND: | |
1ed522b3 | 68 | *val = POWER_SUPPLY_STATUS_NOT_CHARGING; |
87c2d906 KK |
69 | break; |
70 | case MAX77693_CHARGING_OFF: | |
71 | case MAX77693_CHARGING_OVER_TEMP: | |
72 | case MAX77693_CHARGING_WATCHDOG_EXPIRED: | |
1ed522b3 | 73 | *val = POWER_SUPPLY_STATUS_DISCHARGING; |
87c2d906 KK |
74 | break; |
75 | case MAX77693_CHARGING_RESERVED: | |
76 | default: | |
1ed522b3 | 77 | *val = POWER_SUPPLY_STATUS_UNKNOWN; |
87c2d906 KK |
78 | } |
79 | ||
1ed522b3 | 80 | return 0; |
87c2d906 KK |
81 | } |
82 | ||
1ed522b3 | 83 | static int max77693_get_charge_type(struct regmap *regmap, int *val) |
87c2d906 | 84 | { |
1ed522b3 | 85 | int ret; |
87c2d906 KK |
86 | unsigned int data; |
87 | ||
1ed522b3 KK |
88 | ret = regmap_read(regmap, MAX77693_CHG_REG_CHG_DETAILS_01, &data); |
89 | if (ret < 0) | |
90 | return ret; | |
87c2d906 KK |
91 | |
92 | data &= CHG_DETAILS_01_CHG_MASK; | |
93 | data >>= CHG_DETAILS_01_CHG_SHIFT; | |
94 | ||
95 | switch (data) { | |
96 | case MAX77693_CHARGING_PREQUALIFICATION: | |
97 | /* | |
98 | * Top-off: trickle or fast? In top-off the current varies between | |
99 | * 100 and 250 mA. It is higher than prequalification current. | |
100 | */ | |
101 | case MAX77693_CHARGING_TOP_OFF: | |
1ed522b3 | 102 | *val = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; |
87c2d906 KK |
103 | break; |
104 | case MAX77693_CHARGING_FAST_CONST_CURRENT: | |
105 | case MAX77693_CHARGING_FAST_CONST_VOLTAGE: | |
106 | /* In high temp the charging current is reduced, but still charging */ | |
107 | case MAX77693_CHARGING_HIGH_TEMP: | |
1ed522b3 | 108 | *val = POWER_SUPPLY_CHARGE_TYPE_FAST; |
87c2d906 KK |
109 | break; |
110 | case MAX77693_CHARGING_DONE: | |
111 | case MAX77693_CHARGING_TIMER_EXPIRED: | |
112 | case MAX77693_CHARGING_THERMISTOR_SUSPEND: | |
113 | case MAX77693_CHARGING_OFF: | |
114 | case MAX77693_CHARGING_OVER_TEMP: | |
115 | case MAX77693_CHARGING_WATCHDOG_EXPIRED: | |
1ed522b3 | 116 | *val = POWER_SUPPLY_CHARGE_TYPE_NONE; |
87c2d906 KK |
117 | break; |
118 | case MAX77693_CHARGING_RESERVED: | |
119 | default: | |
1ed522b3 | 120 | *val = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN; |
87c2d906 KK |
121 | } |
122 | ||
1ed522b3 | 123 | return 0; |
87c2d906 KK |
124 | } |
125 | ||
126 | /* | |
127 | * Supported health statuses: | |
128 | * - POWER_SUPPLY_HEALTH_DEAD | |
129 | * - POWER_SUPPLY_HEALTH_GOOD | |
130 | * - POWER_SUPPLY_HEALTH_OVERVOLTAGE | |
131 | * - POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE | |
132 | * - POWER_SUPPLY_HEALTH_UNKNOWN | |
133 | * - POWER_SUPPLY_HEALTH_UNSPEC_FAILURE | |
134 | */ | |
1ed522b3 | 135 | static int max77693_get_battery_health(struct regmap *regmap, int *val) |
87c2d906 | 136 | { |
1ed522b3 | 137 | int ret; |
87c2d906 KK |
138 | unsigned int data; |
139 | ||
1ed522b3 KK |
140 | ret = regmap_read(regmap, MAX77693_CHG_REG_CHG_DETAILS_01, &data); |
141 | if (ret < 0) | |
142 | return ret; | |
87c2d906 KK |
143 | |
144 | data &= CHG_DETAILS_01_BAT_MASK; | |
145 | data >>= CHG_DETAILS_01_BAT_SHIFT; | |
146 | ||
147 | switch (data) { | |
148 | case MAX77693_BATTERY_NOBAT: | |
1ed522b3 | 149 | *val = POWER_SUPPLY_HEALTH_DEAD; |
87c2d906 KK |
150 | break; |
151 | case MAX77693_BATTERY_PREQUALIFICATION: | |
152 | case MAX77693_BATTERY_GOOD: | |
153 | case MAX77693_BATTERY_LOWVOLTAGE: | |
1ed522b3 | 154 | *val = POWER_SUPPLY_HEALTH_GOOD; |
87c2d906 KK |
155 | break; |
156 | case MAX77693_BATTERY_TIMER_EXPIRED: | |
157 | /* | |
158 | * Took longer to charge than expected, charging suspended. | |
159 | * Damaged battery? | |
160 | */ | |
1ed522b3 | 161 | *val = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE; |
87c2d906 KK |
162 | break; |
163 | case MAX77693_BATTERY_OVERVOLTAGE: | |
1ed522b3 | 164 | *val = POWER_SUPPLY_HEALTH_OVERVOLTAGE; |
87c2d906 KK |
165 | break; |
166 | case MAX77693_BATTERY_OVERCURRENT: | |
1ed522b3 | 167 | *val = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; |
87c2d906 KK |
168 | break; |
169 | case MAX77693_BATTERY_RESERVED: | |
170 | default: | |
1ed522b3 | 171 | *val = POWER_SUPPLY_HEALTH_UNKNOWN; |
87c2d906 KK |
172 | break; |
173 | } | |
174 | ||
1ed522b3 | 175 | return 0; |
87c2d906 KK |
176 | } |
177 | ||
1ed522b3 | 178 | static int max77693_get_present(struct regmap *regmap, int *val) |
87c2d906 KK |
179 | { |
180 | unsigned int data; | |
1ed522b3 | 181 | int ret; |
87c2d906 KK |
182 | |
183 | /* | |
184 | * Read CHG_INT_OK register. High DETBAT bit here should be | |
185 | * equal to value 0x0 in CHG_DETAILS_01/BAT field. | |
186 | */ | |
1ed522b3 KK |
187 | ret = regmap_read(regmap, MAX77693_CHG_REG_CHG_INT_OK, &data); |
188 | if (ret < 0) | |
189 | return ret; | |
190 | ||
191 | *val = (data & CHG_INT_OK_DETBAT_MASK) ? 0 : 1; | |
192 | ||
193 | return 0; | |
87c2d906 KK |
194 | } |
195 | ||
1ed522b3 | 196 | static int max77693_get_online(struct regmap *regmap, int *val) |
87c2d906 KK |
197 | { |
198 | unsigned int data; | |
1ed522b3 KK |
199 | int ret; |
200 | ||
201 | ret = regmap_read(regmap, MAX77693_CHG_REG_CHG_INT_OK, &data); | |
202 | if (ret < 0) | |
203 | return ret; | |
204 | ||
205 | *val = (data & CHG_INT_OK_CHGIN_MASK) ? 1 : 0; | |
87c2d906 | 206 | |
87c2d906 KK |
207 | return 0; |
208 | } | |
209 | ||
210 | static enum power_supply_property max77693_charger_props[] = { | |
211 | POWER_SUPPLY_PROP_STATUS, | |
212 | POWER_SUPPLY_PROP_CHARGE_TYPE, | |
213 | POWER_SUPPLY_PROP_HEALTH, | |
214 | POWER_SUPPLY_PROP_PRESENT, | |
215 | POWER_SUPPLY_PROP_ONLINE, | |
216 | POWER_SUPPLY_PROP_MODEL_NAME, | |
217 | POWER_SUPPLY_PROP_MANUFACTURER, | |
218 | }; | |
219 | ||
220 | static int max77693_charger_get_property(struct power_supply *psy, | |
221 | enum power_supply_property psp, | |
222 | union power_supply_propval *val) | |
223 | { | |
297d716f | 224 | struct max77693_charger *chg = power_supply_get_drvdata(psy); |
87c2d906 KK |
225 | struct regmap *regmap = chg->max77693->regmap; |
226 | int ret = 0; | |
227 | ||
228 | switch (psp) { | |
229 | case POWER_SUPPLY_PROP_STATUS: | |
1ed522b3 | 230 | ret = max77693_get_charger_state(regmap, &val->intval); |
87c2d906 KK |
231 | break; |
232 | case POWER_SUPPLY_PROP_CHARGE_TYPE: | |
1ed522b3 | 233 | ret = max77693_get_charge_type(regmap, &val->intval); |
87c2d906 KK |
234 | break; |
235 | case POWER_SUPPLY_PROP_HEALTH: | |
1ed522b3 | 236 | ret = max77693_get_battery_health(regmap, &val->intval); |
87c2d906 KK |
237 | break; |
238 | case POWER_SUPPLY_PROP_PRESENT: | |
1ed522b3 | 239 | ret = max77693_get_present(regmap, &val->intval); |
87c2d906 KK |
240 | break; |
241 | case POWER_SUPPLY_PROP_ONLINE: | |
1ed522b3 | 242 | ret = max77693_get_online(regmap, &val->intval); |
87c2d906 KK |
243 | break; |
244 | case POWER_SUPPLY_PROP_MODEL_NAME: | |
245 | val->strval = max77693_charger_model; | |
246 | break; | |
247 | case POWER_SUPPLY_PROP_MANUFACTURER: | |
248 | val->strval = max77693_charger_manufacturer; | |
249 | break; | |
250 | default: | |
251 | return -EINVAL; | |
252 | } | |
253 | ||
254 | return ret; | |
255 | } | |
256 | ||
297d716f KK |
257 | static const struct power_supply_desc max77693_charger_desc = { |
258 | .name = MAX77693_CHARGER_NAME, | |
259 | .type = POWER_SUPPLY_TYPE_BATTERY, | |
260 | .properties = max77693_charger_props, | |
261 | .num_properties = ARRAY_SIZE(max77693_charger_props), | |
262 | .get_property = max77693_charger_get_property, | |
263 | }; | |
264 | ||
87c2d906 KK |
265 | static ssize_t device_attr_store(struct device *dev, |
266 | struct device_attribute *attr, const char *buf, size_t count, | |
267 | int (*fn)(struct max77693_charger *, unsigned long)) | |
268 | { | |
269 | struct max77693_charger *chg = dev_get_drvdata(dev); | |
270 | unsigned long val; | |
271 | int ret; | |
272 | ||
273 | ret = kstrtoul(buf, 10, &val); | |
274 | if (ret) | |
275 | return ret; | |
276 | ||
277 | ret = fn(chg, val); | |
278 | if (ret) | |
279 | return ret; | |
280 | ||
281 | return count; | |
282 | } | |
283 | ||
284 | static ssize_t fast_charge_timer_show(struct device *dev, | |
285 | struct device_attribute *attr, char *buf) | |
286 | { | |
287 | struct max77693_charger *chg = dev_get_drvdata(dev); | |
288 | unsigned int data, val; | |
289 | int ret; | |
290 | ||
291 | ret = regmap_read(chg->max77693->regmap, MAX77693_CHG_REG_CHG_CNFG_01, | |
292 | &data); | |
293 | if (ret < 0) | |
294 | return ret; | |
295 | ||
296 | data &= CHG_CNFG_01_FCHGTIME_MASK; | |
297 | data >>= CHG_CNFG_01_FCHGTIME_SHIFT; | |
298 | switch (data) { | |
299 | case 0x1 ... 0x7: | |
300 | /* Starting from 4 hours, step by 2 hours */ | |
301 | val = 4 + (data - 1) * 2; | |
302 | break; | |
303 | case 0x0: | |
304 | default: | |
305 | val = 0; | |
306 | break; | |
307 | } | |
308 | ||
309 | return scnprintf(buf, PAGE_SIZE, "%u\n", val); | |
310 | } | |
311 | ||
312 | static int max77693_set_fast_charge_timer(struct max77693_charger *chg, | |
313 | unsigned long hours) | |
314 | { | |
315 | unsigned int data; | |
316 | ||
317 | /* | |
318 | * 0x00 - disable | |
319 | * 0x01 - 4h | |
320 | * 0x02 - 6h | |
321 | * ... | |
322 | * 0x07 - 16h | |
323 | * Round down odd values. | |
324 | */ | |
325 | switch (hours) { | |
326 | case 4 ... 16: | |
327 | data = (hours - 4) / 2 + 1; | |
328 | break; | |
329 | case 0: | |
330 | /* Disable */ | |
331 | data = 0; | |
332 | break; | |
333 | default: | |
334 | return -EINVAL; | |
335 | } | |
336 | data <<= CHG_CNFG_01_FCHGTIME_SHIFT; | |
337 | ||
338 | return regmap_update_bits(chg->max77693->regmap, | |
339 | MAX77693_CHG_REG_CHG_CNFG_01, | |
340 | CHG_CNFG_01_FCHGTIME_MASK, data); | |
341 | } | |
342 | ||
343 | static ssize_t fast_charge_timer_store(struct device *dev, | |
344 | struct device_attribute *attr, const char *buf, size_t count) | |
345 | { | |
346 | return device_attr_store(dev, attr, buf, count, | |
347 | max77693_set_fast_charge_timer); | |
348 | } | |
349 | ||
350 | static ssize_t top_off_threshold_current_show(struct device *dev, | |
351 | struct device_attribute *attr, char *buf) | |
352 | { | |
353 | struct max77693_charger *chg = dev_get_drvdata(dev); | |
354 | unsigned int data, val; | |
355 | int ret; | |
356 | ||
357 | ret = regmap_read(chg->max77693->regmap, MAX77693_CHG_REG_CHG_CNFG_03, | |
358 | &data); | |
359 | if (ret < 0) | |
360 | return ret; | |
361 | ||
362 | data &= CHG_CNFG_03_TOITH_MASK; | |
363 | data >>= CHG_CNFG_03_TOITH_SHIFT; | |
364 | ||
365 | if (data <= 0x04) | |
366 | val = 100000 + data * 25000; | |
367 | else | |
368 | val = data * 50000; | |
369 | ||
370 | return scnprintf(buf, PAGE_SIZE, "%u\n", val); | |
371 | } | |
372 | ||
373 | static int max77693_set_top_off_threshold_current(struct max77693_charger *chg, | |
374 | unsigned long uamp) | |
375 | { | |
376 | unsigned int data; | |
377 | ||
378 | if (uamp < 100000 || uamp > 350000) | |
379 | return -EINVAL; | |
380 | ||
381 | if (uamp <= 200000) | |
382 | data = (uamp - 100000) / 25000; | |
383 | else | |
384 | /* (200000, 350000> */ | |
385 | data = uamp / 50000; | |
386 | ||
387 | data <<= CHG_CNFG_03_TOITH_SHIFT; | |
388 | ||
389 | return regmap_update_bits(chg->max77693->regmap, | |
390 | MAX77693_CHG_REG_CHG_CNFG_03, | |
391 | CHG_CNFG_03_TOITH_MASK, data); | |
392 | } | |
393 | ||
394 | static ssize_t top_off_threshold_current_store(struct device *dev, | |
395 | struct device_attribute *attr, const char *buf, size_t count) | |
396 | { | |
397 | return device_attr_store(dev, attr, buf, count, | |
398 | max77693_set_top_off_threshold_current); | |
399 | } | |
400 | ||
401 | static ssize_t top_off_timer_show(struct device *dev, | |
402 | struct device_attribute *attr, char *buf) | |
403 | { | |
404 | struct max77693_charger *chg = dev_get_drvdata(dev); | |
405 | unsigned int data, val; | |
406 | int ret; | |
407 | ||
408 | ret = regmap_read(chg->max77693->regmap, MAX77693_CHG_REG_CHG_CNFG_03, | |
409 | &data); | |
410 | if (ret < 0) | |
411 | return ret; | |
412 | ||
413 | data &= CHG_CNFG_03_TOTIME_MASK; | |
414 | data >>= CHG_CNFG_03_TOTIME_SHIFT; | |
415 | ||
416 | val = data * 10; | |
417 | ||
418 | return scnprintf(buf, PAGE_SIZE, "%u\n", val); | |
419 | } | |
420 | ||
421 | static int max77693_set_top_off_timer(struct max77693_charger *chg, | |
422 | unsigned long minutes) | |
423 | { | |
424 | unsigned int data; | |
425 | ||
426 | if (minutes > 70) | |
427 | return -EINVAL; | |
428 | ||
429 | data = minutes / 10; | |
430 | data <<= CHG_CNFG_03_TOTIME_SHIFT; | |
431 | ||
432 | return regmap_update_bits(chg->max77693->regmap, | |
433 | MAX77693_CHG_REG_CHG_CNFG_03, | |
434 | CHG_CNFG_03_TOTIME_MASK, data); | |
435 | } | |
436 | ||
437 | static ssize_t top_off_timer_store(struct device *dev, | |
438 | struct device_attribute *attr, const char *buf, size_t count) | |
439 | { | |
440 | return device_attr_store(dev, attr, buf, count, | |
441 | max77693_set_top_off_timer); | |
442 | } | |
443 | ||
444 | static DEVICE_ATTR_RW(fast_charge_timer); | |
445 | static DEVICE_ATTR_RW(top_off_threshold_current); | |
446 | static DEVICE_ATTR_RW(top_off_timer); | |
447 | ||
448 | static int max77693_set_constant_volt(struct max77693_charger *chg, | |
449 | unsigned int uvolt) | |
450 | { | |
451 | unsigned int data; | |
452 | ||
453 | /* | |
454 | * 0x00 - 3.650 V | |
455 | * 0x01 - 3.675 V | |
456 | * ... | |
457 | * 0x1b - 4.325 V | |
458 | * 0x1c - 4.340 V | |
459 | * 0x1d - 4.350 V | |
460 | * 0x1e - 4.375 V | |
461 | * 0x1f - 4.400 V | |
462 | */ | |
463 | if (uvolt >= 3650000 && uvolt < 4340000) | |
464 | data = (uvolt - 3650000) / 25000; | |
465 | else if (uvolt >= 4340000 && uvolt < 4350000) | |
466 | data = 0x1c; | |
467 | else if (uvolt >= 4350000 && uvolt <= 4400000) | |
468 | data = 0x1d + (uvolt - 4350000) / 25000; | |
469 | else { | |
470 | dev_err(chg->dev, "Wrong value for charging constant voltage\n"); | |
471 | return -EINVAL; | |
472 | } | |
473 | ||
474 | data <<= CHG_CNFG_04_CHGCVPRM_SHIFT; | |
475 | ||
476 | dev_dbg(chg->dev, "Charging constant voltage: %u (0x%x)\n", uvolt, | |
477 | data); | |
478 | ||
479 | return regmap_update_bits(chg->max77693->regmap, | |
480 | MAX77693_CHG_REG_CHG_CNFG_04, | |
481 | CHG_CNFG_04_CHGCVPRM_MASK, data); | |
482 | } | |
483 | ||
484 | static int max77693_set_min_system_volt(struct max77693_charger *chg, | |
485 | unsigned int uvolt) | |
486 | { | |
487 | unsigned int data; | |
488 | ||
489 | if (uvolt < 3000000 || uvolt > 3700000) { | |
490 | dev_err(chg->dev, "Wrong value for minimum system regulation voltage\n"); | |
491 | return -EINVAL; | |
492 | } | |
493 | ||
494 | data = (uvolt - 3000000) / 100000; | |
495 | ||
496 | data <<= CHG_CNFG_04_MINVSYS_SHIFT; | |
497 | ||
498 | dev_dbg(chg->dev, "Minimum system regulation voltage: %u (0x%x)\n", | |
499 | uvolt, data); | |
500 | ||
501 | return regmap_update_bits(chg->max77693->regmap, | |
502 | MAX77693_CHG_REG_CHG_CNFG_04, | |
503 | CHG_CNFG_04_MINVSYS_MASK, data); | |
504 | } | |
505 | ||
506 | static int max77693_set_thermal_regulation_temp(struct max77693_charger *chg, | |
507 | unsigned int cels) | |
508 | { | |
509 | unsigned int data; | |
510 | ||
511 | switch (cels) { | |
512 | case 70: | |
513 | case 85: | |
514 | case 100: | |
515 | case 115: | |
516 | data = (cels - 70) / 15; | |
517 | break; | |
518 | default: | |
519 | dev_err(chg->dev, "Wrong value for thermal regulation loop temperature\n"); | |
520 | return -EINVAL; | |
521 | } | |
522 | ||
523 | data <<= CHG_CNFG_07_REGTEMP_SHIFT; | |
524 | ||
525 | dev_dbg(chg->dev, "Thermal regulation loop temperature: %u (0x%x)\n", | |
526 | cels, data); | |
527 | ||
528 | return regmap_update_bits(chg->max77693->regmap, | |
529 | MAX77693_CHG_REG_CHG_CNFG_07, | |
530 | CHG_CNFG_07_REGTEMP_MASK, data); | |
531 | } | |
532 | ||
533 | static int max77693_set_batttery_overcurrent(struct max77693_charger *chg, | |
534 | unsigned int uamp) | |
535 | { | |
536 | unsigned int data; | |
537 | ||
538 | if (uamp && (uamp < 2000000 || uamp > 3500000)) { | |
539 | dev_err(chg->dev, "Wrong value for battery overcurrent\n"); | |
540 | return -EINVAL; | |
541 | } | |
542 | ||
543 | if (uamp) | |
544 | data = ((uamp - 2000000) / 250000) + 1; | |
545 | else | |
546 | data = 0; /* disable */ | |
547 | ||
548 | data <<= CHG_CNFG_12_B2SOVRC_SHIFT; | |
549 | ||
550 | dev_dbg(chg->dev, "Battery overcurrent: %u (0x%x)\n", uamp, data); | |
551 | ||
552 | return regmap_update_bits(chg->max77693->regmap, | |
553 | MAX77693_CHG_REG_CHG_CNFG_12, | |
554 | CHG_CNFG_12_B2SOVRC_MASK, data); | |
555 | } | |
556 | ||
557 | static int max77693_set_charge_input_threshold_volt(struct max77693_charger *chg, | |
558 | unsigned int uvolt) | |
559 | { | |
560 | unsigned int data; | |
561 | ||
562 | switch (uvolt) { | |
563 | case 4300000: | |
564 | data = 0x0; | |
565 | break; | |
566 | case 4700000: | |
567 | case 4800000: | |
568 | case 4900000: | |
569 | data = (uvolt - 4700000) / 100000; | |
570 | default: | |
571 | dev_err(chg->dev, "Wrong value for charge input voltage regulation threshold\n"); | |
572 | return -EINVAL; | |
573 | } | |
574 | ||
575 | data <<= CHG_CNFG_12_VCHGINREG_SHIFT; | |
576 | ||
577 | dev_dbg(chg->dev, "Charge input voltage regulation threshold: %u (0x%x)\n", | |
578 | uvolt, data); | |
579 | ||
580 | return regmap_update_bits(chg->max77693->regmap, | |
581 | MAX77693_CHG_REG_CHG_CNFG_12, | |
582 | CHG_CNFG_12_VCHGINREG_MASK, data); | |
583 | } | |
584 | ||
585 | /* | |
586 | * Sets charger registers to proper and safe default values. | |
587 | */ | |
588 | static int max77693_reg_init(struct max77693_charger *chg) | |
589 | { | |
590 | int ret; | |
591 | unsigned int data; | |
592 | ||
593 | /* Unlock charger register protection */ | |
594 | data = (0x3 << CHG_CNFG_06_CHGPROT_SHIFT); | |
595 | ret = regmap_update_bits(chg->max77693->regmap, | |
596 | MAX77693_CHG_REG_CHG_CNFG_06, | |
597 | CHG_CNFG_06_CHGPROT_MASK, data); | |
598 | if (ret) { | |
599 | dev_err(chg->dev, "Error unlocking registers: %d\n", ret); | |
600 | return ret; | |
601 | } | |
602 | ||
603 | ret = max77693_set_fast_charge_timer(chg, DEFAULT_FAST_CHARGE_TIMER); | |
604 | if (ret) | |
605 | return ret; | |
606 | ||
607 | ret = max77693_set_top_off_threshold_current(chg, | |
608 | DEFAULT_TOP_OFF_THRESHOLD_CURRENT); | |
609 | if (ret) | |
610 | return ret; | |
611 | ||
612 | ret = max77693_set_top_off_timer(chg, DEFAULT_TOP_OFF_TIMER); | |
613 | if (ret) | |
614 | return ret; | |
615 | ||
616 | ret = max77693_set_constant_volt(chg, chg->constant_volt); | |
617 | if (ret) | |
618 | return ret; | |
619 | ||
620 | ret = max77693_set_min_system_volt(chg, chg->min_system_volt); | |
621 | if (ret) | |
622 | return ret; | |
623 | ||
624 | ret = max77693_set_thermal_regulation_temp(chg, | |
625 | chg->thermal_regulation_temp); | |
626 | if (ret) | |
627 | return ret; | |
628 | ||
629 | ret = max77693_set_batttery_overcurrent(chg, chg->batttery_overcurrent); | |
630 | if (ret) | |
631 | return ret; | |
632 | ||
5b40b3e6 | 633 | return max77693_set_charge_input_threshold_volt(chg, |
87c2d906 | 634 | chg->charge_input_threshold_volt); |
87c2d906 KK |
635 | } |
636 | ||
637 | #ifdef CONFIG_OF | |
638 | static int max77693_dt_init(struct device *dev, struct max77693_charger *chg) | |
639 | { | |
640 | struct device_node *np = dev->of_node; | |
641 | ||
642 | if (!np) { | |
643 | dev_err(dev, "no charger OF node\n"); | |
644 | return -EINVAL; | |
645 | } | |
646 | ||
647 | if (of_property_read_u32(np, "maxim,constant-microvolt", | |
648 | &chg->constant_volt)) | |
649 | chg->constant_volt = DEFAULT_CONSTANT_VOLT; | |
650 | ||
651 | if (of_property_read_u32(np, "maxim,min-system-microvolt", | |
652 | &chg->min_system_volt)) | |
653 | chg->min_system_volt = DEFAULT_MIN_SYSTEM_VOLT; | |
654 | ||
655 | if (of_property_read_u32(np, "maxim,thermal-regulation-celsius", | |
656 | &chg->thermal_regulation_temp)) | |
657 | chg->thermal_regulation_temp = DEFAULT_THERMAL_REGULATION_TEMP; | |
658 | ||
659 | if (of_property_read_u32(np, "maxim,battery-overcurrent-microamp", | |
660 | &chg->batttery_overcurrent)) | |
661 | chg->batttery_overcurrent = DEFAULT_BATTERY_OVERCURRENT; | |
662 | ||
663 | if (of_property_read_u32(np, "maxim,charge-input-threshold-microvolt", | |
664 | &chg->charge_input_threshold_volt)) | |
665 | chg->charge_input_threshold_volt = | |
666 | DEFAULT_CHARGER_INPUT_THRESHOLD_VOLT; | |
667 | ||
668 | return 0; | |
669 | } | |
670 | #else /* CONFIG_OF */ | |
671 | static int max77693_dt_init(struct device *dev, struct max77693_charger *chg) | |
672 | { | |
673 | return 0; | |
674 | } | |
675 | #endif /* CONFIG_OF */ | |
676 | ||
677 | static int max77693_charger_probe(struct platform_device *pdev) | |
678 | { | |
679 | struct max77693_charger *chg; | |
297d716f | 680 | struct power_supply_config psy_cfg = {}; |
87c2d906 KK |
681 | struct max77693_dev *max77693 = dev_get_drvdata(pdev->dev.parent); |
682 | int ret; | |
683 | ||
684 | chg = devm_kzalloc(&pdev->dev, sizeof(*chg), GFP_KERNEL); | |
685 | if (!chg) | |
686 | return -ENOMEM; | |
687 | ||
688 | platform_set_drvdata(pdev, chg); | |
689 | chg->dev = &pdev->dev; | |
690 | chg->max77693 = max77693; | |
691 | ||
692 | ret = max77693_dt_init(&pdev->dev, chg); | |
693 | if (ret) | |
694 | return ret; | |
695 | ||
696 | ret = max77693_reg_init(chg); | |
697 | if (ret) | |
698 | return ret; | |
699 | ||
297d716f | 700 | psy_cfg.drv_data = chg; |
87c2d906 KK |
701 | |
702 | ret = device_create_file(&pdev->dev, &dev_attr_fast_charge_timer); | |
703 | if (ret) { | |
704 | dev_err(&pdev->dev, "failed: create fast charge timer sysfs entry\n"); | |
705 | goto err; | |
706 | } | |
707 | ||
708 | ret = device_create_file(&pdev->dev, | |
709 | &dev_attr_top_off_threshold_current); | |
710 | if (ret) { | |
711 | dev_err(&pdev->dev, "failed: create top off current sysfs entry\n"); | |
712 | goto err; | |
713 | } | |
714 | ||
715 | ret = device_create_file(&pdev->dev, &dev_attr_top_off_timer); | |
716 | if (ret) { | |
717 | dev_err(&pdev->dev, "failed: create top off timer sysfs entry\n"); | |
718 | goto err; | |
719 | } | |
720 | ||
297d716f KK |
721 | chg->charger = power_supply_register(&pdev->dev, |
722 | &max77693_charger_desc, | |
723 | &psy_cfg); | |
724 | if (IS_ERR(chg->charger)) { | |
87c2d906 | 725 | dev_err(&pdev->dev, "failed: power supply register\n"); |
297d716f | 726 | ret = PTR_ERR(chg->charger); |
87c2d906 KK |
727 | goto err; |
728 | } | |
729 | ||
730 | return 0; | |
731 | ||
732 | err: | |
733 | device_remove_file(&pdev->dev, &dev_attr_top_off_timer); | |
734 | device_remove_file(&pdev->dev, &dev_attr_top_off_threshold_current); | |
735 | device_remove_file(&pdev->dev, &dev_attr_fast_charge_timer); | |
736 | ||
737 | return ret; | |
738 | } | |
739 | ||
740 | static int max77693_charger_remove(struct platform_device *pdev) | |
741 | { | |
742 | struct max77693_charger *chg = platform_get_drvdata(pdev); | |
743 | ||
744 | device_remove_file(&pdev->dev, &dev_attr_top_off_timer); | |
745 | device_remove_file(&pdev->dev, &dev_attr_top_off_threshold_current); | |
746 | device_remove_file(&pdev->dev, &dev_attr_fast_charge_timer); | |
747 | ||
297d716f | 748 | power_supply_unregister(chg->charger); |
87c2d906 KK |
749 | |
750 | return 0; | |
751 | } | |
752 | ||
753 | static const struct platform_device_id max77693_charger_id[] = { | |
754 | { "max77693-charger", 0, }, | |
755 | { } | |
756 | }; | |
757 | MODULE_DEVICE_TABLE(platform, max77693_charger_id); | |
758 | ||
759 | static struct platform_driver max77693_charger_driver = { | |
760 | .driver = { | |
87c2d906 KK |
761 | .name = "max77693-charger", |
762 | }, | |
763 | .probe = max77693_charger_probe, | |
764 | .remove = max77693_charger_remove, | |
765 | .id_table = max77693_charger_id, | |
766 | }; | |
767 | module_platform_driver(max77693_charger_driver); | |
768 | ||
1c4593ed | 769 | MODULE_AUTHOR("Krzysztof Kozlowski <krzk@kernel.org>"); |
87c2d906 KK |
770 | MODULE_DESCRIPTION("Maxim 77693 charger driver"); |
771 | MODULE_LICENSE("GPL"); |