blob: 6fa928ed3128b8b64e1d9652359b35a207f464ab [file] [log] [blame]
Grazvydas Ignotas2e727f12010-09-27 23:18:24 +03001/*
2 * TWL4030/TPS65950 BCI (Battery Charger Interface) driver
3 *
4 * Copyright (C) 2010 Gražvydas Ignotas <notasas@gmail.com>
5 *
6 * based on twl4030_bci_battery.c by TI
7 * Copyright (C) 2008 Texas Instruments, Inc.
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 */
14
15#include <linux/init.h>
16#include <linux/module.h>
17#include <linux/slab.h>
Kishon Vijay Abraham Ided017e2012-06-26 17:40:32 +053018#include <linux/err.h>
Grazvydas Ignotas2e727f12010-09-27 23:18:24 +030019#include <linux/platform_device.h>
20#include <linux/interrupt.h>
21#include <linux/i2c/twl.h>
22#include <linux/power_supply.h>
23#include <linux/notifier.h>
24#include <linux/usb/otg.h>
NeilBrowne4ae5372015-07-30 10:11:24 +100025#include <linux/i2c/twl4030-madc.h>
Grazvydas Ignotas2e727f12010-09-27 23:18:24 +030026
27#define TWL4030_BCIMSTATEC 0x02
28#define TWL4030_BCIICHG 0x08
29#define TWL4030_BCIVAC 0x0a
30#define TWL4030_BCIVBUS 0x0c
Nishanth Menon61a77842014-05-28 16:46:49 -050031#define TWL4030_BCIMFSTS3 0x0F
Grazvydas Ignotas2e727f12010-09-27 23:18:24 +030032#define TWL4030_BCIMFSTS4 0x10
33#define TWL4030_BCICTL1 0x23
NeilBrown210d4bc2012-05-09 07:40:40 +100034#define TWL4030_BB_CFG 0x12
NeilBrown1098cb52015-07-30 10:11:24 +100035#define TWL4030_BCIIREF1 0x27
36#define TWL4030_BCIIREF2 0x28
37#define TWL4030_BCIMFKEY 0x11
38#define TWL4030_BCIMFTH8 0x1d
39#define TWL4030_BCIMFTH9 0x1e
Grazvydas Ignotas2e727f12010-09-27 23:18:24 +030040
Nishanth Menon61a77842014-05-28 16:46:49 -050041#define TWL4030_BCIMFSTS1 0x01
42
Grazvydas Ignotas2e727f12010-09-27 23:18:24 +030043#define TWL4030_BCIAUTOWEN BIT(5)
44#define TWL4030_CONFIG_DONE BIT(4)
45#define TWL4030_BCIAUTOUSB BIT(1)
46#define TWL4030_BCIAUTOAC BIT(0)
47#define TWL4030_CGAIN BIT(5)
48#define TWL4030_USBFASTMCHG BIT(2)
49#define TWL4030_STS_VBUS BIT(7)
50#define TWL4030_STS_USB_ID BIT(2)
NeilBrown210d4bc2012-05-09 07:40:40 +100051#define TWL4030_BBCHEN BIT(4)
Il Han94b05ca2012-08-17 16:49:43 +090052#define TWL4030_BBSEL_MASK 0x0c
53#define TWL4030_BBSEL_2V5 0x00
54#define TWL4030_BBSEL_3V0 0x04
55#define TWL4030_BBSEL_3V1 0x08
56#define TWL4030_BBSEL_3V2 0x0c
57#define TWL4030_BBISEL_MASK 0x03
58#define TWL4030_BBISEL_25uA 0x00
59#define TWL4030_BBISEL_150uA 0x01
60#define TWL4030_BBISEL_500uA 0x02
61#define TWL4030_BBISEL_1000uA 0x03
Grazvydas Ignotas2e727f12010-09-27 23:18:24 +030062
Nishanth Menon61a77842014-05-28 16:46:49 -050063#define TWL4030_BATSTSPCHG BIT(2)
64#define TWL4030_BATSTSMCHG BIT(6)
65
Grazvydas Ignotas2e727f12010-09-27 23:18:24 +030066/* BCI interrupts */
67#define TWL4030_WOVF BIT(0) /* Watchdog overflow */
68#define TWL4030_TMOVF BIT(1) /* Timer overflow */
69#define TWL4030_ICHGHIGH BIT(2) /* Battery charge current high */
70#define TWL4030_ICHGLOW BIT(3) /* Battery cc. low / FSM state change */
71#define TWL4030_ICHGEOC BIT(4) /* Battery current end-of-charge */
72#define TWL4030_TBATOR2 BIT(5) /* Battery temperature out of range 2 */
73#define TWL4030_TBATOR1 BIT(6) /* Battery temperature out of range 1 */
74#define TWL4030_BATSTS BIT(7) /* Battery status */
75
76#define TWL4030_VBATLVL BIT(0) /* VBAT level */
77#define TWL4030_VBATOV BIT(1) /* VBAT overvoltage */
78#define TWL4030_VBUSOV BIT(2) /* VBUS overvoltage */
79#define TWL4030_ACCHGOV BIT(3) /* Ac charger overvoltage */
80
81#define TWL4030_MSTATEC_USB BIT(4)
82#define TWL4030_MSTATEC_AC BIT(5)
83#define TWL4030_MSTATEC_MASK 0x0f
84#define TWL4030_MSTATEC_QUICK1 0x02
85#define TWL4030_MSTATEC_QUICK7 0x07
86#define TWL4030_MSTATEC_COMPLETE1 0x0b
87#define TWL4030_MSTATEC_COMPLETE4 0x0e
88
89static bool allow_usb;
Grazvydas Ignotas28d48f02011-06-03 14:43:11 +030090module_param(allow_usb, bool, 0644);
Grazvydas Ignotas2e727f12010-09-27 23:18:24 +030091MODULE_PARM_DESC(allow_usb, "Allow USB charge drawing default current");
92
93struct twl4030_bci {
94 struct device *dev;
Krzysztof Kozlowski297d7162015-03-12 08:44:11 +010095 struct power_supply *ac;
96 struct power_supply *usb;
Heikki Krogerus86753812012-02-13 13:24:02 +020097 struct usb_phy *transceiver;
Heikki Krogerusfcc8ebc2012-02-13 13:24:16 +020098 struct notifier_block usb_nb;
Heikki Krogerusd6ccc442011-02-25 13:56:36 +020099 struct work_struct work;
Grazvydas Ignotas2e727f12010-09-27 23:18:24 +0300100 int irq_chg;
101 int irq_bci;
NeilBrownab378132012-05-09 07:40:40 +1000102 int usb_enabled;
Heikki Krogerusd6ccc442011-02-25 13:56:36 +0200103
NeilBrown1098cb52015-07-30 10:11:24 +1000104 /*
NeilBrowne4ae5372015-07-30 10:11:24 +1000105 * ichg_* and *_cur values in uA. If any are 'large', we set
106 * CGAIN to '1' which doubles the range for half the
107 * precision.
NeilBrown1098cb52015-07-30 10:11:24 +1000108 */
NeilBrowne4ae5372015-07-30 10:11:24 +1000109 unsigned int ichg_eoc, ichg_lo, ichg_hi;
110 unsigned int usb_cur, ac_cur;
111 bool ac_is_active;
NeilBrown22d4c332015-07-30 10:11:24 +1000112 int usb_mode; /* charging mode requested */
113#define CHARGE_OFF 0
114#define CHARGE_AUTO 1
NeilBrown1098cb52015-07-30 10:11:24 +1000115
Heikki Krogerusd6ccc442011-02-25 13:56:36 +0200116 unsigned long event;
Grazvydas Ignotas2e727f12010-09-27 23:18:24 +0300117};
118
NeilBrown22d4c332015-07-30 10:11:24 +1000119/* strings for 'usb_mode' values */
120static char *modes[] = { "off", "auto" };
121
Grazvydas Ignotas2e727f12010-09-27 23:18:24 +0300122/*
123 * clear and set bits on an given register on a given module
124 */
125static int twl4030_clear_set(u8 mod_no, u8 clear, u8 set, u8 reg)
126{
127 u8 val = 0;
128 int ret;
129
130 ret = twl_i2c_read_u8(mod_no, &val, reg);
131 if (ret)
132 return ret;
133
134 val &= ~clear;
135 val |= set;
136
137 return twl_i2c_write_u8(mod_no, val, reg);
138}
139
140static int twl4030_bci_read(u8 reg, u8 *val)
141{
Peter Ujfalusie9f14c12012-11-13 10:42:11 +0100142 return twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, val, reg);
Grazvydas Ignotas2e727f12010-09-27 23:18:24 +0300143}
144
145static int twl4030_clear_set_boot_bci(u8 clear, u8 set)
146{
Peter Ujfalusie9f14c12012-11-13 10:42:11 +0100147 return twl4030_clear_set(TWL_MODULE_PM_MASTER, clear,
Grazvydas Ignotas2e727f12010-09-27 23:18:24 +0300148 TWL4030_CONFIG_DONE | TWL4030_BCIAUTOWEN | set,
149 TWL4030_PM_MASTER_BOOT_BCI);
150}
151
152static int twl4030bci_read_adc_val(u8 reg)
153{
154 int ret, temp;
155 u8 val;
156
157 /* read MSB */
158 ret = twl4030_bci_read(reg + 1, &val);
159 if (ret)
160 return ret;
161
162 temp = (int)(val & 0x03) << 8;
163
164 /* read LSB */
165 ret = twl4030_bci_read(reg, &val);
166 if (ret)
167 return ret;
168
169 return temp | val;
170}
171
172/*
Nishanth Menon61a77842014-05-28 16:46:49 -0500173 * Check if Battery Pack was present
174 */
175static int twl4030_is_battery_present(struct twl4030_bci *bci)
176{
177 int ret;
178 u8 val = 0;
179
180 /* Battery presence in Main charge? */
181 ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, &val, TWL4030_BCIMFSTS3);
182 if (ret)
183 return ret;
184 if (val & TWL4030_BATSTSMCHG)
185 return 0;
186
187 /*
188 * OK, It could be that bootloader did not enable main charger,
189 * pre-charge is h/w auto. So, Battery presence in Pre-charge?
190 */
191 ret = twl_i2c_read_u8(TWL4030_MODULE_PRECHARGE, &val,
192 TWL4030_BCIMFSTS1);
193 if (ret)
194 return ret;
195 if (val & TWL4030_BATSTSPCHG)
196 return 0;
197
198 return -ENODEV;
199}
200
201/*
NeilBrown3b542f02015-07-30 10:11:24 +1000202 * TI provided formulas:
203 * CGAIN == 0: ICHG = (BCIICHG * 1.7) / (2^10 - 1) - 0.85
204 * CGAIN == 1: ICHG = (BCIICHG * 3.4) / (2^10 - 1) - 1.7
205 * Here we use integer approximation of:
206 * CGAIN == 0: val * 1.6618 - 0.85 * 1000
207 * CGAIN == 1: (val * 1.6618 - 0.85 * 1000) * 2
208 */
209/*
210 * convert twl register value for currents into uA
211 */
212static int regval2ua(int regval, bool cgain)
213{
214 if (cgain)
215 return (regval * 16618 - 8500 * 1000) / 5;
216 else
217 return (regval * 16618 - 8500 * 1000) / 10;
218}
219
220/*
221 * convert uA currents into twl register value
222 */
223static int ua2regval(int ua, bool cgain)
224{
225 int ret;
226 if (cgain)
227 ua /= 2;
228 ret = (ua * 10 + 8500 * 1000) / 16618;
229 /* rounding problems */
230 if (ret < 512)
231 ret = 512;
232 return ret;
233}
234
NeilBrown1098cb52015-07-30 10:11:24 +1000235static int twl4030_charger_update_current(struct twl4030_bci *bci)
236{
237 int status;
NeilBrowne4ae5372015-07-30 10:11:24 +1000238 int cur;
NeilBrown1098cb52015-07-30 10:11:24 +1000239 unsigned reg, cur_reg;
240 u8 bcictl1, oldreg, fullreg;
241 bool cgain = false;
242 u8 boot_bci;
243
NeilBrowne4ae5372015-07-30 10:11:24 +1000244 /*
245 * If AC (Accessory Charger) voltage exceeds 4.5V (MADC 11)
246 * and AC is enabled, set current for 'ac'
247 */
248 if (twl4030_get_madc_conversion(11) > 4500) {
249 cur = bci->ac_cur;
250 bci->ac_is_active = true;
251 } else {
252 cur = bci->usb_cur;
253 bci->ac_is_active = false;
254 }
255
NeilBrown1098cb52015-07-30 10:11:24 +1000256 /* First, check thresholds and see if cgain is needed */
257 if (bci->ichg_eoc >= 200000)
258 cgain = true;
259 if (bci->ichg_lo >= 400000)
260 cgain = true;
261 if (bci->ichg_hi >= 820000)
262 cgain = true;
NeilBrowne4ae5372015-07-30 10:11:24 +1000263 if (cur > 852000)
NeilBrown1098cb52015-07-30 10:11:24 +1000264 cgain = true;
265
266 status = twl4030_bci_read(TWL4030_BCICTL1, &bcictl1);
267 if (status < 0)
268 return status;
269 if (twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &boot_bci,
270 TWL4030_PM_MASTER_BOOT_BCI) < 0)
271 boot_bci = 0;
272 boot_bci &= 7;
273
274 if ((!!cgain) != !!(bcictl1 & TWL4030_CGAIN))
275 /* Need to turn for charging while we change the
276 * CGAIN bit. Leave it off while everything is
277 * updated.
278 */
279 twl4030_clear_set_boot_bci(boot_bci, 0);
280
281 /*
282 * For ichg_eoc, the hardware only supports reg values matching
283 * 100XXXX000, and requires the XXXX be stored in the high nibble
284 * of TWL4030_BCIMFTH8.
285 */
286 reg = ua2regval(bci->ichg_eoc, cgain);
287 if (reg > 0x278)
288 reg = 0x278;
289 if (reg < 0x200)
290 reg = 0x200;
291 reg = (reg >> 3) & 0xf;
292 fullreg = reg << 4;
293
294 /*
295 * For ichg_lo, reg value must match 10XXXX0000.
296 * XXXX is stored in low nibble of TWL4030_BCIMFTH8.
297 */
298 reg = ua2regval(bci->ichg_lo, cgain);
299 if (reg > 0x2F0)
300 reg = 0x2F0;
301 if (reg < 0x200)
302 reg = 0x200;
303 reg = (reg >> 4) & 0xf;
304 fullreg |= reg;
305
306 /* ichg_eoc and ichg_lo live in same register */
307 status = twl4030_bci_read(TWL4030_BCIMFTH8, &oldreg);
308 if (status < 0)
309 return status;
310 if (oldreg != fullreg) {
311 status = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0xF4,
312 TWL4030_BCIMFKEY);
313 if (status < 0)
314 return status;
315 twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE,
316 fullreg, TWL4030_BCIMFTH8);
317 }
318
319 /* ichg_hi threshold must be 1XXXX01100 (I think) */
320 reg = ua2regval(bci->ichg_hi, cgain);
321 if (reg > 0x3E0)
322 reg = 0x3E0;
323 if (reg < 0x200)
324 reg = 0x200;
325 fullreg = (reg >> 5) & 0xF;
326 fullreg <<= 4;
327 status = twl4030_bci_read(TWL4030_BCIMFTH9, &oldreg);
328 if (status < 0)
329 return status;
330 if ((oldreg & 0xF0) != fullreg) {
331 fullreg |= (oldreg & 0x0F);
332 status = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0xE7,
333 TWL4030_BCIMFKEY);
334 if (status < 0)
335 return status;
336 twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE,
337 fullreg, TWL4030_BCIMFTH9);
338 }
339
340 /*
341 * And finally, set the current. This is stored in
342 * two registers.
343 */
NeilBrowne4ae5372015-07-30 10:11:24 +1000344 reg = ua2regval(cur, cgain);
NeilBrown1098cb52015-07-30 10:11:24 +1000345 /* we have only 10 bits */
346 if (reg > 0x3ff)
347 reg = 0x3ff;
348 status = twl4030_bci_read(TWL4030_BCIIREF1, &oldreg);
349 if (status < 0)
350 return status;
351 cur_reg = oldreg;
352 status = twl4030_bci_read(TWL4030_BCIIREF2, &oldreg);
353 if (status < 0)
354 return status;
355 cur_reg |= oldreg << 8;
356 if (reg != oldreg) {
357 /* disable write protection for one write access for
358 * BCIIREF */
359 status = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0xE7,
360 TWL4030_BCIMFKEY);
361 if (status < 0)
362 return status;
363 status = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE,
364 (reg & 0x100) ? 3 : 2,
365 TWL4030_BCIIREF2);
366 if (status < 0)
367 return status;
368 /* disable write protection for one write access for
369 * BCIIREF */
370 status = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0xE7,
371 TWL4030_BCIMFKEY);
372 if (status < 0)
373 return status;
374 status = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE,
375 reg & 0xff,
376 TWL4030_BCIIREF1);
377 }
378 if ((!!cgain) != !!(bcictl1 & TWL4030_CGAIN)) {
379 /* Flip CGAIN and re-enable charging */
380 bcictl1 ^= TWL4030_CGAIN;
381 twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE,
382 bcictl1, TWL4030_BCICTL1);
383 twl4030_clear_set_boot_bci(0, boot_bci);
384 }
385 return 0;
386}
387
NeilBrown3b542f02015-07-30 10:11:24 +1000388/*
NeilBrown9c645d22012-05-09 07:40:39 +1000389 * Enable/Disable USB Charge functionality.
Grazvydas Ignotas2e727f12010-09-27 23:18:24 +0300390 */
391static int twl4030_charger_enable_usb(struct twl4030_bci *bci, bool enable)
392{
393 int ret;
394
NeilBrown22d4c332015-07-30 10:11:24 +1000395 if (bci->usb_mode == CHARGE_OFF)
396 enable = false;
NeilBrowne57c4a62015-07-30 10:11:24 +1000397 if (enable && !IS_ERR_OR_NULL(bci->transceiver)) {
Grazvydas Ignotas2e727f12010-09-27 23:18:24 +0300398
NeilBrowne4ae5372015-07-30 10:11:24 +1000399 twl4030_charger_update_current(bci);
400
NeilBrowne57c4a62015-07-30 10:11:24 +1000401 /* Need to keep phy powered */
NeilBrownab378132012-05-09 07:40:40 +1000402 if (!bci->usb_enabled) {
NeilBrowne57c4a62015-07-30 10:11:24 +1000403 pm_runtime_get_sync(bci->transceiver->dev);
NeilBrownab378132012-05-09 07:40:40 +1000404 bci->usb_enabled = 1;
405 }
406
Grazvydas Ignotas2e727f12010-09-27 23:18:24 +0300407 /* forcing the field BCIAUTOUSB (BOOT_BCI[1]) to 1 */
408 ret = twl4030_clear_set_boot_bci(0, TWL4030_BCIAUTOUSB);
409 if (ret < 0)
410 return ret;
411
412 /* forcing USBFASTMCHG(BCIMFSTS4[2]) to 1 */
Peter Ujfalusie9f14c12012-11-13 10:42:11 +0100413 ret = twl4030_clear_set(TWL_MODULE_MAIN_CHARGE, 0,
Grazvydas Ignotas2e727f12010-09-27 23:18:24 +0300414 TWL4030_USBFASTMCHG, TWL4030_BCIMFSTS4);
415 } else {
416 ret = twl4030_clear_set_boot_bci(TWL4030_BCIAUTOUSB, 0);
NeilBrownab378132012-05-09 07:40:40 +1000417 if (bci->usb_enabled) {
NeilBrowne57c4a62015-07-30 10:11:24 +1000418 pm_runtime_mark_last_busy(bci->transceiver->dev);
419 pm_runtime_put_autosuspend(bci->transceiver->dev);
NeilBrownab378132012-05-09 07:40:40 +1000420 bci->usb_enabled = 0;
421 }
Grazvydas Ignotas2e727f12010-09-27 23:18:24 +0300422 }
423
424 return ret;
425}
426
427/*
428 * Enable/Disable AC Charge funtionality.
429 */
430static int twl4030_charger_enable_ac(bool enable)
431{
432 int ret;
433
434 if (enable)
435 ret = twl4030_clear_set_boot_bci(0, TWL4030_BCIAUTOAC);
436 else
437 ret = twl4030_clear_set_boot_bci(TWL4030_BCIAUTOAC, 0);
438
439 return ret;
440}
441
442/*
NeilBrown210d4bc2012-05-09 07:40:40 +1000443 * Enable/Disable charging of Backup Battery.
444 */
445static int twl4030_charger_enable_backup(int uvolt, int uamp)
446{
447 int ret;
448 u8 flags;
449
450 if (uvolt < 2500000 ||
451 uamp < 25) {
452 /* disable charging of backup battery */
Peter Ujfalusie9f14c12012-11-13 10:42:11 +0100453 ret = twl4030_clear_set(TWL_MODULE_PM_RECEIVER,
NeilBrown210d4bc2012-05-09 07:40:40 +1000454 TWL4030_BBCHEN, 0, TWL4030_BB_CFG);
455 return ret;
456 }
457
458 flags = TWL4030_BBCHEN;
459 if (uvolt >= 3200000)
460 flags |= TWL4030_BBSEL_3V2;
461 else if (uvolt >= 3100000)
462 flags |= TWL4030_BBSEL_3V1;
463 else if (uvolt >= 3000000)
464 flags |= TWL4030_BBSEL_3V0;
465 else
466 flags |= TWL4030_BBSEL_2V5;
467
468 if (uamp >= 1000)
469 flags |= TWL4030_BBISEL_1000uA;
470 else if (uamp >= 500)
471 flags |= TWL4030_BBISEL_500uA;
472 else if (uamp >= 150)
473 flags |= TWL4030_BBISEL_150uA;
474 else
475 flags |= TWL4030_BBISEL_25uA;
476
Peter Ujfalusie9f14c12012-11-13 10:42:11 +0100477 ret = twl4030_clear_set(TWL_MODULE_PM_RECEIVER,
NeilBrown210d4bc2012-05-09 07:40:40 +1000478 TWL4030_BBSEL_MASK | TWL4030_BBISEL_MASK,
479 flags,
480 TWL4030_BB_CFG);
481
482 return ret;
483}
484
485/*
Grazvydas Ignotas2e727f12010-09-27 23:18:24 +0300486 * TWL4030 CHG_PRES (AC charger presence) events
487 */
488static irqreturn_t twl4030_charger_interrupt(int irq, void *arg)
489{
490 struct twl4030_bci *bci = arg;
491
492 dev_dbg(bci->dev, "CHG_PRES irq\n");
NeilBrownaca3c352015-07-30 10:11:24 +1000493 /* reset current on each 'plug' event */
494 bci->ac_cur = 500000;
NeilBrowne4ae5372015-07-30 10:11:24 +1000495 twl4030_charger_update_current(bci);
Krzysztof Kozlowski297d7162015-03-12 08:44:11 +0100496 power_supply_changed(bci->ac);
497 power_supply_changed(bci->usb);
Grazvydas Ignotas2e727f12010-09-27 23:18:24 +0300498
499 return IRQ_HANDLED;
500}
501
502/*
503 * TWL4030 BCI monitoring events
504 */
505static irqreturn_t twl4030_bci_interrupt(int irq, void *arg)
506{
507 struct twl4030_bci *bci = arg;
508 u8 irqs1, irqs2;
509 int ret;
510
511 ret = twl_i2c_read_u8(TWL4030_MODULE_INTERRUPTS, &irqs1,
512 TWL4030_INTERRUPTS_BCIISR1A);
513 if (ret < 0)
514 return IRQ_HANDLED;
515
516 ret = twl_i2c_read_u8(TWL4030_MODULE_INTERRUPTS, &irqs2,
517 TWL4030_INTERRUPTS_BCIISR2A);
518 if (ret < 0)
519 return IRQ_HANDLED;
520
521 dev_dbg(bci->dev, "BCI irq %02x %02x\n", irqs2, irqs1);
522
523 if (irqs1 & (TWL4030_ICHGLOW | TWL4030_ICHGEOC)) {
524 /* charger state change, inform the core */
Krzysztof Kozlowski297d7162015-03-12 08:44:11 +0100525 power_supply_changed(bci->ac);
526 power_supply_changed(bci->usb);
Grazvydas Ignotas2e727f12010-09-27 23:18:24 +0300527 }
NeilBrowne4ae5372015-07-30 10:11:24 +1000528 twl4030_charger_update_current(bci);
Grazvydas Ignotas2e727f12010-09-27 23:18:24 +0300529
530 /* various monitoring events, for now we just log them here */
531 if (irqs1 & (TWL4030_TBATOR2 | TWL4030_TBATOR1))
532 dev_warn(bci->dev, "battery temperature out of range\n");
533
534 if (irqs1 & TWL4030_BATSTS)
535 dev_crit(bci->dev, "battery disconnected\n");
536
537 if (irqs2 & TWL4030_VBATOV)
538 dev_crit(bci->dev, "VBAT overvoltage\n");
539
540 if (irqs2 & TWL4030_VBUSOV)
541 dev_crit(bci->dev, "VBUS overvoltage\n");
542
543 if (irqs2 & TWL4030_ACCHGOV)
544 dev_crit(bci->dev, "Ac charger overvoltage\n");
545
546 return IRQ_HANDLED;
547}
548
NeilBrownaca3c352015-07-30 10:11:24 +1000549/*
550 * Provide "max_current" attribute in sysfs.
551 */
552static ssize_t
553twl4030_bci_max_current_store(struct device *dev, struct device_attribute *attr,
554 const char *buf, size_t n)
555{
556 struct twl4030_bci *bci = dev_get_drvdata(dev->parent);
557 int cur = 0;
558 int status = 0;
559 status = kstrtoint(buf, 10, &cur);
560 if (status)
561 return status;
562 if (cur < 0)
563 return -EINVAL;
564 if (dev == &bci->ac->dev)
565 bci->ac_cur = cur;
566 else
567 bci->usb_cur = cur;
568
569 twl4030_charger_update_current(bci);
570 return n;
571}
572
573/*
574 * sysfs max_current show
575 */
576static ssize_t twl4030_bci_max_current_show(struct device *dev,
577 struct device_attribute *attr, char *buf)
578{
579 int status = 0;
580 int cur = -1;
581 u8 bcictl1;
582 struct twl4030_bci *bci = dev_get_drvdata(dev->parent);
583
584 if (dev == &bci->ac->dev) {
585 if (!bci->ac_is_active)
586 cur = bci->ac_cur;
587 } else {
588 if (bci->ac_is_active)
589 cur = bci->usb_cur;
590 }
591 if (cur < 0) {
592 cur = twl4030bci_read_adc_val(TWL4030_BCIIREF1);
593 if (cur < 0)
594 return cur;
595 status = twl4030_bci_read(TWL4030_BCICTL1, &bcictl1);
596 if (status < 0)
597 return status;
598 cur = regval2ua(cur, bcictl1 & TWL4030_CGAIN);
599 }
600 return scnprintf(buf, PAGE_SIZE, "%u\n", cur);
601}
602
603static DEVICE_ATTR(max_current, 0644, twl4030_bci_max_current_show,
604 twl4030_bci_max_current_store);
605
Heikki Krogerusd6ccc442011-02-25 13:56:36 +0200606static void twl4030_bci_usb_work(struct work_struct *data)
Grazvydas Ignotas2e727f12010-09-27 23:18:24 +0300607{
Heikki Krogerusd6ccc442011-02-25 13:56:36 +0200608 struct twl4030_bci *bci = container_of(data, struct twl4030_bci, work);
Grazvydas Ignotas2e727f12010-09-27 23:18:24 +0300609
Heikki Krogerusd6ccc442011-02-25 13:56:36 +0200610 switch (bci->event) {
Grazvydas Ignotas2e727f12010-09-27 23:18:24 +0300611 case USB_EVENT_VBUS:
612 case USB_EVENT_CHARGER:
613 twl4030_charger_enable_usb(bci, true);
614 break;
615 case USB_EVENT_NONE:
616 twl4030_charger_enable_usb(bci, false);
617 break;
618 }
Heikki Krogerusd6ccc442011-02-25 13:56:36 +0200619}
620
621static int twl4030_bci_usb_ncb(struct notifier_block *nb, unsigned long val,
622 void *priv)
623{
Heikki Krogerusfcc8ebc2012-02-13 13:24:16 +0200624 struct twl4030_bci *bci = container_of(nb, struct twl4030_bci, usb_nb);
Heikki Krogerusd6ccc442011-02-25 13:56:36 +0200625
626 dev_dbg(bci->dev, "OTG notify %lu\n", val);
627
NeilBrownaca3c352015-07-30 10:11:24 +1000628 /* reset current on each 'plug' event */
629 if (allow_usb)
630 bci->usb_cur = 500000;
631 else
632 bci->usb_cur = 100000;
633
Heikki Krogerusd6ccc442011-02-25 13:56:36 +0200634 bci->event = val;
635 schedule_work(&bci->work);
Grazvydas Ignotas2e727f12010-09-27 23:18:24 +0300636
637 return NOTIFY_OK;
638}
639
NeilBrown22d4c332015-07-30 10:11:24 +1000640/*
641 * sysfs charger enabled store
642 */
643static ssize_t
644twl4030_bci_mode_store(struct device *dev, struct device_attribute *attr,
645 const char *buf, size_t n)
646{
647 struct twl4030_bci *bci = dev_get_drvdata(dev->parent);
648 int mode;
649 int status;
650
651 if (sysfs_streq(buf, modes[0]))
652 mode = 0;
653 else if (sysfs_streq(buf, modes[1]))
654 mode = 1;
655 else
656 return -EINVAL;
657 twl4030_charger_enable_usb(bci, false);
658 bci->usb_mode = mode;
659 status = twl4030_charger_enable_usb(bci, true);
660 return (status == 0) ? n : status;
661}
662
663/*
664 * sysfs charger enabled show
665 */
666static ssize_t
667twl4030_bci_mode_show(struct device *dev,
668 struct device_attribute *attr, char *buf)
669{
670 struct twl4030_bci *bci = dev_get_drvdata(dev->parent);
671 int len = 0;
672 int i;
673
674 for (i = 0; i < ARRAY_SIZE(modes); i++)
675 if (bci->usb_mode == i)
676 len += snprintf(buf+len, PAGE_SIZE-len,
677 "[%s] ", modes[i]);
678 else
679 len += snprintf(buf+len, PAGE_SIZE-len,
680 "%s ", modes[i]);
681 buf[len-1] = '\n';
682 return len;
683}
684static DEVICE_ATTR(mode, 0644, twl4030_bci_mode_show,
685 twl4030_bci_mode_store);
686
Grazvydas Ignotas2e727f12010-09-27 23:18:24 +0300687static int twl4030_charger_get_current(void)
688{
689 int curr;
690 int ret;
691 u8 bcictl1;
692
693 curr = twl4030bci_read_adc_val(TWL4030_BCIICHG);
694 if (curr < 0)
695 return curr;
696
697 ret = twl4030_bci_read(TWL4030_BCICTL1, &bcictl1);
698 if (ret)
699 return ret;
700
NeilBrown3b542f02015-07-30 10:11:24 +1000701 return regval2ua(curr, bcictl1 & TWL4030_CGAIN);
Grazvydas Ignotas2e727f12010-09-27 23:18:24 +0300702}
703
704/*
705 * Returns the main charge FSM state
706 * Or < 0 on failure.
707 */
708static int twl4030bci_state(struct twl4030_bci *bci)
709{
710 int ret;
711 u8 state;
712
713 ret = twl4030_bci_read(TWL4030_BCIMSTATEC, &state);
714 if (ret) {
715 pr_err("twl4030_bci: error reading BCIMSTATEC\n");
716 return ret;
717 }
718
719 dev_dbg(bci->dev, "state: %02x\n", state);
720
721 return state;
722}
723
724static int twl4030_bci_state_to_status(int state)
725{
726 state &= TWL4030_MSTATEC_MASK;
727 if (TWL4030_MSTATEC_QUICK1 <= state && state <= TWL4030_MSTATEC_QUICK7)
728 return POWER_SUPPLY_STATUS_CHARGING;
729 else if (TWL4030_MSTATEC_COMPLETE1 <= state &&
730 state <= TWL4030_MSTATEC_COMPLETE4)
731 return POWER_SUPPLY_STATUS_FULL;
732 else
733 return POWER_SUPPLY_STATUS_NOT_CHARGING;
734}
735
736static int twl4030_bci_get_property(struct power_supply *psy,
737 enum power_supply_property psp,
738 union power_supply_propval *val)
739{
Krzysztof Kozlowski297d7162015-03-12 08:44:11 +0100740 struct twl4030_bci *bci = dev_get_drvdata(psy->dev.parent);
Grazvydas Ignotas2e727f12010-09-27 23:18:24 +0300741 int is_charging;
742 int state;
743 int ret;
744
745 state = twl4030bci_state(bci);
746 if (state < 0)
747 return state;
748
Krzysztof Kozlowski297d7162015-03-12 08:44:11 +0100749 if (psy->desc->type == POWER_SUPPLY_TYPE_USB)
Grazvydas Ignotas2e727f12010-09-27 23:18:24 +0300750 is_charging = state & TWL4030_MSTATEC_USB;
751 else
752 is_charging = state & TWL4030_MSTATEC_AC;
753
754 switch (psp) {
755 case POWER_SUPPLY_PROP_STATUS:
756 if (is_charging)
757 val->intval = twl4030_bci_state_to_status(state);
758 else
759 val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
760 break;
761 case POWER_SUPPLY_PROP_VOLTAGE_NOW:
762 /* charging must be active for meaningful result */
763 if (!is_charging)
764 return -ENODATA;
Krzysztof Kozlowski297d7162015-03-12 08:44:11 +0100765 if (psy->desc->type == POWER_SUPPLY_TYPE_USB) {
Grazvydas Ignotas2e727f12010-09-27 23:18:24 +0300766 ret = twl4030bci_read_adc_val(TWL4030_BCIVBUS);
767 if (ret < 0)
768 return ret;
769 /* BCIVBUS uses ADCIN8, 7/1023 V/step */
770 val->intval = ret * 6843;
771 } else {
772 ret = twl4030bci_read_adc_val(TWL4030_BCIVAC);
773 if (ret < 0)
774 return ret;
775 /* BCIVAC uses ADCIN11, 10/1023 V/step */
776 val->intval = ret * 9775;
777 }
778 break;
779 case POWER_SUPPLY_PROP_CURRENT_NOW:
780 if (!is_charging)
781 return -ENODATA;
782 /* current measurement is shared between AC and USB */
783 ret = twl4030_charger_get_current();
784 if (ret < 0)
785 return ret;
786 val->intval = ret;
787 break;
788 case POWER_SUPPLY_PROP_ONLINE:
789 val->intval = is_charging &&
790 twl4030_bci_state_to_status(state) !=
791 POWER_SUPPLY_STATUS_NOT_CHARGING;
792 break;
793 default:
794 return -EINVAL;
795 }
796
797 return 0;
798}
799
800static enum power_supply_property twl4030_charger_props[] = {
801 POWER_SUPPLY_PROP_STATUS,
802 POWER_SUPPLY_PROP_ONLINE,
803 POWER_SUPPLY_PROP_VOLTAGE_NOW,
804 POWER_SUPPLY_PROP_CURRENT_NOW,
805};
806
NeilBrownec0b3802013-10-31 17:05:50 +1100807#ifdef CONFIG_OF
808static const struct twl4030_bci_platform_data *
809twl4030_bci_parse_dt(struct device *dev)
810{
811 struct device_node *np = dev->of_node;
812 struct twl4030_bci_platform_data *pdata;
813 u32 num;
814
815 if (!np)
816 return NULL;
817 pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
818 if (!pdata)
819 return pdata;
820
821 if (of_property_read_u32(np, "ti,bb-uvolt", &num) == 0)
822 pdata->bb_uvolt = num;
823 if (of_property_read_u32(np, "ti,bb-uamp", &num) == 0)
824 pdata->bb_uamp = num;
825 return pdata;
826}
827#else
828static inline const struct twl4030_bci_platform_data *
829twl4030_bci_parse_dt(struct device *dev)
830{
831 return NULL;
832}
833#endif
834
Krzysztof Kozlowski297d7162015-03-12 08:44:11 +0100835static const struct power_supply_desc twl4030_bci_ac_desc = {
836 .name = "twl4030_ac",
837 .type = POWER_SUPPLY_TYPE_MAINS,
838 .properties = twl4030_charger_props,
839 .num_properties = ARRAY_SIZE(twl4030_charger_props),
840 .get_property = twl4030_bci_get_property,
841};
842
843static const struct power_supply_desc twl4030_bci_usb_desc = {
844 .name = "twl4030_usb",
845 .type = POWER_SUPPLY_TYPE_USB,
846 .properties = twl4030_charger_props,
847 .num_properties = ARRAY_SIZE(twl4030_charger_props),
848 .get_property = twl4030_bci_get_property,
849};
850
Pavel Machek7396f702015-07-30 10:11:24 +1000851static int twl4030_bci_probe(struct platform_device *pdev)
Grazvydas Ignotas2e727f12010-09-27 23:18:24 +0300852{
853 struct twl4030_bci *bci;
NeilBrownec0b3802013-10-31 17:05:50 +1100854 const struct twl4030_bci_platform_data *pdata = pdev->dev.platform_data;
Grazvydas Ignotas2e727f12010-09-27 23:18:24 +0300855 int ret;
Grazvydas Ignotas28d48f02011-06-03 14:43:11 +0300856 u32 reg;
Grazvydas Ignotas2e727f12010-09-27 23:18:24 +0300857
NeilBrown325b50a2015-03-23 10:20:28 +1100858 bci = devm_kzalloc(&pdev->dev, sizeof(*bci), GFP_KERNEL);
Grazvydas Ignotas2e727f12010-09-27 23:18:24 +0300859 if (bci == NULL)
860 return -ENOMEM;
861
NeilBrownec0b3802013-10-31 17:05:50 +1100862 if (!pdata)
863 pdata = twl4030_bci_parse_dt(&pdev->dev);
864
NeilBrown1098cb52015-07-30 10:11:24 +1000865 bci->ichg_eoc = 80100; /* Stop charging when current drops to here */
866 bci->ichg_lo = 241000; /* Low threshold */
867 bci->ichg_hi = 500000; /* High threshold */
NeilBrowne4ae5372015-07-30 10:11:24 +1000868 bci->ac_cur = 500000; /* 500mA */
NeilBrown1098cb52015-07-30 10:11:24 +1000869 if (allow_usb)
NeilBrowne4ae5372015-07-30 10:11:24 +1000870 bci->usb_cur = 500000; /* 500mA */
NeilBrown1098cb52015-07-30 10:11:24 +1000871 else
NeilBrowne4ae5372015-07-30 10:11:24 +1000872 bci->usb_cur = 100000; /* 100mA */
NeilBrown22d4c332015-07-30 10:11:24 +1000873 bci->usb_mode = CHARGE_AUTO;
NeilBrown1098cb52015-07-30 10:11:24 +1000874
Grazvydas Ignotas2e727f12010-09-27 23:18:24 +0300875 bci->dev = &pdev->dev;
876 bci->irq_chg = platform_get_irq(pdev, 0);
877 bci->irq_bci = platform_get_irq(pdev, 1);
878
Nishanth Menon61a77842014-05-28 16:46:49 -0500879 /* Only proceed further *IF* battery is physically present */
880 ret = twl4030_is_battery_present(bci);
881 if (ret) {
882 dev_crit(&pdev->dev, "Battery was not detected:%d\n", ret);
NeilBrown325b50a2015-03-23 10:20:28 +1100883 return ret;
Nishanth Menon61a77842014-05-28 16:46:49 -0500884 }
Grazvydas Ignotas2e727f12010-09-27 23:18:24 +0300885
Nishanth Menon61a77842014-05-28 16:46:49 -0500886 platform_set_drvdata(pdev, bci);
Grazvydas Ignotas2e727f12010-09-27 23:18:24 +0300887
NeilBrown325b50a2015-03-23 10:20:28 +1100888 bci->ac = devm_power_supply_register(&pdev->dev, &twl4030_bci_ac_desc,
889 NULL);
Krzysztof Kozlowski297d7162015-03-12 08:44:11 +0100890 if (IS_ERR(bci->ac)) {
891 ret = PTR_ERR(bci->ac);
Grazvydas Ignotas2e727f12010-09-27 23:18:24 +0300892 dev_err(&pdev->dev, "failed to register ac: %d\n", ret);
NeilBrown325b50a2015-03-23 10:20:28 +1100893 return ret;
Grazvydas Ignotas2e727f12010-09-27 23:18:24 +0300894 }
895
NeilBrown325b50a2015-03-23 10:20:28 +1100896 bci->usb = devm_power_supply_register(&pdev->dev, &twl4030_bci_usb_desc,
897 NULL);
Krzysztof Kozlowski297d7162015-03-12 08:44:11 +0100898 if (IS_ERR(bci->usb)) {
899 ret = PTR_ERR(bci->usb);
Grazvydas Ignotas2e727f12010-09-27 23:18:24 +0300900 dev_err(&pdev->dev, "failed to register usb: %d\n", ret);
NeilBrown325b50a2015-03-23 10:20:28 +1100901 return ret;
Grazvydas Ignotas2e727f12010-09-27 23:18:24 +0300902 }
903
NeilBrown1b7cac22015-03-23 10:20:28 +1100904 ret = devm_request_threaded_irq(&pdev->dev, bci->irq_chg, NULL,
Fengguang Wua2778b02012-08-23 19:56:43 +0800905 twl4030_charger_interrupt, IRQF_ONESHOT, pdev->name,
906 bci);
Grazvydas Ignotas2e727f12010-09-27 23:18:24 +0300907 if (ret < 0) {
908 dev_err(&pdev->dev, "could not request irq %d, status %d\n",
909 bci->irq_chg, ret);
NeilBrown325b50a2015-03-23 10:20:28 +1100910 return ret;
Grazvydas Ignotas2e727f12010-09-27 23:18:24 +0300911 }
912
NeilBrown1b7cac22015-03-23 10:20:28 +1100913 ret = devm_request_threaded_irq(&pdev->dev, bci->irq_bci, NULL,
Fengguang Wua2778b02012-08-23 19:56:43 +0800914 twl4030_bci_interrupt, IRQF_ONESHOT, pdev->name, bci);
Grazvydas Ignotas2e727f12010-09-27 23:18:24 +0300915 if (ret < 0) {
916 dev_err(&pdev->dev, "could not request irq %d, status %d\n",
917 bci->irq_bci, ret);
NeilBrown325b50a2015-03-23 10:20:28 +1100918 return ret;
Grazvydas Ignotas2e727f12010-09-27 23:18:24 +0300919 }
920
Heikki Krogerusd6ccc442011-02-25 13:56:36 +0200921 INIT_WORK(&bci->work, twl4030_bci_usb_work);
922
NeilBrownf5e4edb2015-03-23 09:52:48 +1100923 bci->usb_nb.notifier_call = twl4030_bci_usb_ncb;
924 if (bci->dev->of_node) {
925 struct device_node *phynode;
926
927 phynode = of_find_compatible_node(bci->dev->of_node->parent,
928 NULL, "ti,twl4030-usb");
NeilBrown3fc38952015-07-30 10:11:24 +1000929 if (phynode) {
NeilBrownf5e4edb2015-03-23 09:52:48 +1100930 bci->transceiver = devm_usb_get_phy_by_node(
931 bci->dev, phynode, &bci->usb_nb);
NeilBrown3fc38952015-07-30 10:11:24 +1000932 if (IS_ERR(bci->transceiver) &&
933 PTR_ERR(bci->transceiver) == -EPROBE_DEFER)
934 return -EPROBE_DEFER;
935 }
Grazvydas Ignotas2e727f12010-09-27 23:18:24 +0300936 }
937
938 /* Enable interrupts now. */
Grazvydas Ignotas28d48f02011-06-03 14:43:11 +0300939 reg = ~(u32)(TWL4030_ICHGLOW | TWL4030_ICHGEOC | TWL4030_TBATOR2 |
Grazvydas Ignotas2e727f12010-09-27 23:18:24 +0300940 TWL4030_TBATOR1 | TWL4030_BATSTS);
941 ret = twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, reg,
942 TWL4030_INTERRUPTS_BCIIMR1A);
943 if (ret < 0) {
944 dev_err(&pdev->dev, "failed to unmask interrupts: %d\n", ret);
NeilBrown325b50a2015-03-23 10:20:28 +1100945 return ret;
Grazvydas Ignotas2e727f12010-09-27 23:18:24 +0300946 }
947
Grazvydas Ignotas28d48f02011-06-03 14:43:11 +0300948 reg = ~(u32)(TWL4030_VBATOV | TWL4030_VBUSOV | TWL4030_ACCHGOV);
Grazvydas Ignotas2e727f12010-09-27 23:18:24 +0300949 ret = twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, reg,
950 TWL4030_INTERRUPTS_BCIIMR2A);
951 if (ret < 0)
952 dev_warn(&pdev->dev, "failed to unmask interrupts: %d\n", ret);
953
NeilBrown1098cb52015-07-30 10:11:24 +1000954 twl4030_charger_update_current(bci);
NeilBrownaca3c352015-07-30 10:11:24 +1000955 if (device_create_file(&bci->usb->dev, &dev_attr_max_current))
956 dev_warn(&pdev->dev, "could not create sysfs file\n");
NeilBrown22d4c332015-07-30 10:11:24 +1000957 if (device_create_file(&bci->usb->dev, &dev_attr_mode))
958 dev_warn(&pdev->dev, "could not create sysfs file\n");
NeilBrownaca3c352015-07-30 10:11:24 +1000959 if (device_create_file(&bci->ac->dev, &dev_attr_max_current))
960 dev_warn(&pdev->dev, "could not create sysfs file\n");
961
Grazvydas Ignotas2e727f12010-09-27 23:18:24 +0300962 twl4030_charger_enable_ac(true);
NeilBrown6e37ec82015-07-30 10:11:24 +1000963 if (!IS_ERR_OR_NULL(bci->transceiver))
964 twl4030_bci_usb_ncb(&bci->usb_nb,
965 bci->transceiver->last_event,
966 NULL);
967 else
968 twl4030_charger_enable_usb(bci, false);
NeilBrownec0b3802013-10-31 17:05:50 +1100969 if (pdata)
970 twl4030_charger_enable_backup(pdata->bb_uvolt,
971 pdata->bb_uamp);
972 else
973 twl4030_charger_enable_backup(0, 0);
Grazvydas Ignotas2e727f12010-09-27 23:18:24 +0300974
975 return 0;
Grazvydas Ignotas2e727f12010-09-27 23:18:24 +0300976}
977
978static int __exit twl4030_bci_remove(struct platform_device *pdev)
979{
980 struct twl4030_bci *bci = platform_get_drvdata(pdev);
981
982 twl4030_charger_enable_ac(false);
983 twl4030_charger_enable_usb(bci, false);
NeilBrown210d4bc2012-05-09 07:40:40 +1000984 twl4030_charger_enable_backup(0, 0);
Grazvydas Ignotas2e727f12010-09-27 23:18:24 +0300985
NeilBrownaca3c352015-07-30 10:11:24 +1000986 device_remove_file(&bci->usb->dev, &dev_attr_max_current);
NeilBrown22d4c332015-07-30 10:11:24 +1000987 device_remove_file(&bci->usb->dev, &dev_attr_mode);
NeilBrownaca3c352015-07-30 10:11:24 +1000988 device_remove_file(&bci->ac->dev, &dev_attr_max_current);
Grazvydas Ignotas2e727f12010-09-27 23:18:24 +0300989 /* mask interrupts */
990 twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xff,
991 TWL4030_INTERRUPTS_BCIIMR1A);
992 twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xff,
993 TWL4030_INTERRUPTS_BCIIMR2A);
994
Grazvydas Ignotas2e727f12010-09-27 23:18:24 +0300995 return 0;
996}
997
NeilBrownec0b3802013-10-31 17:05:50 +1100998static const struct of_device_id twl_bci_of_match[] = {
999 {.compatible = "ti,twl4030-bci", },
1000 { }
1001};
1002MODULE_DEVICE_TABLE(of, twl_bci_of_match);
1003
Grazvydas Ignotas2e727f12010-09-27 23:18:24 +03001004static struct platform_driver twl4030_bci_driver = {
Pavel Machek7396f702015-07-30 10:11:24 +10001005 .probe = twl4030_bci_probe,
Grazvydas Ignotas2e727f12010-09-27 23:18:24 +03001006 .driver = {
1007 .name = "twl4030_bci",
NeilBrownec0b3802013-10-31 17:05:50 +11001008 .of_match_table = of_match_ptr(twl_bci_of_match),
Grazvydas Ignotas2e727f12010-09-27 23:18:24 +03001009 },
1010 .remove = __exit_p(twl4030_bci_remove),
1011};
Pavel Machek7396f702015-07-30 10:11:24 +10001012module_platform_driver(twl4030_bci_driver);
Grazvydas Ignotas2e727f12010-09-27 23:18:24 +03001013
Grazvydas Ignotas28d48f02011-06-03 14:43:11 +03001014MODULE_AUTHOR("Gražvydas Ignotas");
Grazvydas Ignotas2e727f12010-09-27 23:18:24 +03001015MODULE_DESCRIPTION("TWL4030 Battery Charger Interface driver");
1016MODULE_LICENSE("GPL");
1017MODULE_ALIAS("platform:twl4030_bci");