1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69#include <linux/kernel.h>
70#include <linux/interrupt.h>
71#include <linux/mfd/rohm-bd70528.h>
72#include <linux/module.h>
73#include <linux/platform_device.h>
74#include <linux/power_supply.h>
75#include <linux/linear_range.h>
76
77#define CHG_STAT_SUSPEND 0x0
78#define CHG_STAT_TRICKLE 0x1
79#define CHG_STAT_FAST 0x3
80#define CHG_STAT_TOPOFF 0xe
81#define CHG_STAT_DONE 0xf
82#define CHG_STAT_OTP_TRICKLE 0x10
83#define CHG_STAT_OTP_FAST 0x11
84#define CHG_STAT_OTP_DONE 0x12
85#define CHG_STAT_TSD_TRICKLE 0x20
86#define CHG_STAT_TSD_FAST 0x21
87#define CHG_STAT_TSD_TOPOFF 0x22
88#define CHG_STAT_BAT_ERR 0x7f
89
90static const char *bd70528_charger_model = "BD70528";
91static const char *bd70528_charger_manufacturer = "ROHM Semiconductors";
92
93#define BD_ERR_IRQ_HND(_name_, _wrn_) \
94static irqreturn_t bd0528_##_name_##_interrupt(int irq, void *arg) \
95{ \
96 struct power_supply *psy = (struct power_supply *)arg; \
97 \
98 power_supply_changed(psy); \
99 dev_err(&psy->dev, (_wrn_)); \
100 \
101 return IRQ_HANDLED; \
102}
103
104#define BD_INFO_IRQ_HND(_name_, _wrn_) \
105static irqreturn_t bd0528_##_name_##_interrupt(int irq, void *arg) \
106{ \
107 struct power_supply *psy = (struct power_supply *)arg; \
108 \
109 power_supply_changed(psy); \
110 dev_dbg(&psy->dev, (_wrn_)); \
111 \
112 return IRQ_HANDLED; \
113}
114
115#define BD_IRQ_HND(_name_) bd0528_##_name_##_interrupt
116
117struct bd70528_psy {
118 struct regmap *regmap;
119 struct device *dev;
120 struct power_supply *psy;
121};
122
123BD_ERR_IRQ_HND(BAT_OV_DET, "Battery overvoltage detected\n");
124BD_ERR_IRQ_HND(DBAT_DET, "Dead battery detected\n");
125BD_ERR_IRQ_HND(COLD_DET, "Battery cold\n");
126BD_ERR_IRQ_HND(HOT_DET, "Battery hot\n");
127BD_ERR_IRQ_HND(CHG_TSD, "Charger thermal shutdown\n");
128BD_ERR_IRQ_HND(DCIN2_OV_DET, "DCIN2 overvoltage detected\n");
129
130BD_INFO_IRQ_HND(BAT_OV_RES, "Battery voltage back to normal\n");
131BD_INFO_IRQ_HND(COLD_RES, "Battery temperature back to normal\n");
132BD_INFO_IRQ_HND(HOT_RES, "Battery temperature back to normal\n");
133BD_INFO_IRQ_HND(BAT_RMV, "Battery removed\n");
134BD_INFO_IRQ_HND(BAT_DET, "Battery detected\n");
135BD_INFO_IRQ_HND(DCIN2_OV_RES, "DCIN2 voltage back to normal\n");
136BD_INFO_IRQ_HND(DCIN2_RMV, "DCIN2 removed\n");
137BD_INFO_IRQ_HND(DCIN2_DET, "DCIN2 detected\n");
138BD_INFO_IRQ_HND(DCIN1_RMV, "DCIN1 removed\n");
139BD_INFO_IRQ_HND(DCIN1_DET, "DCIN1 detected\n");
140
141struct irq_name_pair {
142 const char *n;
143 irqreturn_t (*h)(int irq, void *arg);
144};
145
146static int bd70528_get_irqs(struct platform_device *pdev,
147 struct bd70528_psy *bdpsy)
148{
149 int irq, i, ret;
150 unsigned int mask;
151 static const struct irq_name_pair bd70528_chg_irqs[] = {
152 { .n = "bd70528-bat-ov-res", .h = BD_IRQ_HND(BAT_OV_RES) },
153 { .n = "bd70528-bat-ov-det", .h = BD_IRQ_HND(BAT_OV_DET) },
154 { .n = "bd70528-bat-dead", .h = BD_IRQ_HND(DBAT_DET) },
155 { .n = "bd70528-bat-warmed", .h = BD_IRQ_HND(COLD_RES) },
156 { .n = "bd70528-bat-cold", .h = BD_IRQ_HND(COLD_DET) },
157 { .n = "bd70528-bat-cooled", .h = BD_IRQ_HND(HOT_RES) },
158 { .n = "bd70528-bat-hot", .h = BD_IRQ_HND(HOT_DET) },
159 { .n = "bd70528-chg-tshd", .h = BD_IRQ_HND(CHG_TSD) },
160 { .n = "bd70528-bat-removed", .h = BD_IRQ_HND(BAT_RMV) },
161 { .n = "bd70528-bat-detected", .h = BD_IRQ_HND(BAT_DET) },
162 { .n = "bd70528-dcin2-ov-res", .h = BD_IRQ_HND(DCIN2_OV_RES) },
163 { .n = "bd70528-dcin2-ov-det", .h = BD_IRQ_HND(DCIN2_OV_DET) },
164 { .n = "bd70528-dcin2-removed", .h = BD_IRQ_HND(DCIN2_RMV) },
165 { .n = "bd70528-dcin2-detected", .h = BD_IRQ_HND(DCIN2_DET) },
166 { .n = "bd70528-dcin1-removed", .h = BD_IRQ_HND(DCIN1_RMV) },
167 { .n = "bd70528-dcin1-detected", .h = BD_IRQ_HND(DCIN1_DET) },
168 };
169
170 for (i = 0; i < ARRAY_SIZE(bd70528_chg_irqs); i++) {
171 irq = platform_get_irq_byname(pdev, bd70528_chg_irqs[i].n);
172 if (irq < 0) {
173 dev_err(&pdev->dev, "Bad IRQ information for %s (%d)\n",
174 bd70528_chg_irqs[i].n, irq);
175 return irq;
176 }
177 ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
178 bd70528_chg_irqs[i].h,
179 IRQF_ONESHOT,
180 bd70528_chg_irqs[i].n,
181 bdpsy->psy);
182
183 if (ret)
184 return ret;
185 }
186
187
188
189
190
191
192 mask = BD70528_REG_INT_BAT1_MASK | BD70528_REG_INT_BAT2_MASK;
193 ret = regmap_update_bits(bdpsy->regmap,
194 BD70528_REG_INT_MAIN_MASK, mask, 0);
195 if (ret)
196 dev_err(&pdev->dev, "Failed to enable charger IRQs\n");
197
198 return ret;
199}
200
201static int bd70528_get_charger_status(struct bd70528_psy *bdpsy, int *val)
202{
203 int ret;
204 unsigned int v;
205
206 ret = regmap_read(bdpsy->regmap, BD70528_REG_CHG_CURR_STAT, &v);
207 if (ret) {
208 dev_err(bdpsy->dev, "Charger state read failure %d\n",
209 ret);
210 return ret;
211 }
212
213 switch (v & BD70528_MASK_CHG_STAT) {
214 case CHG_STAT_SUSPEND:
215
216 case CHG_STAT_OTP_TRICKLE:
217 case CHG_STAT_OTP_FAST:
218 case CHG_STAT_OTP_DONE:
219 case CHG_STAT_TSD_TRICKLE:
220 case CHG_STAT_TSD_FAST:
221 case CHG_STAT_TSD_TOPOFF:
222 case CHG_STAT_BAT_ERR:
223 *val = POWER_SUPPLY_STATUS_NOT_CHARGING;
224 break;
225 case CHG_STAT_DONE:
226 *val = POWER_SUPPLY_STATUS_FULL;
227 break;
228 case CHG_STAT_TRICKLE:
229 case CHG_STAT_FAST:
230 case CHG_STAT_TOPOFF:
231 *val = POWER_SUPPLY_STATUS_CHARGING;
232 break;
233 default:
234 *val = POWER_SUPPLY_STATUS_UNKNOWN;
235 break;
236 }
237
238 return 0;
239}
240
241static int bd70528_get_charge_type(struct bd70528_psy *bdpsy, int *val)
242{
243 int ret;
244 unsigned int v;
245
246 ret = regmap_read(bdpsy->regmap, BD70528_REG_CHG_CURR_STAT, &v);
247 if (ret) {
248 dev_err(bdpsy->dev, "Charger state read failure %d\n",
249 ret);
250 return ret;
251 }
252
253 switch (v & BD70528_MASK_CHG_STAT) {
254 case CHG_STAT_TRICKLE:
255 *val = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
256 break;
257 case CHG_STAT_FAST:
258 case CHG_STAT_TOPOFF:
259 *val = POWER_SUPPLY_CHARGE_TYPE_FAST;
260 break;
261 case CHG_STAT_DONE:
262 case CHG_STAT_SUSPEND:
263
264 case CHG_STAT_OTP_TRICKLE:
265 case CHG_STAT_OTP_FAST:
266 case CHG_STAT_OTP_DONE:
267 case CHG_STAT_TSD_TRICKLE:
268 case CHG_STAT_TSD_FAST:
269 case CHG_STAT_TSD_TOPOFF:
270 case CHG_STAT_BAT_ERR:
271 *val = POWER_SUPPLY_CHARGE_TYPE_NONE;
272 break;
273 default:
274 *val = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
275 break;
276 }
277
278 return 0;
279}
280
281static int bd70528_get_battery_health(struct bd70528_psy *bdpsy, int *val)
282{
283 int ret;
284 unsigned int v;
285
286 ret = regmap_read(bdpsy->regmap, BD70528_REG_CHG_BAT_STAT, &v);
287 if (ret) {
288 dev_err(bdpsy->dev, "Battery state read failure %d\n",
289 ret);
290 return ret;
291 }
292
293 if (!(v & BD70528_MASK_CHG_BAT_DETECT))
294 *val = POWER_SUPPLY_HEALTH_DEAD;
295 else if (v & BD70528_MASK_CHG_BAT_OVERVOLT)
296 *val = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
297 else if (v & BD70528_MASK_CHG_BAT_TIMER)
298 *val = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
299 else
300 *val = POWER_SUPPLY_HEALTH_GOOD;
301
302 return 0;
303}
304
305static int bd70528_get_online(struct bd70528_psy *bdpsy, int *val)
306{
307 int ret;
308 unsigned int v;
309
310 ret = regmap_read(bdpsy->regmap, BD70528_REG_CHG_IN_STAT, &v);
311 if (ret) {
312 dev_err(bdpsy->dev, "DC1 IN state read failure %d\n",
313 ret);
314 return ret;
315 }
316
317 *val = (v & BD70528_MASK_CHG_DCIN1_UVLO) ? 1 : 0;
318
319 return 0;
320}
321
322static int bd70528_get_present(struct bd70528_psy *bdpsy, int *val)
323{
324 int ret;
325 unsigned int v;
326
327 ret = regmap_read(bdpsy->regmap, BD70528_REG_CHG_BAT_STAT, &v);
328 if (ret) {
329 dev_err(bdpsy->dev, "Battery state read failure %d\n",
330 ret);
331 return ret;
332 }
333
334 *val = (v & BD70528_MASK_CHG_BAT_DETECT) ? 1 : 0;
335
336 return 0;
337}
338
339static const struct linear_range current_limit_ranges[] = {
340 {
341 .min = 5,
342 .step = 1,
343 .min_sel = 0,
344 .max_sel = 0x22,
345 },
346 {
347 .min = 40,
348 .step = 5,
349 .min_sel = 0x23,
350 .max_sel = 0x26,
351 },
352 {
353 .min = 60,
354 .step = 20,
355 .min_sel = 0x27,
356 .max_sel = 0x2d,
357 },
358 {
359 .min = 200,
360 .step = 50,
361 .min_sel = 0x2e,
362 .max_sel = 0x34,
363 },
364 {
365 .min = 500,
366 .step = 0,
367 .min_sel = 0x35,
368 .max_sel = 0x3f,
369 },
370};
371
372
373
374
375
376
377static const struct linear_range warm_charge_curr[] = {
378 {
379 .min = 10,
380 .step = 10,
381 .min_sel = 0,
382 .max_sel = 0x12,
383 },
384 {
385 .min = 200,
386 .step = 25,
387 .min_sel = 0x13,
388 .max_sel = 0x1f,
389 },
390};
391
392
393
394
395
396
397#define MAX_COLD_CHG_CURR_SEL 0x15
398#define MAX_WARM_CHG_CURR_SEL 0x1f
399#define MIN_CHG_CURR_SEL 0x0
400
401static int get_charge_current(struct bd70528_psy *bdpsy, int *ma)
402{
403 unsigned int sel;
404 int ret;
405
406 ret = regmap_read(bdpsy->regmap, BD70528_REG_CHG_CHG_CURR_WARM,
407 &sel);
408 if (ret) {
409 dev_err(bdpsy->dev,
410 "Charge current reading failed (%d)\n", ret);
411 return ret;
412 }
413
414 sel &= BD70528_MASK_CHG_CHG_CURR;
415
416 ret = linear_range_get_value_array(&warm_charge_curr[0],
417 ARRAY_SIZE(warm_charge_curr),
418 sel, ma);
419 if (ret) {
420 dev_err(bdpsy->dev,
421 "Unknown charge current value 0x%x\n",
422 sel);
423 }
424
425 return ret;
426}
427
428static int get_current_limit(struct bd70528_psy *bdpsy, int *ma)
429{
430 unsigned int sel;
431 int ret;
432
433 ret = regmap_read(bdpsy->regmap, BD70528_REG_CHG_DCIN_ILIM,
434 &sel);
435
436 if (ret) {
437 dev_err(bdpsy->dev,
438 "Input current limit reading failed (%d)\n", ret);
439 return ret;
440 }
441
442 sel &= BD70528_MASK_CHG_DCIN_ILIM;
443
444 ret = linear_range_get_value_array(¤t_limit_ranges[0],
445 ARRAY_SIZE(current_limit_ranges),
446 sel, ma);
447 if (ret) {
448
449 *ma = 500;
450 }
451 return 0;
452}
453
454static enum power_supply_property bd70528_charger_props[] = {
455 POWER_SUPPLY_PROP_STATUS,
456 POWER_SUPPLY_PROP_CHARGE_TYPE,
457 POWER_SUPPLY_PROP_HEALTH,
458 POWER_SUPPLY_PROP_PRESENT,
459 POWER_SUPPLY_PROP_ONLINE,
460 POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
461 POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
462 POWER_SUPPLY_PROP_MODEL_NAME,
463 POWER_SUPPLY_PROP_MANUFACTURER,
464};
465
466static int bd70528_charger_get_property(struct power_supply *psy,
467 enum power_supply_property psp,
468 union power_supply_propval *val)
469{
470 struct bd70528_psy *bdpsy = power_supply_get_drvdata(psy);
471 int ret = 0;
472
473 switch (psp) {
474 case POWER_SUPPLY_PROP_STATUS:
475 return bd70528_get_charger_status(bdpsy, &val->intval);
476 case POWER_SUPPLY_PROP_CHARGE_TYPE:
477 return bd70528_get_charge_type(bdpsy, &val->intval);
478 case POWER_SUPPLY_PROP_HEALTH:
479 return bd70528_get_battery_health(bdpsy, &val->intval);
480 case POWER_SUPPLY_PROP_PRESENT:
481 return bd70528_get_present(bdpsy, &val->intval);
482 case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
483 ret = get_current_limit(bdpsy, &val->intval);
484 val->intval *= 1000;
485 return ret;
486 case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
487 ret = get_charge_current(bdpsy, &val->intval);
488 val->intval *= 1000;
489 return ret;
490 case POWER_SUPPLY_PROP_ONLINE:
491 return bd70528_get_online(bdpsy, &val->intval);
492 case POWER_SUPPLY_PROP_MODEL_NAME:
493 val->strval = bd70528_charger_model;
494 return 0;
495 case POWER_SUPPLY_PROP_MANUFACTURER:
496 val->strval = bd70528_charger_manufacturer;
497 return 0;
498 default:
499 break;
500 }
501
502 return -EINVAL;
503}
504
505static int bd70528_prop_is_writable(struct power_supply *psy,
506 enum power_supply_property psp)
507{
508 switch (psp) {
509 case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
510 case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
511 return 1;
512 default:
513 break;
514 }
515 return 0;
516}
517
518static int set_charge_current(struct bd70528_psy *bdpsy, int ma)
519{
520 unsigned int reg;
521 int ret = 0, tmpret;
522 bool found;
523
524 if (ma > 500) {
525 dev_warn(bdpsy->dev,
526 "Requested charge current %u exceed maximum (500mA)\n",
527 ma);
528 reg = MAX_WARM_CHG_CURR_SEL;
529 goto set;
530 }
531 if (ma < 10) {
532 dev_err(bdpsy->dev,
533 "Requested charge current %u smaller than min (10mA)\n",
534 ma);
535 reg = MIN_CHG_CURR_SEL;
536 ret = -EINVAL;
537 goto set;
538 }
539
540
541
542
543
544
545
546
547
548 ret = linear_range_get_selector_low_array(warm_charge_curr,
549 ARRAY_SIZE(warm_charge_curr),
550 ma, ®, &found);
551 if (ret) {
552 dev_err(bdpsy->dev,
553 "Unsupported charge current %u mA\n", ma);
554 reg = MIN_CHG_CURR_SEL;
555 goto set;
556 }
557 if (!found) {
558
559
560
561
562 dev_warn(bdpsy->dev,
563 "Unsupported charge current %u mA\n", ma);
564 }
565set:
566
567 tmpret = regmap_update_bits(bdpsy->regmap,
568 BD70528_REG_CHG_CHG_CURR_WARM,
569 BD70528_MASK_CHG_CHG_CURR, reg);
570 if (tmpret)
571 dev_err(bdpsy->dev,
572 "Charge current write failure (%d)\n", tmpret);
573
574 if (reg > MAX_COLD_CHG_CURR_SEL)
575 reg = MAX_COLD_CHG_CURR_SEL;
576
577 if (!tmpret)
578 tmpret = regmap_update_bits(bdpsy->regmap,
579 BD70528_REG_CHG_CHG_CURR_COLD,
580 BD70528_MASK_CHG_CHG_CURR, reg);
581
582 if (!ret)
583 ret = tmpret;
584
585 return ret;
586}
587
588#define MAX_CURR_LIMIT_SEL 0x34
589#define MIN_CURR_LIMIT_SEL 0x0
590
591static int set_current_limit(struct bd70528_psy *bdpsy, int ma)
592{
593 unsigned int reg;
594 int ret = 0, tmpret;
595 bool found;
596
597 if (ma > 500) {
598 dev_warn(bdpsy->dev,
599 "Requested current limit %u exceed maximum (500mA)\n",
600 ma);
601 reg = MAX_CURR_LIMIT_SEL;
602 goto set;
603 }
604 if (ma < 5) {
605 dev_err(bdpsy->dev,
606 "Requested current limit %u smaller than min (5mA)\n",
607 ma);
608 reg = MIN_CURR_LIMIT_SEL;
609 ret = -EINVAL;
610 goto set;
611 }
612
613 ret = linear_range_get_selector_low_array(current_limit_ranges,
614 ARRAY_SIZE(current_limit_ranges),
615 ma, ®, &found);
616 if (ret) {
617 dev_err(bdpsy->dev, "Unsupported current limit %umA\n", ma);
618 reg = MIN_CURR_LIMIT_SEL;
619 goto set;
620 }
621 if (!found) {
622
623
624
625
626
627 dev_warn(bdpsy->dev, "Unsupported current limit %umA\n", ma);
628 }
629
630set:
631 tmpret = regmap_update_bits(bdpsy->regmap,
632 BD70528_REG_CHG_DCIN_ILIM,
633 BD70528_MASK_CHG_DCIN_ILIM, reg);
634
635 if (!ret)
636 ret = tmpret;
637
638 return ret;
639}
640
641static int bd70528_charger_set_property(struct power_supply *psy,
642 enum power_supply_property psp,
643 const union power_supply_propval *val)
644{
645 struct bd70528_psy *bdpsy = power_supply_get_drvdata(psy);
646
647 switch (psp) {
648 case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
649 return set_current_limit(bdpsy, val->intval / 1000);
650 case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
651 return set_charge_current(bdpsy, val->intval / 1000);
652 default:
653 break;
654 }
655 return -EINVAL;
656}
657
658static const struct power_supply_desc bd70528_charger_desc = {
659 .name = "bd70528-charger",
660 .type = POWER_SUPPLY_TYPE_MAINS,
661 .properties = bd70528_charger_props,
662 .num_properties = ARRAY_SIZE(bd70528_charger_props),
663 .get_property = bd70528_charger_get_property,
664 .set_property = bd70528_charger_set_property,
665 .property_is_writeable = bd70528_prop_is_writable,
666};
667
668static int bd70528_power_probe(struct platform_device *pdev)
669{
670 struct bd70528_psy *bdpsy;
671 struct power_supply_config cfg = {};
672
673 bdpsy = devm_kzalloc(&pdev->dev, sizeof(*bdpsy), GFP_KERNEL);
674 if (!bdpsy)
675 return -ENOMEM;
676
677 bdpsy->regmap = dev_get_regmap(pdev->dev.parent, NULL);
678 if (!bdpsy->regmap) {
679 dev_err(&pdev->dev, "No regmap found for chip\n");
680 return -EINVAL;
681 }
682 bdpsy->dev = &pdev->dev;
683
684 platform_set_drvdata(pdev, bdpsy);
685 cfg.drv_data = bdpsy;
686 cfg.of_node = pdev->dev.parent->of_node;
687
688 bdpsy->psy = devm_power_supply_register(&pdev->dev,
689 &bd70528_charger_desc, &cfg);
690 if (IS_ERR(bdpsy->psy)) {
691 dev_err(&pdev->dev, "failed: power supply register\n");
692 return PTR_ERR(bdpsy->psy);
693 }
694
695 return bd70528_get_irqs(pdev, bdpsy);
696}
697
698static struct platform_driver bd70528_power = {
699 .driver = {
700 .name = "bd70528-power"
701 },
702 .probe = bd70528_power_probe,
703};
704
705module_platform_driver(bd70528_power);
706
707MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
708MODULE_DESCRIPTION("BD70528 power-supply driver");
709MODULE_LICENSE("GPL");
710MODULE_ALIAS("platform:bd70528-power");
711