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