1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * TWL4030/TPS65950 BCI (Battery Charger Interface) driver |
4 | * |
5 | * Copyright (C) 2010 Gražvydas Ignotas <notasas@gmail.com> |
6 | * |
7 | * based on twl4030_bci_battery.c by TI |
8 | * Copyright (C) 2008 Texas Instruments, Inc. |
9 | */ |
10 | |
11 | #include <linux/init.h> |
12 | #include <linux/module.h> |
13 | #include <linux/slab.h> |
14 | #include <linux/err.h> |
15 | #include <linux/of.h> |
16 | #include <linux/platform_device.h> |
17 | #include <linux/interrupt.h> |
18 | #include <linux/mfd/twl.h> |
19 | #include <linux/power_supply.h> |
20 | #include <linux/notifier.h> |
21 | #include <linux/usb/otg.h> |
22 | #include <linux/iio/consumer.h> |
23 | |
24 | #define TWL4030_BCIMDEN 0x00 |
25 | #define TWL4030_BCIMDKEY 0x01 |
26 | #define TWL4030_BCIMSTATEC 0x02 |
27 | #define TWL4030_BCIICHG 0x08 |
28 | #define TWL4030_BCIVAC 0x0a |
29 | #define TWL4030_BCIVBUS 0x0c |
30 | #define TWL4030_BCIMFSTS3 0x0F |
31 | #define TWL4030_BCIMFSTS4 0x10 |
32 | #define TWL4030_BCICTL1 0x23 |
33 | #define TWL4030_BB_CFG 0x12 |
34 | #define TWL4030_BCIIREF1 0x27 |
35 | #define TWL4030_BCIIREF2 0x28 |
36 | #define TWL4030_BCIMFKEY 0x11 |
37 | #define TWL4030_BCIMFEN3 0x14 |
38 | #define TWL4030_BCIMFTH8 0x1d |
39 | #define TWL4030_BCIMFTH9 0x1e |
40 | #define TWL4030_BCIWDKEY 0x21 |
41 | |
42 | #define TWL4030_BCIMFSTS1 0x01 |
43 | |
44 | #define TWL4030_BCIAUTOWEN BIT(5) |
45 | #define TWL4030_CONFIG_DONE BIT(4) |
46 | #define TWL4030_CVENAC BIT(2) |
47 | #define TWL4030_BCIAUTOUSB BIT(1) |
48 | #define TWL4030_BCIAUTOAC BIT(0) |
49 | #define TWL4030_CGAIN BIT(5) |
50 | #define TWL4030_USBFASTMCHG BIT(2) |
51 | #define TWL4030_STS_VBUS BIT(7) |
52 | #define TWL4030_STS_USB_ID BIT(2) |
53 | #define TWL4030_BBCHEN BIT(4) |
54 | #define TWL4030_BBSEL_MASK 0x0c |
55 | #define TWL4030_BBSEL_2V5 0x00 |
56 | #define TWL4030_BBSEL_3V0 0x04 |
57 | #define TWL4030_BBSEL_3V1 0x08 |
58 | #define TWL4030_BBSEL_3V2 0x0c |
59 | #define TWL4030_BBISEL_MASK 0x03 |
60 | #define TWL4030_BBISEL_25uA 0x00 |
61 | #define TWL4030_BBISEL_150uA 0x01 |
62 | #define TWL4030_BBISEL_500uA 0x02 |
63 | #define TWL4030_BBISEL_1000uA 0x03 |
64 | |
65 | #define TWL4030_BATSTSPCHG BIT(2) |
66 | #define TWL4030_BATSTSMCHG BIT(6) |
67 | |
68 | /* BCI interrupts */ |
69 | #define TWL4030_WOVF BIT(0) /* Watchdog overflow */ |
70 | #define TWL4030_TMOVF BIT(1) /* Timer overflow */ |
71 | #define TWL4030_ICHGHIGH BIT(2) /* Battery charge current high */ |
72 | #define TWL4030_ICHGLOW BIT(3) /* Battery cc. low / FSM state change */ |
73 | #define TWL4030_ICHGEOC BIT(4) /* Battery current end-of-charge */ |
74 | #define TWL4030_TBATOR2 BIT(5) /* Battery temperature out of range 2 */ |
75 | #define TWL4030_TBATOR1 BIT(6) /* Battery temperature out of range 1 */ |
76 | #define TWL4030_BATSTS BIT(7) /* Battery status */ |
77 | |
78 | #define TWL4030_VBATLVL BIT(0) /* VBAT level */ |
79 | #define TWL4030_VBATOV BIT(1) /* VBAT overvoltage */ |
80 | #define TWL4030_VBUSOV BIT(2) /* VBUS overvoltage */ |
81 | #define TWL4030_ACCHGOV BIT(3) /* Ac charger overvoltage */ |
82 | |
83 | #define TWL4030_MSTATEC_USB BIT(4) |
84 | #define TWL4030_MSTATEC_AC BIT(5) |
85 | #define TWL4030_MSTATEC_MASK 0x0f |
86 | #define TWL4030_MSTATEC_QUICK1 0x02 |
87 | #define TWL4030_MSTATEC_QUICK7 0x07 |
88 | #define TWL4030_MSTATEC_COMPLETE1 0x0b |
89 | #define TWL4030_MSTATEC_COMPLETE4 0x0e |
90 | |
91 | /* |
92 | * If AC (Accessory Charger) voltage exceeds 4.5V (MADC 11) |
93 | * then AC is available. |
94 | */ |
95 | static inline int ac_available(struct iio_channel *channel_vac) |
96 | { |
97 | int val, err; |
98 | |
99 | if (!channel_vac) |
100 | return 0; |
101 | |
102 | err = iio_read_channel_processed(chan: channel_vac, val: &val); |
103 | if (err < 0) |
104 | return 0; |
105 | return val > 4500; |
106 | } |
107 | |
108 | static bool allow_usb; |
109 | module_param(allow_usb, bool, 0644); |
110 | MODULE_PARM_DESC(allow_usb, "Allow USB charge drawing default current" ); |
111 | |
112 | struct twl4030_bci { |
113 | struct device *dev; |
114 | struct power_supply *ac; |
115 | struct power_supply *usb; |
116 | struct usb_phy *transceiver; |
117 | struct notifier_block usb_nb; |
118 | struct work_struct work; |
119 | int irq_chg; |
120 | int irq_bci; |
121 | int usb_enabled; |
122 | |
123 | /* |
124 | * ichg_* and *_cur values in uA. If any are 'large', we set |
125 | * CGAIN to '1' which doubles the range for half the |
126 | * precision. |
127 | */ |
128 | unsigned int ichg_eoc, ichg_lo, ichg_hi; |
129 | unsigned int usb_cur, ac_cur; |
130 | struct iio_channel *channel_vac; |
131 | bool ac_is_active; |
132 | int usb_mode, ac_mode; /* charging mode requested */ |
133 | #define CHARGE_OFF 0 |
134 | #define CHARGE_AUTO 1 |
135 | #define CHARGE_LINEAR 2 |
136 | |
137 | /* When setting the USB current we slowly increase the |
138 | * requested current until target is reached or the voltage |
139 | * drops below 4.75V. In the latter case we step back one |
140 | * step. |
141 | */ |
142 | unsigned int usb_cur_target; |
143 | struct delayed_work current_worker; |
144 | #define USB_CUR_STEP 20000 /* 20mA at a time */ |
145 | #define USB_MIN_VOLT 4750000 /* 4.75V */ |
146 | #define USB_CUR_DELAY msecs_to_jiffies(100) |
147 | #define USB_MAX_CURRENT 1700000 /* TWL4030 caps at 1.7A */ |
148 | |
149 | unsigned long event; |
150 | }; |
151 | |
152 | /* strings for 'usb_mode' values */ |
153 | static const char *modes[] = { "off" , "auto" , "continuous" }; |
154 | |
155 | /* |
156 | * clear and set bits on an given register on a given module |
157 | */ |
158 | static int twl4030_clear_set(u8 mod_no, u8 clear, u8 set, u8 reg) |
159 | { |
160 | u8 val = 0; |
161 | int ret; |
162 | |
163 | ret = twl_i2c_read_u8(mod_no, val: &val, reg); |
164 | if (ret) |
165 | return ret; |
166 | |
167 | val &= ~clear; |
168 | val |= set; |
169 | |
170 | return twl_i2c_write_u8(mod_no, val, reg); |
171 | } |
172 | |
173 | static int twl4030_bci_read(u8 reg, u8 *val) |
174 | { |
175 | return twl_i2c_read_u8(mod_no: TWL_MODULE_MAIN_CHARGE, val, reg); |
176 | } |
177 | |
178 | static int twl4030_clear_set_boot_bci(u8 clear, u8 set) |
179 | { |
180 | return twl4030_clear_set(mod_no: TWL_MODULE_PM_MASTER, clear, |
181 | TWL4030_CONFIG_DONE | TWL4030_BCIAUTOWEN | set, |
182 | TWL4030_PM_MASTER_BOOT_BCI); |
183 | } |
184 | |
185 | static int twl4030bci_read_adc_val(u8 reg) |
186 | { |
187 | int ret, temp; |
188 | u8 val; |
189 | |
190 | /* read MSB */ |
191 | ret = twl4030_bci_read(reg: reg + 1, val: &val); |
192 | if (ret) |
193 | return ret; |
194 | |
195 | temp = (int)(val & 0x03) << 8; |
196 | |
197 | /* read LSB */ |
198 | ret = twl4030_bci_read(reg, val: &val); |
199 | if (ret) |
200 | return ret; |
201 | |
202 | return temp | val; |
203 | } |
204 | |
205 | /* |
206 | * TI provided formulas: |
207 | * CGAIN == 0: ICHG = (BCIICHG * 1.7) / (2^10 - 1) - 0.85 |
208 | * CGAIN == 1: ICHG = (BCIICHG * 3.4) / (2^10 - 1) - 1.7 |
209 | * Here we use integer approximation of: |
210 | * CGAIN == 0: val * 1.6618 - 0.85 * 1000 |
211 | * CGAIN == 1: (val * 1.6618 - 0.85 * 1000) * 2 |
212 | */ |
213 | /* |
214 | * convert twl register value for currents into uA |
215 | */ |
216 | static int regval2ua(int regval, bool cgain) |
217 | { |
218 | if (cgain) |
219 | return (regval * 16618 - 8500 * 1000) / 5; |
220 | else |
221 | return (regval * 16618 - 8500 * 1000) / 10; |
222 | } |
223 | |
224 | /* |
225 | * convert uA currents into twl register value |
226 | */ |
227 | static int ua2regval(int ua, bool cgain) |
228 | { |
229 | int ret; |
230 | if (cgain) |
231 | ua /= 2; |
232 | ret = (ua * 10 + 8500 * 1000) / 16618; |
233 | /* rounding problems */ |
234 | if (ret < 512) |
235 | ret = 512; |
236 | return ret; |
237 | } |
238 | |
239 | static int twl4030_charger_update_current(struct twl4030_bci *bci) |
240 | { |
241 | int status; |
242 | int cur; |
243 | unsigned reg, cur_reg; |
244 | u8 bcictl1, oldreg, fullreg; |
245 | bool cgain = false; |
246 | u8 boot_bci; |
247 | |
248 | /* |
249 | * If AC (Accessory Charger) voltage exceeds 4.5V (MADC 11) |
250 | * and AC is enabled, set current for 'ac' |
251 | */ |
252 | if (ac_available(channel_vac: bci->channel_vac)) { |
253 | cur = bci->ac_cur; |
254 | bci->ac_is_active = true; |
255 | } else { |
256 | cur = bci->usb_cur; |
257 | bci->ac_is_active = false; |
258 | if (cur > bci->usb_cur_target) { |
259 | cur = bci->usb_cur_target; |
260 | bci->usb_cur = cur; |
261 | } |
262 | if (cur < bci->usb_cur_target) |
263 | schedule_delayed_work(dwork: &bci->current_worker, USB_CUR_DELAY); |
264 | } |
265 | |
266 | /* First, check thresholds and see if cgain is needed */ |
267 | if (bci->ichg_eoc >= 200000) |
268 | cgain = true; |
269 | if (bci->ichg_lo >= 400000) |
270 | cgain = true; |
271 | if (bci->ichg_hi >= 820000) |
272 | cgain = true; |
273 | if (cur > 852000) |
274 | cgain = true; |
275 | |
276 | status = twl4030_bci_read(TWL4030_BCICTL1, val: &bcictl1); |
277 | if (status < 0) |
278 | return status; |
279 | if (twl_i2c_read_u8(mod_no: TWL_MODULE_PM_MASTER, val: &boot_bci, |
280 | TWL4030_PM_MASTER_BOOT_BCI) < 0) |
281 | boot_bci = 0; |
282 | boot_bci &= 7; |
283 | |
284 | if ((!!cgain) != !!(bcictl1 & TWL4030_CGAIN)) |
285 | /* Need to turn for charging while we change the |
286 | * CGAIN bit. Leave it off while everything is |
287 | * updated. |
288 | */ |
289 | twl4030_clear_set_boot_bci(clear: boot_bci, set: 0); |
290 | |
291 | /* |
292 | * For ichg_eoc, the hardware only supports reg values matching |
293 | * 100XXXX000, and requires the XXXX be stored in the high nibble |
294 | * of TWL4030_BCIMFTH8. |
295 | */ |
296 | reg = ua2regval(ua: bci->ichg_eoc, cgain); |
297 | if (reg > 0x278) |
298 | reg = 0x278; |
299 | if (reg < 0x200) |
300 | reg = 0x200; |
301 | reg = (reg >> 3) & 0xf; |
302 | fullreg = reg << 4; |
303 | |
304 | /* |
305 | * For ichg_lo, reg value must match 10XXXX0000. |
306 | * XXXX is stored in low nibble of TWL4030_BCIMFTH8. |
307 | */ |
308 | reg = ua2regval(ua: bci->ichg_lo, cgain); |
309 | if (reg > 0x2F0) |
310 | reg = 0x2F0; |
311 | if (reg < 0x200) |
312 | reg = 0x200; |
313 | reg = (reg >> 4) & 0xf; |
314 | fullreg |= reg; |
315 | |
316 | /* ichg_eoc and ichg_lo live in same register */ |
317 | status = twl4030_bci_read(TWL4030_BCIMFTH8, val: &oldreg); |
318 | if (status < 0) |
319 | return status; |
320 | if (oldreg != fullreg) { |
321 | status = twl_i2c_write_u8(mod_no: TWL_MODULE_MAIN_CHARGE, val: 0xF4, |
322 | TWL4030_BCIMFKEY); |
323 | if (status < 0) |
324 | return status; |
325 | twl_i2c_write_u8(mod_no: TWL_MODULE_MAIN_CHARGE, |
326 | val: fullreg, TWL4030_BCIMFTH8); |
327 | } |
328 | |
329 | /* ichg_hi threshold must be 1XXXX01100 (I think) */ |
330 | reg = ua2regval(ua: bci->ichg_hi, cgain); |
331 | if (reg > 0x3E0) |
332 | reg = 0x3E0; |
333 | if (reg < 0x200) |
334 | reg = 0x200; |
335 | fullreg = (reg >> 5) & 0xF; |
336 | fullreg <<= 4; |
337 | status = twl4030_bci_read(TWL4030_BCIMFTH9, val: &oldreg); |
338 | if (status < 0) |
339 | return status; |
340 | if ((oldreg & 0xF0) != fullreg) { |
341 | fullreg |= (oldreg & 0x0F); |
342 | status = twl_i2c_write_u8(mod_no: TWL_MODULE_MAIN_CHARGE, val: 0xE7, |
343 | TWL4030_BCIMFKEY); |
344 | if (status < 0) |
345 | return status; |
346 | twl_i2c_write_u8(mod_no: TWL_MODULE_MAIN_CHARGE, |
347 | val: fullreg, TWL4030_BCIMFTH9); |
348 | } |
349 | |
350 | /* |
351 | * And finally, set the current. This is stored in |
352 | * two registers. |
353 | */ |
354 | reg = ua2regval(ua: cur, cgain); |
355 | /* we have only 10 bits */ |
356 | if (reg > 0x3ff) |
357 | reg = 0x3ff; |
358 | status = twl4030_bci_read(TWL4030_BCIIREF1, val: &oldreg); |
359 | if (status < 0) |
360 | return status; |
361 | cur_reg = oldreg; |
362 | status = twl4030_bci_read(TWL4030_BCIIREF2, val: &oldreg); |
363 | if (status < 0) |
364 | return status; |
365 | cur_reg |= oldreg << 8; |
366 | if (reg != oldreg) { |
367 | /* disable write protection for one write access for |
368 | * BCIIREF */ |
369 | status = twl_i2c_write_u8(mod_no: TWL_MODULE_MAIN_CHARGE, val: 0xE7, |
370 | TWL4030_BCIMFKEY); |
371 | if (status < 0) |
372 | return status; |
373 | status = twl_i2c_write_u8(mod_no: TWL_MODULE_MAIN_CHARGE, |
374 | val: (reg & 0x100) ? 3 : 2, |
375 | TWL4030_BCIIREF2); |
376 | if (status < 0) |
377 | return status; |
378 | /* disable write protection for one write access for |
379 | * BCIIREF */ |
380 | status = twl_i2c_write_u8(mod_no: TWL_MODULE_MAIN_CHARGE, val: 0xE7, |
381 | TWL4030_BCIMFKEY); |
382 | if (status < 0) |
383 | return status; |
384 | status = twl_i2c_write_u8(mod_no: TWL_MODULE_MAIN_CHARGE, |
385 | val: reg & 0xff, |
386 | TWL4030_BCIIREF1); |
387 | } |
388 | if ((!!cgain) != !!(bcictl1 & TWL4030_CGAIN)) { |
389 | /* Flip CGAIN and re-enable charging */ |
390 | bcictl1 ^= TWL4030_CGAIN; |
391 | twl_i2c_write_u8(mod_no: TWL_MODULE_MAIN_CHARGE, |
392 | val: bcictl1, TWL4030_BCICTL1); |
393 | twl4030_clear_set_boot_bci(clear: 0, set: boot_bci); |
394 | } |
395 | return 0; |
396 | } |
397 | |
398 | static int twl4030_charger_get_current(void); |
399 | |
400 | static void twl4030_current_worker(struct work_struct *data) |
401 | { |
402 | int v, curr; |
403 | int res; |
404 | struct twl4030_bci *bci = container_of(data, struct twl4030_bci, |
405 | current_worker.work); |
406 | |
407 | res = twl4030bci_read_adc_val(TWL4030_BCIVBUS); |
408 | if (res < 0) |
409 | v = 0; |
410 | else |
411 | /* BCIVBUS uses ADCIN8, 7/1023 V/step */ |
412 | v = res * 6843; |
413 | curr = twl4030_charger_get_current(); |
414 | |
415 | dev_dbg(bci->dev, "v=%d cur=%d limit=%d target=%d\n" , v, curr, |
416 | bci->usb_cur, bci->usb_cur_target); |
417 | |
418 | if (v < USB_MIN_VOLT) { |
419 | /* Back up and stop adjusting. */ |
420 | if (bci->usb_cur >= USB_CUR_STEP) |
421 | bci->usb_cur -= USB_CUR_STEP; |
422 | bci->usb_cur_target = bci->usb_cur; |
423 | } else if (bci->usb_cur >= bci->usb_cur_target || |
424 | bci->usb_cur + USB_CUR_STEP > USB_MAX_CURRENT) { |
425 | /* Reached target and voltage is OK - stop */ |
426 | return; |
427 | } else { |
428 | bci->usb_cur += USB_CUR_STEP; |
429 | schedule_delayed_work(dwork: &bci->current_worker, USB_CUR_DELAY); |
430 | } |
431 | twl4030_charger_update_current(bci); |
432 | } |
433 | |
434 | /* |
435 | * Enable/Disable USB Charge functionality. |
436 | */ |
437 | static int twl4030_charger_enable_usb(struct twl4030_bci *bci, bool enable) |
438 | { |
439 | int ret; |
440 | u32 reg; |
441 | |
442 | if (bci->usb_mode == CHARGE_OFF) |
443 | enable = false; |
444 | if (enable && !IS_ERR_OR_NULL(ptr: bci->transceiver)) { |
445 | |
446 | twl4030_charger_update_current(bci); |
447 | |
448 | /* Need to keep phy powered */ |
449 | if (!bci->usb_enabled) { |
450 | pm_runtime_get_sync(dev: bci->transceiver->dev); |
451 | bci->usb_enabled = 1; |
452 | } |
453 | |
454 | if (bci->usb_mode == CHARGE_AUTO) { |
455 | /* Enable interrupts now. */ |
456 | reg = ~(u32)(TWL4030_ICHGLOW | TWL4030_ICHGEOC | |
457 | TWL4030_TBATOR2 | TWL4030_TBATOR1 | |
458 | TWL4030_BATSTS); |
459 | ret = twl_i2c_write_u8(mod_no: TWL4030_MODULE_INTERRUPTS, val: reg, |
460 | TWL4030_INTERRUPTS_BCIIMR1A); |
461 | if (ret < 0) { |
462 | dev_err(bci->dev, |
463 | "failed to unmask interrupts: %d\n" , |
464 | ret); |
465 | return ret; |
466 | } |
467 | /* forcing the field BCIAUTOUSB (BOOT_BCI[1]) to 1 */ |
468 | ret = twl4030_clear_set_boot_bci(clear: 0, TWL4030_BCIAUTOUSB); |
469 | } |
470 | |
471 | /* forcing USBFASTMCHG(BCIMFSTS4[2]) to 1 */ |
472 | ret = twl4030_clear_set(mod_no: TWL_MODULE_MAIN_CHARGE, clear: 0, |
473 | TWL4030_USBFASTMCHG, TWL4030_BCIMFSTS4); |
474 | if (bci->usb_mode == CHARGE_LINEAR) { |
475 | /* Enable interrupts now. */ |
476 | reg = ~(u32)(TWL4030_ICHGLOW | TWL4030_TBATOR2 | |
477 | TWL4030_TBATOR1 | TWL4030_BATSTS); |
478 | ret = twl_i2c_write_u8(mod_no: TWL4030_MODULE_INTERRUPTS, val: reg, |
479 | TWL4030_INTERRUPTS_BCIIMR1A); |
480 | if (ret < 0) { |
481 | dev_err(bci->dev, |
482 | "failed to unmask interrupts: %d\n" , |
483 | ret); |
484 | return ret; |
485 | } |
486 | twl4030_clear_set_boot_bci(TWL4030_BCIAUTOAC|TWL4030_CVENAC, set: 0); |
487 | /* Watch dog key: WOVF acknowledge */ |
488 | ret = twl_i2c_write_u8(mod_no: TWL_MODULE_MAIN_CHARGE, val: 0x33, |
489 | TWL4030_BCIWDKEY); |
490 | /* 0x24 + EKEY6: off mode */ |
491 | ret = twl_i2c_write_u8(mod_no: TWL_MODULE_MAIN_CHARGE, val: 0x2a, |
492 | TWL4030_BCIMDKEY); |
493 | /* EKEY2: Linear charge: USB path */ |
494 | ret = twl_i2c_write_u8(mod_no: TWL_MODULE_MAIN_CHARGE, val: 0x26, |
495 | TWL4030_BCIMDKEY); |
496 | /* WDKEY5: stop watchdog count */ |
497 | ret = twl_i2c_write_u8(mod_no: TWL_MODULE_MAIN_CHARGE, val: 0xf3, |
498 | TWL4030_BCIWDKEY); |
499 | /* enable MFEN3 access */ |
500 | ret = twl_i2c_write_u8(mod_no: TWL_MODULE_MAIN_CHARGE, val: 0x9c, |
501 | TWL4030_BCIMFKEY); |
502 | /* ICHGEOCEN - end-of-charge monitor (current < 80mA) |
503 | * (charging continues) |
504 | * ICHGLOWEN - current level monitor (charge continues) |
505 | * don't monitor over-current or heat save |
506 | */ |
507 | ret = twl_i2c_write_u8(mod_no: TWL_MODULE_MAIN_CHARGE, val: 0xf0, |
508 | TWL4030_BCIMFEN3); |
509 | } |
510 | } else { |
511 | ret = twl4030_clear_set_boot_bci(TWL4030_BCIAUTOUSB, set: 0); |
512 | ret |= twl_i2c_write_u8(mod_no: TWL_MODULE_MAIN_CHARGE, val: 0x2a, |
513 | TWL4030_BCIMDKEY); |
514 | if (bci->usb_enabled) { |
515 | pm_runtime_mark_last_busy(dev: bci->transceiver->dev); |
516 | pm_runtime_put_autosuspend(dev: bci->transceiver->dev); |
517 | bci->usb_enabled = 0; |
518 | } |
519 | bci->usb_cur = 0; |
520 | } |
521 | |
522 | return ret; |
523 | } |
524 | |
525 | /* |
526 | * Enable/Disable AC Charge funtionality. |
527 | */ |
528 | static int twl4030_charger_enable_ac(struct twl4030_bci *bci, bool enable) |
529 | { |
530 | int ret; |
531 | |
532 | if (bci->ac_mode == CHARGE_OFF) |
533 | enable = false; |
534 | |
535 | if (enable) |
536 | ret = twl4030_clear_set_boot_bci(clear: 0, TWL4030_BCIAUTOAC); |
537 | else |
538 | ret = twl4030_clear_set_boot_bci(TWL4030_BCIAUTOAC, set: 0); |
539 | |
540 | return ret; |
541 | } |
542 | |
543 | /* |
544 | * Enable/Disable charging of Backup Battery. |
545 | */ |
546 | static int twl4030_charger_enable_backup(int uvolt, int uamp) |
547 | { |
548 | int ret; |
549 | u8 flags; |
550 | |
551 | if (uvolt < 2500000 || |
552 | uamp < 25) { |
553 | /* disable charging of backup battery */ |
554 | ret = twl4030_clear_set(mod_no: TWL_MODULE_PM_RECEIVER, |
555 | TWL4030_BBCHEN, set: 0, TWL4030_BB_CFG); |
556 | return ret; |
557 | } |
558 | |
559 | flags = TWL4030_BBCHEN; |
560 | if (uvolt >= 3200000) |
561 | flags |= TWL4030_BBSEL_3V2; |
562 | else if (uvolt >= 3100000) |
563 | flags |= TWL4030_BBSEL_3V1; |
564 | else if (uvolt >= 3000000) |
565 | flags |= TWL4030_BBSEL_3V0; |
566 | else |
567 | flags |= TWL4030_BBSEL_2V5; |
568 | |
569 | if (uamp >= 1000) |
570 | flags |= TWL4030_BBISEL_1000uA; |
571 | else if (uamp >= 500) |
572 | flags |= TWL4030_BBISEL_500uA; |
573 | else if (uamp >= 150) |
574 | flags |= TWL4030_BBISEL_150uA; |
575 | else |
576 | flags |= TWL4030_BBISEL_25uA; |
577 | |
578 | ret = twl4030_clear_set(mod_no: TWL_MODULE_PM_RECEIVER, |
579 | TWL4030_BBSEL_MASK | TWL4030_BBISEL_MASK, |
580 | set: flags, |
581 | TWL4030_BB_CFG); |
582 | |
583 | return ret; |
584 | } |
585 | |
586 | /* |
587 | * TWL4030 CHG_PRES (AC charger presence) events |
588 | */ |
589 | static irqreturn_t twl4030_charger_interrupt(int irq, void *arg) |
590 | { |
591 | struct twl4030_bci *bci = arg; |
592 | |
593 | dev_dbg(bci->dev, "CHG_PRES irq\n" ); |
594 | /* reset current on each 'plug' event */ |
595 | bci->ac_cur = 500000; |
596 | twl4030_charger_update_current(bci); |
597 | power_supply_changed(psy: bci->ac); |
598 | power_supply_changed(psy: bci->usb); |
599 | |
600 | return IRQ_HANDLED; |
601 | } |
602 | |
603 | /* |
604 | * TWL4030 BCI monitoring events |
605 | */ |
606 | static irqreturn_t twl4030_bci_interrupt(int irq, void *arg) |
607 | { |
608 | struct twl4030_bci *bci = arg; |
609 | u8 irqs1, irqs2; |
610 | int ret; |
611 | |
612 | ret = twl_i2c_read_u8(mod_no: TWL4030_MODULE_INTERRUPTS, val: &irqs1, |
613 | TWL4030_INTERRUPTS_BCIISR1A); |
614 | if (ret < 0) |
615 | return IRQ_HANDLED; |
616 | |
617 | ret = twl_i2c_read_u8(mod_no: TWL4030_MODULE_INTERRUPTS, val: &irqs2, |
618 | TWL4030_INTERRUPTS_BCIISR2A); |
619 | if (ret < 0) |
620 | return IRQ_HANDLED; |
621 | |
622 | dev_dbg(bci->dev, "BCI irq %02x %02x\n" , irqs2, irqs1); |
623 | |
624 | if (irqs1 & (TWL4030_ICHGLOW | TWL4030_ICHGEOC)) { |
625 | /* charger state change, inform the core */ |
626 | power_supply_changed(psy: bci->ac); |
627 | power_supply_changed(psy: bci->usb); |
628 | } |
629 | twl4030_charger_update_current(bci); |
630 | |
631 | /* various monitoring events, for now we just log them here */ |
632 | if (irqs1 & (TWL4030_TBATOR2 | TWL4030_TBATOR1)) |
633 | dev_warn(bci->dev, "battery temperature out of range\n" ); |
634 | |
635 | if (irqs1 & TWL4030_BATSTS) |
636 | dev_crit(bci->dev, "battery disconnected\n" ); |
637 | |
638 | if (irqs2 & TWL4030_VBATOV) |
639 | dev_crit(bci->dev, "VBAT overvoltage\n" ); |
640 | |
641 | if (irqs2 & TWL4030_VBUSOV) |
642 | dev_crit(bci->dev, "VBUS overvoltage\n" ); |
643 | |
644 | if (irqs2 & TWL4030_ACCHGOV) |
645 | dev_crit(bci->dev, "Ac charger overvoltage\n" ); |
646 | |
647 | return IRQ_HANDLED; |
648 | } |
649 | |
650 | static void twl4030_bci_usb_work(struct work_struct *data) |
651 | { |
652 | struct twl4030_bci *bci = container_of(data, struct twl4030_bci, work); |
653 | |
654 | switch (bci->event) { |
655 | case USB_EVENT_VBUS: |
656 | case USB_EVENT_CHARGER: |
657 | twl4030_charger_enable_usb(bci, enable: true); |
658 | break; |
659 | case USB_EVENT_NONE: |
660 | twl4030_charger_enable_usb(bci, enable: false); |
661 | break; |
662 | } |
663 | } |
664 | |
665 | static int twl4030_bci_usb_ncb(struct notifier_block *nb, unsigned long val, |
666 | void *priv) |
667 | { |
668 | struct twl4030_bci *bci = container_of(nb, struct twl4030_bci, usb_nb); |
669 | |
670 | dev_dbg(bci->dev, "OTG notify %lu\n" , val); |
671 | |
672 | /* reset current on each 'plug' event */ |
673 | if (allow_usb) |
674 | bci->usb_cur_target = 500000; |
675 | else |
676 | bci->usb_cur_target = 100000; |
677 | |
678 | bci->event = val; |
679 | schedule_work(work: &bci->work); |
680 | |
681 | return NOTIFY_OK; |
682 | } |
683 | |
684 | /* |
685 | * sysfs charger enabled store |
686 | */ |
687 | static ssize_t |
688 | twl4030_bci_mode_store(struct device *dev, struct device_attribute *attr, |
689 | const char *buf, size_t n) |
690 | { |
691 | struct twl4030_bci *bci = dev_get_drvdata(dev: dev->parent); |
692 | int mode; |
693 | int status; |
694 | |
695 | mode = sysfs_match_string(modes, buf); |
696 | if (mode < 0) |
697 | return mode; |
698 | |
699 | if (dev == &bci->ac->dev) { |
700 | if (mode == 2) |
701 | return -EINVAL; |
702 | twl4030_charger_enable_ac(bci, enable: false); |
703 | bci->ac_mode = mode; |
704 | status = twl4030_charger_enable_ac(bci, enable: true); |
705 | } else { |
706 | twl4030_charger_enable_usb(bci, enable: false); |
707 | bci->usb_mode = mode; |
708 | status = twl4030_charger_enable_usb(bci, enable: true); |
709 | } |
710 | return (status == 0) ? n : status; |
711 | } |
712 | |
713 | /* |
714 | * sysfs charger enabled show |
715 | */ |
716 | static ssize_t |
717 | twl4030_bci_mode_show(struct device *dev, |
718 | struct device_attribute *attr, char *buf) |
719 | { |
720 | struct twl4030_bci *bci = dev_get_drvdata(dev: dev->parent); |
721 | int len = 0; |
722 | int i; |
723 | int mode = bci->usb_mode; |
724 | |
725 | if (dev == &bci->ac->dev) |
726 | mode = bci->ac_mode; |
727 | |
728 | for (i = 0; i < ARRAY_SIZE(modes); i++) |
729 | if (mode == i) |
730 | len += sysfs_emit_at(buf, at: len, fmt: "[%s] " , modes[i]); |
731 | else |
732 | len += sysfs_emit_at(buf, at: len, fmt: "%s " , modes[i]); |
733 | buf[len-1] = '\n'; |
734 | return len; |
735 | } |
736 | static DEVICE_ATTR(mode, 0644, twl4030_bci_mode_show, |
737 | twl4030_bci_mode_store); |
738 | |
739 | static int twl4030_charger_get_current(void) |
740 | { |
741 | int curr; |
742 | int ret; |
743 | u8 bcictl1; |
744 | |
745 | curr = twl4030bci_read_adc_val(TWL4030_BCIICHG); |
746 | if (curr < 0) |
747 | return curr; |
748 | |
749 | ret = twl4030_bci_read(TWL4030_BCICTL1, val: &bcictl1); |
750 | if (ret) |
751 | return ret; |
752 | |
753 | return regval2ua(regval: curr, cgain: bcictl1 & TWL4030_CGAIN); |
754 | } |
755 | |
756 | /* |
757 | * Returns the main charge FSM state |
758 | * Or < 0 on failure. |
759 | */ |
760 | static int twl4030bci_state(struct twl4030_bci *bci) |
761 | { |
762 | int ret; |
763 | u8 state; |
764 | |
765 | ret = twl4030_bci_read(TWL4030_BCIMSTATEC, val: &state); |
766 | if (ret) { |
767 | dev_err(bci->dev, "error reading BCIMSTATEC\n" ); |
768 | return ret; |
769 | } |
770 | |
771 | dev_dbg(bci->dev, "state: %02x\n" , state); |
772 | |
773 | return state; |
774 | } |
775 | |
776 | static int twl4030_bci_state_to_status(int state) |
777 | { |
778 | state &= TWL4030_MSTATEC_MASK; |
779 | if (TWL4030_MSTATEC_QUICK1 <= state && state <= TWL4030_MSTATEC_QUICK7) |
780 | return POWER_SUPPLY_STATUS_CHARGING; |
781 | else if (TWL4030_MSTATEC_COMPLETE1 <= state && |
782 | state <= TWL4030_MSTATEC_COMPLETE4) |
783 | return POWER_SUPPLY_STATUS_FULL; |
784 | else |
785 | return POWER_SUPPLY_STATUS_NOT_CHARGING; |
786 | } |
787 | |
788 | static int twl4030_bci_get_property(struct power_supply *psy, |
789 | enum power_supply_property psp, |
790 | union power_supply_propval *val) |
791 | { |
792 | struct twl4030_bci *bci = dev_get_drvdata(dev: psy->dev.parent); |
793 | int is_charging; |
794 | int state; |
795 | int ret; |
796 | |
797 | state = twl4030bci_state(bci); |
798 | if (state < 0) |
799 | return state; |
800 | |
801 | if (psy->desc->type == POWER_SUPPLY_TYPE_USB) |
802 | is_charging = state & TWL4030_MSTATEC_USB; |
803 | else |
804 | is_charging = state & TWL4030_MSTATEC_AC; |
805 | if (!is_charging) { |
806 | u8 s; |
807 | ret = twl4030_bci_read(TWL4030_BCIMDEN, val: &s); |
808 | if (ret < 0) |
809 | return ret; |
810 | if (psy->desc->type == POWER_SUPPLY_TYPE_USB) |
811 | is_charging = s & 1; |
812 | else |
813 | is_charging = s & 2; |
814 | if (is_charging) |
815 | /* A little white lie */ |
816 | state = TWL4030_MSTATEC_QUICK1; |
817 | } |
818 | |
819 | switch (psp) { |
820 | case POWER_SUPPLY_PROP_STATUS: |
821 | if (is_charging) |
822 | val->intval = twl4030_bci_state_to_status(state); |
823 | else |
824 | val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; |
825 | break; |
826 | case POWER_SUPPLY_PROP_VOLTAGE_NOW: |
827 | /* charging must be active for meaningful result */ |
828 | if (!is_charging) |
829 | return -ENODATA; |
830 | if (psy->desc->type == POWER_SUPPLY_TYPE_USB) { |
831 | ret = twl4030bci_read_adc_val(TWL4030_BCIVBUS); |
832 | if (ret < 0) |
833 | return ret; |
834 | /* BCIVBUS uses ADCIN8, 7/1023 V/step */ |
835 | val->intval = ret * 6843; |
836 | } else { |
837 | ret = twl4030bci_read_adc_val(TWL4030_BCIVAC); |
838 | if (ret < 0) |
839 | return ret; |
840 | /* BCIVAC uses ADCIN11, 10/1023 V/step */ |
841 | val->intval = ret * 9775; |
842 | } |
843 | break; |
844 | case POWER_SUPPLY_PROP_CURRENT_NOW: |
845 | if (!is_charging) |
846 | return -ENODATA; |
847 | /* current measurement is shared between AC and USB */ |
848 | ret = twl4030_charger_get_current(); |
849 | if (ret < 0) |
850 | return ret; |
851 | val->intval = ret; |
852 | break; |
853 | case POWER_SUPPLY_PROP_ONLINE: |
854 | val->intval = is_charging && |
855 | twl4030_bci_state_to_status(state) != |
856 | POWER_SUPPLY_STATUS_NOT_CHARGING; |
857 | break; |
858 | case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: |
859 | val->intval = -1; |
860 | if (psy->desc->type != POWER_SUPPLY_TYPE_USB) { |
861 | if (!bci->ac_is_active) |
862 | val->intval = bci->ac_cur; |
863 | } else { |
864 | if (bci->ac_is_active) |
865 | val->intval = bci->usb_cur_target; |
866 | } |
867 | if (val->intval < 0) { |
868 | u8 bcictl1; |
869 | |
870 | val->intval = twl4030bci_read_adc_val(TWL4030_BCIIREF1); |
871 | if (val->intval < 0) |
872 | return val->intval; |
873 | ret = twl4030_bci_read(TWL4030_BCICTL1, val: &bcictl1); |
874 | if (ret < 0) |
875 | return ret; |
876 | val->intval = regval2ua(regval: val->intval, cgain: bcictl1 & |
877 | TWL4030_CGAIN); |
878 | } |
879 | break; |
880 | default: |
881 | return -EINVAL; |
882 | } |
883 | |
884 | return 0; |
885 | } |
886 | |
887 | static int twl4030_bci_set_property(struct power_supply *psy, |
888 | enum power_supply_property psp, |
889 | const union power_supply_propval *val) |
890 | { |
891 | struct twl4030_bci *bci = dev_get_drvdata(dev: psy->dev.parent); |
892 | |
893 | switch (psp) { |
894 | case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: |
895 | if (psy->desc->type == POWER_SUPPLY_TYPE_USB) |
896 | bci->usb_cur_target = val->intval; |
897 | else |
898 | bci->ac_cur = val->intval; |
899 | twl4030_charger_update_current(bci); |
900 | break; |
901 | default: |
902 | return -EINVAL; |
903 | } |
904 | |
905 | return 0; |
906 | } |
907 | |
908 | static int twl4030_bci_property_is_writeable(struct power_supply *psy, |
909 | enum power_supply_property psp) |
910 | { |
911 | switch (psp) { |
912 | case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: |
913 | return true; |
914 | default: |
915 | return false; |
916 | } |
917 | } |
918 | |
919 | static enum power_supply_property twl4030_charger_props[] = { |
920 | POWER_SUPPLY_PROP_STATUS, |
921 | POWER_SUPPLY_PROP_ONLINE, |
922 | POWER_SUPPLY_PROP_VOLTAGE_NOW, |
923 | POWER_SUPPLY_PROP_CURRENT_NOW, |
924 | POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, |
925 | }; |
926 | |
927 | #ifdef CONFIG_OF |
928 | static const struct twl4030_bci_platform_data * |
929 | twl4030_bci_parse_dt(struct device *dev) |
930 | { |
931 | struct device_node *np = dev->of_node; |
932 | struct twl4030_bci_platform_data *pdata; |
933 | u32 num; |
934 | |
935 | if (!np) |
936 | return NULL; |
937 | pdata = devm_kzalloc(dev, size: sizeof(*pdata), GFP_KERNEL); |
938 | if (!pdata) |
939 | return pdata; |
940 | |
941 | if (of_property_read_u32(np, propname: "ti,bb-uvolt" , out_value: &num) == 0) |
942 | pdata->bb_uvolt = num; |
943 | if (of_property_read_u32(np, propname: "ti,bb-uamp" , out_value: &num) == 0) |
944 | pdata->bb_uamp = num; |
945 | return pdata; |
946 | } |
947 | #else |
948 | static inline const struct twl4030_bci_platform_data * |
949 | twl4030_bci_parse_dt(struct device *dev) |
950 | { |
951 | return NULL; |
952 | } |
953 | #endif |
954 | |
955 | static const struct power_supply_desc twl4030_bci_ac_desc = { |
956 | .name = "twl4030_ac" , |
957 | .type = POWER_SUPPLY_TYPE_MAINS, |
958 | .properties = twl4030_charger_props, |
959 | .num_properties = ARRAY_SIZE(twl4030_charger_props), |
960 | .get_property = twl4030_bci_get_property, |
961 | .set_property = twl4030_bci_set_property, |
962 | .property_is_writeable = twl4030_bci_property_is_writeable, |
963 | }; |
964 | |
965 | static const struct power_supply_desc twl4030_bci_usb_desc = { |
966 | .name = "twl4030_usb" , |
967 | .type = POWER_SUPPLY_TYPE_USB, |
968 | .properties = twl4030_charger_props, |
969 | .num_properties = ARRAY_SIZE(twl4030_charger_props), |
970 | .get_property = twl4030_bci_get_property, |
971 | .set_property = twl4030_bci_set_property, |
972 | .property_is_writeable = twl4030_bci_property_is_writeable, |
973 | }; |
974 | |
975 | static int twl4030_bci_probe(struct platform_device *pdev) |
976 | { |
977 | struct twl4030_bci *bci; |
978 | const struct twl4030_bci_platform_data *pdata = pdev->dev.platform_data; |
979 | int ret; |
980 | u32 reg; |
981 | |
982 | bci = devm_kzalloc(dev: &pdev->dev, size: sizeof(*bci), GFP_KERNEL); |
983 | if (bci == NULL) |
984 | return -ENOMEM; |
985 | |
986 | if (!pdata) |
987 | pdata = twl4030_bci_parse_dt(dev: &pdev->dev); |
988 | |
989 | bci->ichg_eoc = 80100; /* Stop charging when current drops to here */ |
990 | bci->ichg_lo = 241000; /* Low threshold */ |
991 | bci->ichg_hi = 500000; /* High threshold */ |
992 | bci->ac_cur = 500000; /* 500mA */ |
993 | if (allow_usb) |
994 | bci->usb_cur_target = 500000; /* 500mA */ |
995 | else |
996 | bci->usb_cur_target = 100000; /* 100mA */ |
997 | bci->usb_mode = CHARGE_AUTO; |
998 | bci->ac_mode = CHARGE_AUTO; |
999 | |
1000 | bci->dev = &pdev->dev; |
1001 | bci->irq_chg = platform_get_irq(pdev, 0); |
1002 | bci->irq_bci = platform_get_irq(pdev, 1); |
1003 | |
1004 | platform_set_drvdata(pdev, data: bci); |
1005 | |
1006 | INIT_WORK(&bci->work, twl4030_bci_usb_work); |
1007 | INIT_DELAYED_WORK(&bci->current_worker, twl4030_current_worker); |
1008 | |
1009 | bci->channel_vac = devm_iio_channel_get(dev: &pdev->dev, consumer_channel: "vac" ); |
1010 | if (IS_ERR(ptr: bci->channel_vac)) { |
1011 | ret = PTR_ERR(ptr: bci->channel_vac); |
1012 | if (ret == -EPROBE_DEFER) |
1013 | return ret; /* iio not ready */ |
1014 | dev_warn(&pdev->dev, "could not request vac iio channel (%d)" , |
1015 | ret); |
1016 | bci->channel_vac = NULL; |
1017 | } |
1018 | |
1019 | if (bci->dev->of_node) { |
1020 | struct device_node *phynode; |
1021 | |
1022 | phynode = of_get_compatible_child(parent: bci->dev->of_node->parent, |
1023 | compatible: "ti,twl4030-usb" ); |
1024 | if (phynode) { |
1025 | bci->usb_nb.notifier_call = twl4030_bci_usb_ncb; |
1026 | bci->transceiver = devm_usb_get_phy_by_node( |
1027 | dev: bci->dev, node: phynode, nb: &bci->usb_nb); |
1028 | of_node_put(node: phynode); |
1029 | if (IS_ERR(ptr: bci->transceiver)) { |
1030 | ret = PTR_ERR(ptr: bci->transceiver); |
1031 | if (ret == -EPROBE_DEFER) |
1032 | return ret; /* phy not ready */ |
1033 | dev_warn(&pdev->dev, "could not request transceiver (%d)" , |
1034 | ret); |
1035 | bci->transceiver = NULL; |
1036 | } |
1037 | } |
1038 | } |
1039 | |
1040 | bci->ac = devm_power_supply_register(parent: &pdev->dev, desc: &twl4030_bci_ac_desc, |
1041 | NULL); |
1042 | if (IS_ERR(ptr: bci->ac)) { |
1043 | ret = PTR_ERR(ptr: bci->ac); |
1044 | dev_err(&pdev->dev, "failed to register ac: %d\n" , ret); |
1045 | return ret; |
1046 | } |
1047 | |
1048 | bci->usb = devm_power_supply_register(parent: &pdev->dev, desc: &twl4030_bci_usb_desc, |
1049 | NULL); |
1050 | if (IS_ERR(ptr: bci->usb)) { |
1051 | ret = PTR_ERR(ptr: bci->usb); |
1052 | dev_err(&pdev->dev, "failed to register usb: %d\n" , ret); |
1053 | return ret; |
1054 | } |
1055 | |
1056 | ret = devm_request_threaded_irq(dev: &pdev->dev, irq: bci->irq_chg, NULL, |
1057 | thread_fn: twl4030_charger_interrupt, IRQF_ONESHOT, devname: pdev->name, |
1058 | dev_id: bci); |
1059 | if (ret < 0) { |
1060 | dev_err(&pdev->dev, "could not request irq %d, status %d\n" , |
1061 | bci->irq_chg, ret); |
1062 | return ret; |
1063 | } |
1064 | |
1065 | ret = devm_request_threaded_irq(dev: &pdev->dev, irq: bci->irq_bci, NULL, |
1066 | thread_fn: twl4030_bci_interrupt, IRQF_ONESHOT, devname: pdev->name, dev_id: bci); |
1067 | if (ret < 0) { |
1068 | dev_err(&pdev->dev, "could not request irq %d, status %d\n" , |
1069 | bci->irq_bci, ret); |
1070 | return ret; |
1071 | } |
1072 | |
1073 | /* Enable interrupts now. */ |
1074 | reg = ~(u32)(TWL4030_ICHGLOW | TWL4030_ICHGEOC | TWL4030_TBATOR2 | |
1075 | TWL4030_TBATOR1 | TWL4030_BATSTS); |
1076 | ret = twl_i2c_write_u8(mod_no: TWL4030_MODULE_INTERRUPTS, val: reg, |
1077 | TWL4030_INTERRUPTS_BCIIMR1A); |
1078 | if (ret < 0) { |
1079 | dev_err(&pdev->dev, "failed to unmask interrupts: %d\n" , ret); |
1080 | return ret; |
1081 | } |
1082 | |
1083 | reg = ~(u32)(TWL4030_VBATOV | TWL4030_VBUSOV | TWL4030_ACCHGOV); |
1084 | ret = twl_i2c_write_u8(mod_no: TWL4030_MODULE_INTERRUPTS, val: reg, |
1085 | TWL4030_INTERRUPTS_BCIIMR2A); |
1086 | if (ret < 0) |
1087 | dev_warn(&pdev->dev, "failed to unmask interrupts: %d\n" , ret); |
1088 | |
1089 | twl4030_charger_update_current(bci); |
1090 | if (device_create_file(device: &bci->usb->dev, entry: &dev_attr_mode)) |
1091 | dev_warn(&pdev->dev, "could not create sysfs file\n" ); |
1092 | if (device_create_file(device: &bci->ac->dev, entry: &dev_attr_mode)) |
1093 | dev_warn(&pdev->dev, "could not create sysfs file\n" ); |
1094 | |
1095 | twl4030_charger_enable_ac(bci, enable: true); |
1096 | if (!IS_ERR_OR_NULL(ptr: bci->transceiver)) |
1097 | twl4030_bci_usb_ncb(nb: &bci->usb_nb, |
1098 | val: bci->transceiver->last_event, |
1099 | NULL); |
1100 | else |
1101 | twl4030_charger_enable_usb(bci, enable: false); |
1102 | if (pdata) |
1103 | twl4030_charger_enable_backup(uvolt: pdata->bb_uvolt, |
1104 | uamp: pdata->bb_uamp); |
1105 | else |
1106 | twl4030_charger_enable_backup(uvolt: 0, uamp: 0); |
1107 | |
1108 | return 0; |
1109 | } |
1110 | |
1111 | static void twl4030_bci_remove(struct platform_device *pdev) |
1112 | { |
1113 | struct twl4030_bci *bci = platform_get_drvdata(pdev); |
1114 | |
1115 | twl4030_charger_enable_ac(bci, enable: false); |
1116 | twl4030_charger_enable_usb(bci, enable: false); |
1117 | twl4030_charger_enable_backup(uvolt: 0, uamp: 0); |
1118 | |
1119 | device_remove_file(dev: &bci->usb->dev, attr: &dev_attr_mode); |
1120 | device_remove_file(dev: &bci->ac->dev, attr: &dev_attr_mode); |
1121 | /* mask interrupts */ |
1122 | twl_i2c_write_u8(mod_no: TWL4030_MODULE_INTERRUPTS, val: 0xff, |
1123 | TWL4030_INTERRUPTS_BCIIMR1A); |
1124 | twl_i2c_write_u8(mod_no: TWL4030_MODULE_INTERRUPTS, val: 0xff, |
1125 | TWL4030_INTERRUPTS_BCIIMR2A); |
1126 | } |
1127 | |
1128 | static const struct of_device_id twl_bci_of_match[] __maybe_unused = { |
1129 | {.compatible = "ti,twl4030-bci" , }, |
1130 | { } |
1131 | }; |
1132 | MODULE_DEVICE_TABLE(of, twl_bci_of_match); |
1133 | |
1134 | static struct platform_driver twl4030_bci_driver = { |
1135 | .probe = twl4030_bci_probe, |
1136 | .remove_new = twl4030_bci_remove, |
1137 | .driver = { |
1138 | .name = "twl4030_bci" , |
1139 | .of_match_table = of_match_ptr(twl_bci_of_match), |
1140 | }, |
1141 | }; |
1142 | module_platform_driver(twl4030_bci_driver); |
1143 | |
1144 | MODULE_AUTHOR("Gražvydas Ignotas" ); |
1145 | MODULE_DESCRIPTION("TWL4030 Battery Charger Interface driver" ); |
1146 | MODULE_LICENSE("GPL" ); |
1147 | MODULE_ALIAS("platform:twl4030_bci" ); |
1148 | |