blob: 5eb1e9e543e2a3cfbf50570a35c213b5c1a831e8 [file] [log] [blame]
Courtney Cavin56d7df82015-07-30 10:53:56 -07001/* Copyright (c) 2014, Sony Mobile Communications Inc.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * This driver is for the multi-block Switch-Mode Battery Charger and Boost
13 * (SMBB) hardware, found in Qualcomm PM8941 PMICs. The charger is an
14 * integrated, single-cell lithium-ion battery charger.
15 *
16 * Sub-components:
17 * - Charger core
18 * - Buck
19 * - DC charge-path
20 * - USB charge-path
21 * - Battery interface
22 * - Boost (not implemented)
23 * - Misc
24 * - HF-Buck
25 */
26
27#include <linux/errno.h>
28#include <linux/interrupt.h>
29#include <linux/kernel.h>
30#include <linux/module.h>
31#include <linux/mutex.h>
32#include <linux/of.h>
33#include <linux/platform_device.h>
34#include <linux/power_supply.h>
35#include <linux/regmap.h>
36#include <linux/slab.h>
37
38#define SMBB_CHG_VMAX 0x040
39#define SMBB_CHG_VSAFE 0x041
40#define SMBB_CHG_CFG 0x043
41#define SMBB_CHG_IMAX 0x044
42#define SMBB_CHG_ISAFE 0x045
43#define SMBB_CHG_VIN_MIN 0x047
44#define SMBB_CHG_CTRL 0x049
45#define CTRL_EN BIT(7)
46#define SMBB_CHG_VBAT_WEAK 0x052
47#define SMBB_CHG_IBAT_TERM_CHG 0x05b
48#define IBAT_TERM_CHG_IEOC BIT(7)
49#define IBAT_TERM_CHG_IEOC_BMS BIT(7)
50#define IBAT_TERM_CHG_IEOC_CHG 0
51#define SMBB_CHG_VBAT_DET 0x05d
52#define SMBB_CHG_TCHG_MAX_EN 0x060
53#define TCHG_MAX_EN BIT(7)
54#define SMBB_CHG_WDOG_TIME 0x062
55#define SMBB_CHG_WDOG_EN 0x065
56#define WDOG_EN BIT(7)
57
58#define SMBB_BUCK_REG_MODE 0x174
59#define BUCK_REG_MODE BIT(0)
60#define BUCK_REG_MODE_VBAT BIT(0)
61#define BUCK_REG_MODE_VSYS 0
62
63#define SMBB_BAT_PRES_STATUS 0x208
64#define PRES_STATUS_BAT_PRES BIT(7)
65#define SMBB_BAT_TEMP_STATUS 0x209
66#define TEMP_STATUS_OK BIT(7)
67#define TEMP_STATUS_HOT BIT(6)
68#define SMBB_BAT_BTC_CTRL 0x249
69#define BTC_CTRL_COMP_EN BIT(7)
70#define BTC_CTRL_COLD_EXT BIT(1)
71#define BTC_CTRL_HOT_EXT_N BIT(0)
72
73#define SMBB_USB_IMAX 0x344
74#define SMBB_USB_ENUM_TIMER_STOP 0x34e
75#define ENUM_TIMER_STOP BIT(0)
76#define SMBB_USB_SEC_ACCESS 0x3d0
77#define SEC_ACCESS_MAGIC 0xa5
78#define SMBB_USB_REV_BST 0x3ed
79#define REV_BST_CHG_GONE BIT(7)
80
81#define SMBB_DC_IMAX 0x444
82
83#define SMBB_MISC_REV2 0x601
84#define SMBB_MISC_BOOT_DONE 0x642
85#define BOOT_DONE BIT(7)
86
87#define STATUS_USBIN_VALID BIT(0) /* USB connection is valid */
88#define STATUS_DCIN_VALID BIT(1) /* DC connection is valid */
89#define STATUS_BAT_HOT BIT(2) /* Battery temp 1=Hot, 0=Cold */
90#define STATUS_BAT_OK BIT(3) /* Battery temp OK */
91#define STATUS_BAT_PRESENT BIT(4) /* Battery is present */
92#define STATUS_CHG_DONE BIT(5) /* Charge cycle is complete */
93#define STATUS_CHG_TRKL BIT(6) /* Trickle charging */
94#define STATUS_CHG_FAST BIT(7) /* Fast charging */
95#define STATUS_CHG_GONE BIT(8) /* No charger is connected */
96
97enum smbb_attr {
98 ATTR_BAT_ISAFE,
99 ATTR_BAT_IMAX,
100 ATTR_USBIN_IMAX,
101 ATTR_DCIN_IMAX,
102 ATTR_BAT_VSAFE,
103 ATTR_BAT_VMAX,
104 ATTR_BAT_VMIN,
105 ATTR_CHG_VDET,
106 ATTR_VIN_MIN,
107 _ATTR_CNT,
108};
109
110struct smbb_charger {
111 unsigned int revision;
112 unsigned int addr;
113 struct device *dev;
114
115 bool dc_disabled;
116 bool jeita_ext_temp;
117 unsigned long status;
118 struct mutex statlock;
119
120 unsigned int attr[_ATTR_CNT];
121
122 struct power_supply *usb_psy;
123 struct power_supply *dc_psy;
124 struct power_supply *bat_psy;
125 struct regmap *regmap;
126};
127
128static int smbb_vbat_weak_fn(unsigned int index)
129{
130 return 2100000 + index * 100000;
131}
132
133static int smbb_vin_fn(unsigned int index)
134{
135 if (index > 42)
136 return 5600000 + (index - 43) * 200000;
137 return 3400000 + index * 50000;
138}
139
140static int smbb_vmax_fn(unsigned int index)
141{
142 return 3240000 + index * 10000;
143}
144
145static int smbb_vbat_det_fn(unsigned int index)
146{
147 return 3240000 + index * 20000;
148}
149
150static int smbb_imax_fn(unsigned int index)
151{
152 if (index < 2)
153 return 100000 + index * 50000;
154 return index * 100000;
155}
156
157static int smbb_bat_imax_fn(unsigned int index)
158{
159 return index * 50000;
160}
161
162static unsigned int smbb_hw_lookup(unsigned int val, int (*fn)(unsigned int))
163{
164 unsigned int widx;
165 unsigned int sel;
166
167 for (widx = sel = 0; (*fn)(widx) <= val; ++widx)
168 sel = widx;
169
170 return sel;
171}
172
173static const struct smbb_charger_attr {
174 const char *name;
175 unsigned int reg;
176 unsigned int safe_reg;
177 unsigned int max;
178 unsigned int min;
179 unsigned int fail_ok;
180 int (*hw_fn)(unsigned int);
181} smbb_charger_attrs[] = {
182 [ATTR_BAT_ISAFE] = {
183 .name = "qcom,fast-charge-safe-current",
184 .reg = SMBB_CHG_ISAFE,
185 .max = 3000000,
186 .min = 200000,
187 .hw_fn = smbb_bat_imax_fn,
188 .fail_ok = 1,
189 },
190 [ATTR_BAT_IMAX] = {
191 .name = "qcom,fast-charge-current-limit",
192 .reg = SMBB_CHG_IMAX,
193 .safe_reg = SMBB_CHG_ISAFE,
194 .max = 3000000,
195 .min = 200000,
196 .hw_fn = smbb_bat_imax_fn,
197 },
198 [ATTR_DCIN_IMAX] = {
199 .name = "qcom,dc-current-limit",
200 .reg = SMBB_DC_IMAX,
201 .max = 2500000,
202 .min = 100000,
203 .hw_fn = smbb_imax_fn,
204 },
205 [ATTR_BAT_VSAFE] = {
206 .name = "qcom,fast-charge-safe-voltage",
207 .reg = SMBB_CHG_VSAFE,
208 .max = 5000000,
209 .min = 3240000,
210 .hw_fn = smbb_vmax_fn,
211 .fail_ok = 1,
212 },
213 [ATTR_BAT_VMAX] = {
214 .name = "qcom,fast-charge-high-threshold-voltage",
215 .reg = SMBB_CHG_VMAX,
216 .safe_reg = SMBB_CHG_VSAFE,
217 .max = 5000000,
218 .min = 3240000,
219 .hw_fn = smbb_vmax_fn,
220 },
221 [ATTR_BAT_VMIN] = {
222 .name = "qcom,fast-charge-low-threshold-voltage",
223 .reg = SMBB_CHG_VBAT_WEAK,
224 .max = 3600000,
225 .min = 2100000,
226 .hw_fn = smbb_vbat_weak_fn,
227 },
228 [ATTR_CHG_VDET] = {
229 .name = "qcom,auto-recharge-threshold-voltage",
230 .reg = SMBB_CHG_VBAT_DET,
231 .max = 5000000,
232 .min = 3240000,
233 .hw_fn = smbb_vbat_det_fn,
234 },
235 [ATTR_VIN_MIN] = {
236 .name = "qcom,minimum-input-voltage",
237 .reg = SMBB_CHG_VIN_MIN,
238 .max = 9600000,
239 .min = 4200000,
240 .hw_fn = smbb_vin_fn,
241 },
242 [ATTR_USBIN_IMAX] = {
243 .name = "usb-charge-current-limit",
244 .reg = SMBB_USB_IMAX,
245 .max = 2500000,
246 .min = 100000,
247 .hw_fn = smbb_imax_fn,
248 },
249};
250
251static int smbb_charger_attr_write(struct smbb_charger *chg,
252 enum smbb_attr which, unsigned int val)
253{
254 const struct smbb_charger_attr *prop;
255 unsigned int wval;
256 unsigned int out;
257 int rc;
258
259 prop = &smbb_charger_attrs[which];
260
261 if (val > prop->max || val < prop->min) {
262 dev_err(chg->dev, "value out of range for %s [%u:%u]\n",
263 prop->name, prop->min, prop->max);
264 return -EINVAL;
265 }
266
267 if (prop->safe_reg) {
268 rc = regmap_read(chg->regmap,
269 chg->addr + prop->safe_reg, &wval);
270 if (rc) {
271 dev_err(chg->dev,
272 "unable to read safe value for '%s'\n",
273 prop->name);
274 return rc;
275 }
276
277 wval = prop->hw_fn(wval);
278
279 if (val > wval) {
280 dev_warn(chg->dev,
281 "%s above safe value, clamping at %u\n",
282 prop->name, wval);
283 val = wval;
284 }
285 }
286
287 wval = smbb_hw_lookup(val, prop->hw_fn);
288
289 rc = regmap_write(chg->regmap, chg->addr + prop->reg, wval);
290 if (rc) {
291 dev_err(chg->dev, "unable to update %s", prop->name);
292 return rc;
293 }
294 out = prop->hw_fn(wval);
295 if (out != val) {
296 dev_warn(chg->dev,
297 "%s inaccurate, rounded to %u\n",
298 prop->name, out);
299 }
300
301 dev_dbg(chg->dev, "%s <= %d\n", prop->name, out);
302
303 chg->attr[which] = out;
304
305 return 0;
306}
307
308static int smbb_charger_attr_read(struct smbb_charger *chg,
309 enum smbb_attr which)
310{
311 const struct smbb_charger_attr *prop;
312 unsigned int val;
313 int rc;
314
315 prop = &smbb_charger_attrs[which];
316
317 rc = regmap_read(chg->regmap, chg->addr + prop->reg, &val);
318 if (rc) {
319 dev_err(chg->dev, "failed to read %s\n", prop->name);
320 return rc;
321 }
322 val = prop->hw_fn(val);
323 dev_dbg(chg->dev, "%s => %d\n", prop->name, val);
324
325 chg->attr[which] = val;
326
327 return 0;
328}
329
330static int smbb_charger_attr_parse(struct smbb_charger *chg,
331 enum smbb_attr which)
332{
333 const struct smbb_charger_attr *prop;
334 unsigned int val;
335 int rc;
336
337 prop = &smbb_charger_attrs[which];
338
339 rc = of_property_read_u32(chg->dev->of_node, prop->name, &val);
340 if (rc == 0) {
341 rc = smbb_charger_attr_write(chg, which, val);
342 if (!rc || !prop->fail_ok)
343 return rc;
344 }
345 return smbb_charger_attr_read(chg, which);
346}
347
348static void smbb_set_line_flag(struct smbb_charger *chg, int irq, int flag)
349{
350 bool state;
351 int ret;
352
353 ret = irq_get_irqchip_state(irq, IRQCHIP_STATE_LINE_LEVEL, &state);
Dan Carpenter0bc58e92015-09-24 21:41:37 +0300354 if (ret < 0) {
Courtney Cavin56d7df82015-07-30 10:53:56 -0700355 dev_err(chg->dev, "failed to read irq line\n");
356 return;
357 }
358
359 mutex_lock(&chg->statlock);
360 if (state)
361 chg->status |= flag;
362 else
363 chg->status &= ~flag;
364 mutex_unlock(&chg->statlock);
365
366 dev_dbg(chg->dev, "status = %03lx\n", chg->status);
367}
368
369static irqreturn_t smbb_usb_valid_handler(int irq, void *_data)
370{
371 struct smbb_charger *chg = _data;
372
373 smbb_set_line_flag(chg, irq, STATUS_USBIN_VALID);
374 power_supply_changed(chg->usb_psy);
375
376 return IRQ_HANDLED;
377}
378
379static irqreturn_t smbb_dc_valid_handler(int irq, void *_data)
380{
381 struct smbb_charger *chg = _data;
382
383 smbb_set_line_flag(chg, irq, STATUS_DCIN_VALID);
384 if (!chg->dc_disabled)
385 power_supply_changed(chg->dc_psy);
386
387 return IRQ_HANDLED;
388}
389
390static irqreturn_t smbb_bat_temp_handler(int irq, void *_data)
391{
392 struct smbb_charger *chg = _data;
393 unsigned int val;
394 int rc;
395
396 rc = regmap_read(chg->regmap, chg->addr + SMBB_BAT_TEMP_STATUS, &val);
397 if (rc)
398 return IRQ_HANDLED;
399
400 mutex_lock(&chg->statlock);
401 if (val & TEMP_STATUS_OK) {
402 chg->status |= STATUS_BAT_OK;
403 } else {
404 chg->status &= ~STATUS_BAT_OK;
405 if (val & TEMP_STATUS_HOT)
406 chg->status |= STATUS_BAT_HOT;
407 }
408 mutex_unlock(&chg->statlock);
409
410 power_supply_changed(chg->bat_psy);
411 return IRQ_HANDLED;
412}
413
414static irqreturn_t smbb_bat_present_handler(int irq, void *_data)
415{
416 struct smbb_charger *chg = _data;
417
418 smbb_set_line_flag(chg, irq, STATUS_BAT_PRESENT);
419 power_supply_changed(chg->bat_psy);
420
421 return IRQ_HANDLED;
422}
423
424static irqreturn_t smbb_chg_done_handler(int irq, void *_data)
425{
426 struct smbb_charger *chg = _data;
427
428 smbb_set_line_flag(chg, irq, STATUS_CHG_DONE);
429 power_supply_changed(chg->bat_psy);
430
431 return IRQ_HANDLED;
432}
433
434static irqreturn_t smbb_chg_gone_handler(int irq, void *_data)
435{
436 struct smbb_charger *chg = _data;
437
438 smbb_set_line_flag(chg, irq, STATUS_CHG_GONE);
439 power_supply_changed(chg->bat_psy);
440 power_supply_changed(chg->usb_psy);
441 if (!chg->dc_disabled)
442 power_supply_changed(chg->dc_psy);
443
444 return IRQ_HANDLED;
445}
446
447static irqreturn_t smbb_chg_fast_handler(int irq, void *_data)
448{
449 struct smbb_charger *chg = _data;
450
451 smbb_set_line_flag(chg, irq, STATUS_CHG_FAST);
452 power_supply_changed(chg->bat_psy);
453
454 return IRQ_HANDLED;
455}
456
457static irqreturn_t smbb_chg_trkl_handler(int irq, void *_data)
458{
459 struct smbb_charger *chg = _data;
460
461 smbb_set_line_flag(chg, irq, STATUS_CHG_TRKL);
462 power_supply_changed(chg->bat_psy);
463
464 return IRQ_HANDLED;
465}
466
467static const struct smbb_irq {
468 const char *name;
469 irqreturn_t (*handler)(int, void *);
470} smbb_charger_irqs[] = {
471 { "chg-done", smbb_chg_done_handler },
472 { "chg-fast", smbb_chg_fast_handler },
473 { "chg-trkl", smbb_chg_trkl_handler },
474 { "bat-temp-ok", smbb_bat_temp_handler },
475 { "bat-present", smbb_bat_present_handler },
476 { "chg-gone", smbb_chg_gone_handler },
477 { "usb-valid", smbb_usb_valid_handler },
478 { "dc-valid", smbb_dc_valid_handler },
479};
480
481static int smbb_usbin_get_property(struct power_supply *psy,
482 enum power_supply_property psp,
483 union power_supply_propval *val)
484{
485 struct smbb_charger *chg = power_supply_get_drvdata(psy);
486 int rc = 0;
487
488 switch (psp) {
489 case POWER_SUPPLY_PROP_ONLINE:
490 mutex_lock(&chg->statlock);
491 val->intval = !(chg->status & STATUS_CHG_GONE) &&
492 (chg->status & STATUS_USBIN_VALID);
493 mutex_unlock(&chg->statlock);
494 break;
495 case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
496 val->intval = chg->attr[ATTR_USBIN_IMAX];
497 break;
498 case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX:
499 val->intval = 2500000;
500 break;
501 default:
502 rc = -EINVAL;
503 break;
504 }
505
506 return rc;
507}
508
509static int smbb_usbin_set_property(struct power_supply *psy,
510 enum power_supply_property psp,
511 const union power_supply_propval *val)
512{
513 struct smbb_charger *chg = power_supply_get_drvdata(psy);
514 int rc;
515
516 switch (psp) {
517 case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
518 rc = smbb_charger_attr_write(chg, ATTR_USBIN_IMAX,
519 val->intval);
520 break;
521 default:
522 rc = -EINVAL;
523 break;
524 }
525
526 return rc;
527}
528
529static int smbb_dcin_get_property(struct power_supply *psy,
530 enum power_supply_property psp,
531 union power_supply_propval *val)
532{
533 struct smbb_charger *chg = power_supply_get_drvdata(psy);
534 int rc = 0;
535
536 switch (psp) {
537 case POWER_SUPPLY_PROP_ONLINE:
538 mutex_lock(&chg->statlock);
539 val->intval = !(chg->status & STATUS_CHG_GONE) &&
540 (chg->status & STATUS_DCIN_VALID);
541 mutex_unlock(&chg->statlock);
542 break;
543 case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
544 val->intval = chg->attr[ATTR_DCIN_IMAX];
545 break;
546 case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX:
547 val->intval = 2500000;
548 break;
549 default:
550 rc = -EINVAL;
551 break;
552 }
553
554 return rc;
555}
556
557static int smbb_dcin_set_property(struct power_supply *psy,
558 enum power_supply_property psp,
559 const union power_supply_propval *val)
560{
561 struct smbb_charger *chg = power_supply_get_drvdata(psy);
562 int rc;
563
564 switch (psp) {
565 case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
566 rc = smbb_charger_attr_write(chg, ATTR_DCIN_IMAX,
567 val->intval);
568 break;
569 default:
570 rc = -EINVAL;
571 break;
572 }
573
574 return rc;
575}
576
577static int smbb_charger_writable_property(struct power_supply *psy,
578 enum power_supply_property psp)
579{
580 return psp == POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT;
581}
582
583static int smbb_battery_get_property(struct power_supply *psy,
584 enum power_supply_property psp,
585 union power_supply_propval *val)
586{
587 struct smbb_charger *chg = power_supply_get_drvdata(psy);
588 unsigned long status;
589 int rc = 0;
590
591 mutex_lock(&chg->statlock);
592 status = chg->status;
593 mutex_unlock(&chg->statlock);
594
595 switch (psp) {
596 case POWER_SUPPLY_PROP_STATUS:
597 if (status & STATUS_CHG_GONE)
598 val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
599 else if (!(status & (STATUS_DCIN_VALID | STATUS_USBIN_VALID)))
600 val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
601 else if (status & STATUS_CHG_DONE)
602 val->intval = POWER_SUPPLY_STATUS_FULL;
603 else if (!(status & STATUS_BAT_OK))
604 val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
605 else if (status & (STATUS_CHG_FAST | STATUS_CHG_TRKL))
606 val->intval = POWER_SUPPLY_STATUS_CHARGING;
607 else /* everything is ok for charging, but we are not... */
608 val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
609 break;
610 case POWER_SUPPLY_PROP_HEALTH:
611 if (status & STATUS_BAT_OK)
612 val->intval = POWER_SUPPLY_HEALTH_GOOD;
613 else if (status & STATUS_BAT_HOT)
614 val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
615 else
616 val->intval = POWER_SUPPLY_HEALTH_COLD;
617 break;
618 case POWER_SUPPLY_PROP_CHARGE_TYPE:
619 if (status & STATUS_CHG_FAST)
620 val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST;
621 else if (status & STATUS_CHG_TRKL)
622 val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
623 else
624 val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;
625 break;
626 case POWER_SUPPLY_PROP_PRESENT:
627 val->intval = !!(status & STATUS_BAT_PRESENT);
628 break;
629 case POWER_SUPPLY_PROP_CURRENT_MAX:
630 val->intval = chg->attr[ATTR_BAT_IMAX];
631 break;
632 case POWER_SUPPLY_PROP_VOLTAGE_MAX:
633 val->intval = chg->attr[ATTR_BAT_VMAX];
634 break;
635 case POWER_SUPPLY_PROP_TECHNOLOGY:
636 /* this charger is a single-cell lithium-ion battery charger
637 * only. If you hook up some other technology, there will be
638 * fireworks.
639 */
640 val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
641 break;
642 case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
643 val->intval = 3000000; /* single-cell li-ion low end */
644 break;
645 default:
646 rc = -EINVAL;
647 break;
648 }
649
650 return rc;
651}
652
653static int smbb_battery_set_property(struct power_supply *psy,
654 enum power_supply_property psp,
655 const union power_supply_propval *val)
656{
657 struct smbb_charger *chg = power_supply_get_drvdata(psy);
658 int rc;
659
660 switch (psp) {
661 case POWER_SUPPLY_PROP_CURRENT_MAX:
662 rc = smbb_charger_attr_write(chg, ATTR_BAT_IMAX, val->intval);
663 break;
664 case POWER_SUPPLY_PROP_VOLTAGE_MAX:
665 rc = smbb_charger_attr_write(chg, ATTR_BAT_VMAX, val->intval);
666 break;
667 default:
668 rc = -EINVAL;
669 break;
670 }
671
672 return rc;
673}
674
675static int smbb_battery_writable_property(struct power_supply *psy,
676 enum power_supply_property psp)
677{
678 switch (psp) {
679 case POWER_SUPPLY_PROP_CURRENT_MAX:
680 case POWER_SUPPLY_PROP_VOLTAGE_MAX:
681 return 1;
682 default:
683 return 0;
684 }
685}
686
687static enum power_supply_property smbb_charger_properties[] = {
688 POWER_SUPPLY_PROP_ONLINE,
689 POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT,
690 POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX,
691};
692
693static enum power_supply_property smbb_battery_properties[] = {
694 POWER_SUPPLY_PROP_STATUS,
695 POWER_SUPPLY_PROP_HEALTH,
696 POWER_SUPPLY_PROP_PRESENT,
697 POWER_SUPPLY_PROP_CHARGE_TYPE,
698 POWER_SUPPLY_PROP_CURRENT_MAX,
699 POWER_SUPPLY_PROP_VOLTAGE_MAX,
700 POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
701 POWER_SUPPLY_PROP_TECHNOLOGY,
702};
703
704static const struct reg_off_mask_default {
705 unsigned int offset;
706 unsigned int mask;
707 unsigned int value;
708 unsigned int rev_mask;
709} smbb_charger_setup[] = {
710 /* The bootloader is supposed to set this... make sure anyway. */
711 { SMBB_MISC_BOOT_DONE, BOOT_DONE, BOOT_DONE },
712
713 /* Disable software timer */
714 { SMBB_CHG_TCHG_MAX_EN, TCHG_MAX_EN, 0 },
715
716 /* Clear and disable watchdog */
717 { SMBB_CHG_WDOG_TIME, 0xff, 160 },
718 { SMBB_CHG_WDOG_EN, WDOG_EN, 0 },
719
720 /* Use charger based EoC detection */
721 { SMBB_CHG_IBAT_TERM_CHG, IBAT_TERM_CHG_IEOC, IBAT_TERM_CHG_IEOC_CHG },
722
723 /* Disable GSM PA load adjustment.
724 * The PA signal is incorrectly connected on v2.
725 */
726 { SMBB_CHG_CFG, 0xff, 0x00, BIT(3) },
727
728 /* Use VBAT (not VSYS) to compensate for IR drop during fast charging */
729 { SMBB_BUCK_REG_MODE, BUCK_REG_MODE, BUCK_REG_MODE_VBAT },
730
731 /* Enable battery temperature comparators */
732 { SMBB_BAT_BTC_CTRL, BTC_CTRL_COMP_EN, BTC_CTRL_COMP_EN },
733
734 /* Stop USB enumeration timer */
735 { SMBB_USB_ENUM_TIMER_STOP, ENUM_TIMER_STOP, ENUM_TIMER_STOP },
736
737#if 0 /* FIXME supposedly only to disable hardware ARB termination */
738 { SMBB_USB_SEC_ACCESS, SEC_ACCESS_MAGIC },
739 { SMBB_USB_REV_BST, 0xff, REV_BST_CHG_GONE },
740#endif
741
742 /* Stop USB enumeration timer, again */
743 { SMBB_USB_ENUM_TIMER_STOP, ENUM_TIMER_STOP, ENUM_TIMER_STOP },
744
745 /* Enable charging */
746 { SMBB_CHG_CTRL, CTRL_EN, CTRL_EN },
747};
748
749static char *smbb_bif[] = { "smbb-bif" };
750
751static const struct power_supply_desc bat_psy_desc = {
752 .name = "smbb-bif",
753 .type = POWER_SUPPLY_TYPE_BATTERY,
754 .properties = smbb_battery_properties,
755 .num_properties = ARRAY_SIZE(smbb_battery_properties),
756 .get_property = smbb_battery_get_property,
757 .set_property = smbb_battery_set_property,
758 .property_is_writeable = smbb_battery_writable_property,
759};
760
761static const struct power_supply_desc usb_psy_desc = {
762 .name = "smbb-usbin",
763 .type = POWER_SUPPLY_TYPE_USB,
764 .properties = smbb_charger_properties,
765 .num_properties = ARRAY_SIZE(smbb_charger_properties),
766 .get_property = smbb_usbin_get_property,
767 .set_property = smbb_usbin_set_property,
768 .property_is_writeable = smbb_charger_writable_property,
769};
770
771static const struct power_supply_desc dc_psy_desc = {
772 .name = "smbb-dcin",
773 .type = POWER_SUPPLY_TYPE_MAINS,
774 .properties = smbb_charger_properties,
775 .num_properties = ARRAY_SIZE(smbb_charger_properties),
776 .get_property = smbb_dcin_get_property,
777 .set_property = smbb_dcin_set_property,
778 .property_is_writeable = smbb_charger_writable_property,
779};
780
781static int smbb_charger_probe(struct platform_device *pdev)
782{
783 struct power_supply_config bat_cfg = {};
784 struct power_supply_config usb_cfg = {};
785 struct power_supply_config dc_cfg = {};
786 struct smbb_charger *chg;
787 int rc, i;
788
789 chg = devm_kzalloc(&pdev->dev, sizeof(*chg), GFP_KERNEL);
790 if (!chg)
791 return -ENOMEM;
792
793 chg->dev = &pdev->dev;
794 mutex_init(&chg->statlock);
795
796 chg->regmap = dev_get_regmap(pdev->dev.parent, NULL);
797 if (!chg->regmap) {
798 dev_err(&pdev->dev, "failed to locate regmap\n");
799 return -ENODEV;
800 }
801
802 rc = of_property_read_u32(pdev->dev.of_node, "reg", &chg->addr);
803 if (rc) {
804 dev_err(&pdev->dev, "missing or invalid 'reg' property\n");
805 return rc;
806 }
807
808 rc = regmap_read(chg->regmap, chg->addr + SMBB_MISC_REV2, &chg->revision);
809 if (rc) {
810 dev_err(&pdev->dev, "unable to read revision\n");
811 return rc;
812 }
813
814 chg->revision += 1;
815 if (chg->revision != 2 && chg->revision != 3) {
816 dev_err(&pdev->dev, "v1 hardware not supported\n");
817 return -ENODEV;
818 }
819 dev_info(&pdev->dev, "Initializing SMBB rev %u", chg->revision);
820
821 chg->dc_disabled = of_property_read_bool(pdev->dev.of_node, "qcom,disable-dc");
822
823 for (i = 0; i < _ATTR_CNT; ++i) {
824 rc = smbb_charger_attr_parse(chg, i);
825 if (rc) {
826 dev_err(&pdev->dev, "failed to parse/apply settings\n");
827 return rc;
828 }
829 }
830
831 bat_cfg.drv_data = chg;
832 bat_cfg.of_node = pdev->dev.of_node;
833 chg->bat_psy = devm_power_supply_register(&pdev->dev,
834 &bat_psy_desc,
835 &bat_cfg);
836 if (IS_ERR(chg->bat_psy)) {
837 dev_err(&pdev->dev, "failed to register battery\n");
838 return PTR_ERR(chg->bat_psy);
839 }
840
841 usb_cfg.drv_data = chg;
842 usb_cfg.supplied_to = smbb_bif;
843 usb_cfg.num_supplicants = ARRAY_SIZE(smbb_bif);
844 chg->usb_psy = devm_power_supply_register(&pdev->dev,
845 &usb_psy_desc,
846 &usb_cfg);
847 if (IS_ERR(chg->usb_psy)) {
848 dev_err(&pdev->dev, "failed to register USB power supply\n");
849 return PTR_ERR(chg->usb_psy);
850 }
851
852 if (!chg->dc_disabled) {
853 dc_cfg.drv_data = chg;
854 dc_cfg.supplied_to = smbb_bif;
855 dc_cfg.num_supplicants = ARRAY_SIZE(smbb_bif);
856 chg->dc_psy = devm_power_supply_register(&pdev->dev,
857 &dc_psy_desc,
858 &dc_cfg);
859 if (IS_ERR(chg->dc_psy)) {
860 dev_err(&pdev->dev, "failed to register DC power supply\n");
861 return PTR_ERR(chg->dc_psy);
862 }
863 }
864
865 for (i = 0; i < ARRAY_SIZE(smbb_charger_irqs); ++i) {
866 int irq;
867
868 irq = platform_get_irq_byname(pdev, smbb_charger_irqs[i].name);
869 if (irq < 0) {
870 dev_err(&pdev->dev, "failed to get irq '%s'\n",
871 smbb_charger_irqs[i].name);
872 return irq;
873 }
874
875 smbb_charger_irqs[i].handler(irq, chg);
876
877 rc = devm_request_threaded_irq(&pdev->dev, irq, NULL,
878 smbb_charger_irqs[i].handler, IRQF_ONESHOT,
879 smbb_charger_irqs[i].name, chg);
880 if (rc) {
881 dev_err(&pdev->dev, "failed to request irq '%s'\n",
882 smbb_charger_irqs[i].name);
883 return rc;
884 }
885 }
886
887 chg->jeita_ext_temp = of_property_read_bool(pdev->dev.of_node,
888 "qcom,jeita-extended-temp-range");
889
890 /* Set temperature range to [35%:70%] or [25%:80%] accordingly */
891 rc = regmap_update_bits(chg->regmap, chg->addr + SMBB_BAT_BTC_CTRL,
892 BTC_CTRL_COLD_EXT | BTC_CTRL_HOT_EXT_N,
893 chg->jeita_ext_temp ?
894 BTC_CTRL_COLD_EXT :
895 BTC_CTRL_HOT_EXT_N);
896 if (rc) {
897 dev_err(&pdev->dev,
898 "unable to set %s temperature range\n",
899 chg->jeita_ext_temp ? "JEITA extended" : "normal");
900 return rc;
901 }
902
903 for (i = 0; i < ARRAY_SIZE(smbb_charger_setup); ++i) {
904 const struct reg_off_mask_default *r = &smbb_charger_setup[i];
905
906 if (r->rev_mask & BIT(chg->revision))
907 continue;
908
909 rc = regmap_update_bits(chg->regmap, chg->addr + r->offset,
910 r->mask, r->value);
911 if (rc) {
912 dev_err(&pdev->dev,
913 "unable to initializing charging, bailing\n");
914 return rc;
915 }
916 }
917
918 platform_set_drvdata(pdev, chg);
919
920 return 0;
921}
922
923static int smbb_charger_remove(struct platform_device *pdev)
924{
925 struct smbb_charger *chg;
926
927 chg = platform_get_drvdata(pdev);
928
929 regmap_update_bits(chg->regmap, chg->addr + SMBB_CHG_CTRL, CTRL_EN, 0);
930
931 return 0;
932}
933
934static const struct of_device_id smbb_charger_id_table[] = {
935 { .compatible = "qcom,pm8941-charger" },
936 { }
937};
938MODULE_DEVICE_TABLE(of, smbb_charger_id_table);
939
940static struct platform_driver smbb_charger_driver = {
941 .probe = smbb_charger_probe,
942 .remove = smbb_charger_remove,
943 .driver = {
944 .name = "qcom-smbb",
945 .of_match_table = smbb_charger_id_table,
946 },
947};
948module_platform_driver(smbb_charger_driver);
949
950MODULE_DESCRIPTION("Qualcomm Switch-Mode Battery Charger and Boost driver");
951MODULE_LICENSE("GPL v2");