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