1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // Copyright (C) 2018 Spreadtrum Communications Inc. |
3 | |
4 | #include <linux/module.h> |
5 | #include <linux/platform_device.h> |
6 | #include <linux/power_supply.h> |
7 | #include <linux/usb/phy.h> |
8 | #include <linux/regmap.h> |
9 | #include <linux/notifier.h> |
10 | #include <linux/of.h> |
11 | |
12 | /* PMIC global registers definition */ |
13 | #define SC2731_CHARGE_STATUS 0xedc |
14 | #define SC2731_CHARGE_FULL BIT(4) |
15 | #define SC2731_MODULE_EN1 0xc0c |
16 | #define SC2731_CHARGE_EN BIT(5) |
17 | |
18 | /* SC2731 switch charger registers definition */ |
19 | #define SC2731_CHG_CFG0 0x0 |
20 | #define SC2731_CHG_CFG1 0x4 |
21 | #define SC2731_CHG_CFG2 0x8 |
22 | #define SC2731_CHG_CFG3 0xc |
23 | #define SC2731_CHG_CFG4 0x10 |
24 | #define SC2731_CHG_CFG5 0x28 |
25 | |
26 | /* SC2731_CHG_CFG0 register definition */ |
27 | #define SC2731_PRECHG_RNG_SHIFT 11 |
28 | #define SC2731_PRECHG_RNG_MASK GENMASK(12, 11) |
29 | |
30 | #define SC2731_TERMINATION_VOL_MASK GENMASK(2, 1) |
31 | #define SC2731_TERMINATION_VOL_SHIFT 1 |
32 | #define SC2731_TERMINATION_VOL_CAL_MASK GENMASK(8, 3) |
33 | #define SC2731_TERMINATION_VOL_CAL_SHIFT 3 |
34 | #define SC2731_TERMINATION_CUR_MASK GENMASK(2, 0) |
35 | |
36 | #define SC2731_CC_EN BIT(13) |
37 | #define SC2731_CHARGER_PD BIT(0) |
38 | |
39 | /* SC2731_CHG_CFG1 register definition */ |
40 | #define SC2731_CUR_MASK GENMASK(5, 0) |
41 | |
42 | /* SC2731_CHG_CFG5 register definition */ |
43 | #define SC2731_CUR_LIMIT_SHIFT 8 |
44 | #define SC2731_CUR_LIMIT_MASK GENMASK(9, 8) |
45 | |
46 | /* Default current definition (unit is mA) */ |
47 | #define SC2731_CURRENT_LIMIT_100 100 |
48 | #define SC2731_CURRENT_LIMIT_500 500 |
49 | #define SC2731_CURRENT_LIMIT_900 900 |
50 | #define SC2731_CURRENT_LIMIT_2000 2000 |
51 | #define SC2731_CURRENT_PRECHG 450 |
52 | #define SC2731_CURRENT_STEP 50 |
53 | |
54 | struct sc2731_charger_info { |
55 | struct device *dev; |
56 | struct regmap *regmap; |
57 | struct usb_phy *usb_phy; |
58 | struct notifier_block usb_notify; |
59 | struct power_supply *psy_usb; |
60 | struct work_struct work; |
61 | struct mutex lock; |
62 | bool charging; |
63 | u32 base; |
64 | u32 limit; |
65 | }; |
66 | |
67 | static void sc2731_charger_stop_charge(struct sc2731_charger_info *info) |
68 | { |
69 | regmap_update_bits(map: info->regmap, reg: info->base + SC2731_CHG_CFG0, |
70 | SC2731_CC_EN, val: 0); |
71 | |
72 | regmap_update_bits(map: info->regmap, reg: info->base + SC2731_CHG_CFG0, |
73 | SC2731_CHARGER_PD, SC2731_CHARGER_PD); |
74 | } |
75 | |
76 | static int sc2731_charger_start_charge(struct sc2731_charger_info *info) |
77 | { |
78 | int ret; |
79 | |
80 | /* Enable charger constant current mode */ |
81 | ret = regmap_update_bits(map: info->regmap, reg: info->base + SC2731_CHG_CFG0, |
82 | SC2731_CC_EN, SC2731_CC_EN); |
83 | if (ret) |
84 | return ret; |
85 | |
86 | /* Start charging */ |
87 | return regmap_update_bits(map: info->regmap, reg: info->base + SC2731_CHG_CFG0, |
88 | SC2731_CHARGER_PD, val: 0); |
89 | } |
90 | |
91 | static int sc2731_charger_set_current_limit(struct sc2731_charger_info *info, |
92 | u32 limit) |
93 | { |
94 | u32 val; |
95 | |
96 | if (limit <= SC2731_CURRENT_LIMIT_100) |
97 | val = 0; |
98 | else if (limit <= SC2731_CURRENT_LIMIT_500) |
99 | val = 3; |
100 | else if (limit <= SC2731_CURRENT_LIMIT_900) |
101 | val = 2; |
102 | else |
103 | val = 1; |
104 | |
105 | return regmap_update_bits(map: info->regmap, reg: info->base + SC2731_CHG_CFG5, |
106 | SC2731_CUR_LIMIT_MASK, |
107 | val: val << SC2731_CUR_LIMIT_SHIFT); |
108 | } |
109 | |
110 | static int sc2731_charger_set_current(struct sc2731_charger_info *info, u32 cur) |
111 | { |
112 | u32 val; |
113 | int ret; |
114 | |
115 | if (cur > SC2731_CURRENT_LIMIT_2000) |
116 | cur = SC2731_CURRENT_LIMIT_2000; |
117 | else if (cur < SC2731_CURRENT_PRECHG) |
118 | cur = SC2731_CURRENT_PRECHG; |
119 | |
120 | /* Calculate the step value, each step is 50 mA */ |
121 | val = (cur - SC2731_CURRENT_PRECHG) / SC2731_CURRENT_STEP; |
122 | |
123 | /* Set pre-charge current as 450 mA */ |
124 | ret = regmap_update_bits(map: info->regmap, reg: info->base + SC2731_CHG_CFG0, |
125 | SC2731_PRECHG_RNG_MASK, |
126 | val: 0x3 << SC2731_PRECHG_RNG_SHIFT); |
127 | if (ret) |
128 | return ret; |
129 | |
130 | return regmap_update_bits(map: info->regmap, reg: info->base + SC2731_CHG_CFG1, |
131 | SC2731_CUR_MASK, val); |
132 | } |
133 | |
134 | static int sc2731_charger_get_status(struct sc2731_charger_info *info) |
135 | { |
136 | u32 val; |
137 | int ret; |
138 | |
139 | ret = regmap_read(map: info->regmap, SC2731_CHARGE_STATUS, val: &val); |
140 | if (ret) |
141 | return ret; |
142 | |
143 | if (val & SC2731_CHARGE_FULL) |
144 | return POWER_SUPPLY_STATUS_FULL; |
145 | |
146 | return POWER_SUPPLY_STATUS_CHARGING; |
147 | } |
148 | |
149 | static int sc2731_charger_get_current(struct sc2731_charger_info *info, |
150 | u32 *cur) |
151 | { |
152 | int ret; |
153 | u32 val; |
154 | |
155 | ret = regmap_read(map: info->regmap, reg: info->base + SC2731_CHG_CFG1, val: &val); |
156 | if (ret) |
157 | return ret; |
158 | |
159 | val &= SC2731_CUR_MASK; |
160 | *cur = val * SC2731_CURRENT_STEP + SC2731_CURRENT_PRECHG; |
161 | |
162 | return 0; |
163 | } |
164 | |
165 | static int sc2731_charger_get_current_limit(struct sc2731_charger_info *info, |
166 | u32 *cur) |
167 | { |
168 | int ret; |
169 | u32 val; |
170 | |
171 | ret = regmap_read(map: info->regmap, reg: info->base + SC2731_CHG_CFG5, val: &val); |
172 | if (ret) |
173 | return ret; |
174 | |
175 | val = (val & SC2731_CUR_LIMIT_MASK) >> SC2731_CUR_LIMIT_SHIFT; |
176 | |
177 | switch (val) { |
178 | case 0: |
179 | *cur = SC2731_CURRENT_LIMIT_100; |
180 | break; |
181 | |
182 | case 1: |
183 | *cur = SC2731_CURRENT_LIMIT_2000; |
184 | break; |
185 | |
186 | case 2: |
187 | *cur = SC2731_CURRENT_LIMIT_900; |
188 | break; |
189 | |
190 | case 3: |
191 | *cur = SC2731_CURRENT_LIMIT_500; |
192 | break; |
193 | |
194 | default: |
195 | return -EINVAL; |
196 | } |
197 | |
198 | return 0; |
199 | } |
200 | |
201 | static int |
202 | sc2731_charger_usb_set_property(struct power_supply *psy, |
203 | enum power_supply_property psp, |
204 | const union power_supply_propval *val) |
205 | { |
206 | struct sc2731_charger_info *info = power_supply_get_drvdata(psy); |
207 | int ret; |
208 | |
209 | mutex_lock(&info->lock); |
210 | |
211 | if (!info->charging) { |
212 | mutex_unlock(lock: &info->lock); |
213 | return -ENODEV; |
214 | } |
215 | |
216 | switch (psp) { |
217 | case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: |
218 | ret = sc2731_charger_set_current(info, cur: val->intval / 1000); |
219 | if (ret < 0) |
220 | dev_err(info->dev, "set charge current failed\n" ); |
221 | break; |
222 | |
223 | case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: |
224 | ret = sc2731_charger_set_current_limit(info, |
225 | limit: val->intval / 1000); |
226 | if (ret < 0) |
227 | dev_err(info->dev, "set input current limit failed\n" ); |
228 | break; |
229 | |
230 | default: |
231 | ret = -EINVAL; |
232 | } |
233 | |
234 | mutex_unlock(lock: &info->lock); |
235 | return ret; |
236 | } |
237 | |
238 | static int sc2731_charger_usb_get_property(struct power_supply *psy, |
239 | enum power_supply_property psp, |
240 | union power_supply_propval *val) |
241 | { |
242 | struct sc2731_charger_info *info = power_supply_get_drvdata(psy); |
243 | int ret = 0; |
244 | u32 cur; |
245 | |
246 | mutex_lock(&info->lock); |
247 | |
248 | switch (psp) { |
249 | case POWER_SUPPLY_PROP_STATUS: |
250 | if (info->charging) |
251 | val->intval = sc2731_charger_get_status(info); |
252 | else |
253 | val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; |
254 | break; |
255 | |
256 | case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: |
257 | if (!info->charging) { |
258 | val->intval = 0; |
259 | } else { |
260 | ret = sc2731_charger_get_current(info, cur: &cur); |
261 | if (ret) |
262 | goto out; |
263 | |
264 | val->intval = cur * 1000; |
265 | } |
266 | break; |
267 | |
268 | case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: |
269 | if (!info->charging) { |
270 | val->intval = 0; |
271 | } else { |
272 | ret = sc2731_charger_get_current_limit(info, cur: &cur); |
273 | if (ret) |
274 | goto out; |
275 | |
276 | val->intval = cur * 1000; |
277 | } |
278 | break; |
279 | |
280 | default: |
281 | ret = -EINVAL; |
282 | } |
283 | |
284 | out: |
285 | mutex_unlock(lock: &info->lock); |
286 | return ret; |
287 | } |
288 | |
289 | static int sc2731_charger_property_is_writeable(struct power_supply *psy, |
290 | enum power_supply_property psp) |
291 | { |
292 | int ret; |
293 | |
294 | switch (psp) { |
295 | case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: |
296 | case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: |
297 | ret = 1; |
298 | break; |
299 | |
300 | default: |
301 | ret = 0; |
302 | } |
303 | |
304 | return ret; |
305 | } |
306 | |
307 | static enum power_supply_property sc2731_usb_props[] = { |
308 | POWER_SUPPLY_PROP_STATUS, |
309 | POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, |
310 | POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, |
311 | }; |
312 | |
313 | static const struct power_supply_desc sc2731_charger_desc = { |
314 | .name = "sc2731_charger" , |
315 | .type = POWER_SUPPLY_TYPE_USB, |
316 | .properties = sc2731_usb_props, |
317 | .num_properties = ARRAY_SIZE(sc2731_usb_props), |
318 | .get_property = sc2731_charger_usb_get_property, |
319 | .set_property = sc2731_charger_usb_set_property, |
320 | .property_is_writeable = sc2731_charger_property_is_writeable, |
321 | }; |
322 | |
323 | static void sc2731_charger_work(struct work_struct *data) |
324 | { |
325 | struct sc2731_charger_info *info = |
326 | container_of(data, struct sc2731_charger_info, work); |
327 | int ret; |
328 | |
329 | mutex_lock(&info->lock); |
330 | |
331 | if (info->limit > 0 && !info->charging) { |
332 | /* set current limitation and start to charge */ |
333 | ret = sc2731_charger_set_current_limit(info, limit: info->limit); |
334 | if (ret) |
335 | goto out; |
336 | |
337 | ret = sc2731_charger_set_current(info, cur: info->limit); |
338 | if (ret) |
339 | goto out; |
340 | |
341 | ret = sc2731_charger_start_charge(info); |
342 | if (ret) |
343 | goto out; |
344 | |
345 | info->charging = true; |
346 | } else if (!info->limit && info->charging) { |
347 | /* Stop charging */ |
348 | info->charging = false; |
349 | sc2731_charger_stop_charge(info); |
350 | } |
351 | |
352 | out: |
353 | mutex_unlock(lock: &info->lock); |
354 | } |
355 | |
356 | static int sc2731_charger_usb_change(struct notifier_block *nb, |
357 | unsigned long limit, void *data) |
358 | { |
359 | struct sc2731_charger_info *info = |
360 | container_of(nb, struct sc2731_charger_info, usb_notify); |
361 | |
362 | info->limit = limit; |
363 | |
364 | schedule_work(work: &info->work); |
365 | |
366 | return NOTIFY_OK; |
367 | } |
368 | |
369 | static int sc2731_charger_hw_init(struct sc2731_charger_info *info) |
370 | { |
371 | struct power_supply_battery_info *bat_info; |
372 | u32 term_currrent, term_voltage, cur_val, vol_val; |
373 | int ret; |
374 | |
375 | /* Enable charger module */ |
376 | ret = regmap_update_bits(map: info->regmap, SC2731_MODULE_EN1, |
377 | SC2731_CHARGE_EN, SC2731_CHARGE_EN); |
378 | if (ret) |
379 | return ret; |
380 | |
381 | ret = power_supply_get_battery_info(psy: info->psy_usb, info_out: &bat_info); |
382 | if (ret) { |
383 | dev_warn(info->dev, "no battery information is supplied\n" ); |
384 | |
385 | /* |
386 | * If no battery information is supplied, we should set |
387 | * default charge termination current to 120 mA, and default |
388 | * charge termination voltage to 4.35V. |
389 | */ |
390 | cur_val = 0x2; |
391 | vol_val = 0x1; |
392 | } else { |
393 | term_currrent = bat_info->charge_term_current_ua / 1000; |
394 | |
395 | if (term_currrent <= 90) |
396 | cur_val = 0; |
397 | else if (term_currrent >= 265) |
398 | cur_val = 0x7; |
399 | else |
400 | cur_val = ((term_currrent - 90) / 25) + 1; |
401 | |
402 | term_voltage = bat_info->constant_charge_voltage_max_uv / 1000; |
403 | |
404 | if (term_voltage > 4500) |
405 | term_voltage = 4500; |
406 | |
407 | if (term_voltage > 4200) |
408 | vol_val = (term_voltage - 4200) / 100; |
409 | else |
410 | vol_val = 0; |
411 | |
412 | power_supply_put_battery_info(psy: info->psy_usb, info: bat_info); |
413 | } |
414 | |
415 | /* Set charge termination current */ |
416 | ret = regmap_update_bits(map: info->regmap, reg: info->base + SC2731_CHG_CFG2, |
417 | SC2731_TERMINATION_CUR_MASK, val: cur_val); |
418 | if (ret) |
419 | goto error; |
420 | |
421 | /* Set charge termination voltage */ |
422 | ret = regmap_update_bits(map: info->regmap, reg: info->base + SC2731_CHG_CFG0, |
423 | SC2731_TERMINATION_VOL_MASK | |
424 | SC2731_TERMINATION_VOL_CAL_MASK, |
425 | val: (vol_val << SC2731_TERMINATION_VOL_SHIFT) | |
426 | (0x6 << SC2731_TERMINATION_VOL_CAL_SHIFT)); |
427 | if (ret) |
428 | goto error; |
429 | |
430 | return 0; |
431 | |
432 | error: |
433 | regmap_update_bits(map: info->regmap, SC2731_MODULE_EN1, SC2731_CHARGE_EN, val: 0); |
434 | return ret; |
435 | } |
436 | |
437 | static void sc2731_charger_detect_status(struct sc2731_charger_info *info) |
438 | { |
439 | unsigned int min, max; |
440 | |
441 | /* |
442 | * If the USB charger status has been USB_CHARGER_PRESENT before |
443 | * registering the notifier, we should start to charge with getting |
444 | * the charge current. |
445 | */ |
446 | if (info->usb_phy->chg_state != USB_CHARGER_PRESENT) |
447 | return; |
448 | |
449 | usb_phy_get_charger_current(usb_phy: info->usb_phy, min: &min, max: &max); |
450 | info->limit = min; |
451 | |
452 | schedule_work(work: &info->work); |
453 | } |
454 | |
455 | static int sc2731_charger_probe(struct platform_device *pdev) |
456 | { |
457 | struct device_node *np = pdev->dev.of_node; |
458 | struct sc2731_charger_info *info; |
459 | struct power_supply_config charger_cfg = { }; |
460 | int ret; |
461 | |
462 | info = devm_kzalloc(dev: &pdev->dev, size: sizeof(*info), GFP_KERNEL); |
463 | if (!info) |
464 | return -ENOMEM; |
465 | |
466 | mutex_init(&info->lock); |
467 | info->dev = &pdev->dev; |
468 | INIT_WORK(&info->work, sc2731_charger_work); |
469 | |
470 | info->regmap = dev_get_regmap(dev: pdev->dev.parent, NULL); |
471 | if (!info->regmap) { |
472 | dev_err(&pdev->dev, "failed to get charger regmap\n" ); |
473 | return -ENODEV; |
474 | } |
475 | |
476 | ret = of_property_read_u32(np, propname: "reg" , out_value: &info->base); |
477 | if (ret) { |
478 | dev_err(&pdev->dev, "failed to get register address\n" ); |
479 | return -ENODEV; |
480 | } |
481 | |
482 | charger_cfg.drv_data = info; |
483 | charger_cfg.of_node = np; |
484 | info->psy_usb = devm_power_supply_register(parent: &pdev->dev, |
485 | desc: &sc2731_charger_desc, |
486 | cfg: &charger_cfg); |
487 | if (IS_ERR(ptr: info->psy_usb)) { |
488 | dev_err(&pdev->dev, "failed to register power supply\n" ); |
489 | return PTR_ERR(ptr: info->psy_usb); |
490 | } |
491 | |
492 | ret = sc2731_charger_hw_init(info); |
493 | if (ret) |
494 | return ret; |
495 | |
496 | info->usb_phy = devm_usb_get_phy_by_phandle(dev: &pdev->dev, phandle: "phys" , index: 0); |
497 | if (IS_ERR(ptr: info->usb_phy)) { |
498 | dev_err(&pdev->dev, "failed to find USB phy\n" ); |
499 | return PTR_ERR(ptr: info->usb_phy); |
500 | } |
501 | |
502 | info->usb_notify.notifier_call = sc2731_charger_usb_change; |
503 | ret = usb_register_notifier(x: info->usb_phy, nb: &info->usb_notify); |
504 | if (ret) { |
505 | dev_err(&pdev->dev, "failed to register notifier: %d\n" , ret); |
506 | return ret; |
507 | } |
508 | |
509 | sc2731_charger_detect_status(info); |
510 | |
511 | return 0; |
512 | } |
513 | |
514 | static void sc2731_charger_remove(struct platform_device *pdev) |
515 | { |
516 | struct sc2731_charger_info *info = platform_get_drvdata(pdev); |
517 | |
518 | usb_unregister_notifier(x: info->usb_phy, nb: &info->usb_notify); |
519 | } |
520 | |
521 | static const struct of_device_id sc2731_charger_of_match[] = { |
522 | { .compatible = "sprd,sc2731-charger" , }, |
523 | { } |
524 | }; |
525 | MODULE_DEVICE_TABLE(of, sc2731_charger_of_match); |
526 | |
527 | static struct platform_driver sc2731_charger_driver = { |
528 | .driver = { |
529 | .name = "sc2731-charger" , |
530 | .of_match_table = sc2731_charger_of_match, |
531 | }, |
532 | .probe = sc2731_charger_probe, |
533 | .remove_new = sc2731_charger_remove, |
534 | }; |
535 | |
536 | module_platform_driver(sc2731_charger_driver); |
537 | |
538 | MODULE_DESCRIPTION("Spreadtrum SC2731 Charger Driver" ); |
539 | MODULE_LICENSE("GPL v2" ); |
540 | |