diff options
Diffstat (limited to 'extras/recipes-kernel/linux/linux-omap-psp-2.6.32/omap3-touchbook/0015-Forward-port-TWL4030-BCI-driver-from-2.6.29-to-2.6.3.patch')
-rw-r--r-- | extras/recipes-kernel/linux/linux-omap-psp-2.6.32/omap3-touchbook/0015-Forward-port-TWL4030-BCI-driver-from-2.6.29-to-2.6.3.patch | 1367 |
1 files changed, 1367 insertions, 0 deletions
diff --git a/extras/recipes-kernel/linux/linux-omap-psp-2.6.32/omap3-touchbook/0015-Forward-port-TWL4030-BCI-driver-from-2.6.29-to-2.6.3.patch b/extras/recipes-kernel/linux/linux-omap-psp-2.6.32/omap3-touchbook/0015-Forward-port-TWL4030-BCI-driver-from-2.6.29-to-2.6.3.patch new file mode 100644 index 00000000..562c459e --- /dev/null +++ b/extras/recipes-kernel/linux/linux-omap-psp-2.6.32/omap3-touchbook/0015-Forward-port-TWL4030-BCI-driver-from-2.6.29-to-2.6.3.patch | |||
@@ -0,0 +1,1367 @@ | |||
1 | From c3a08f3d696866508ef2b5e2fd065b8295b3e1a8 Mon Sep 17 00:00:00 2001 | ||
2 | From: Tim Yamin <plasm@roo.me.uk> | ||
3 | Date: Sun, 9 May 2010 10:14:23 +0200 | ||
4 | Subject: [PATCH 15/17] Forward port TWL4030 BCI driver from 2.6.29 to 2.6.31 with AI enhancements. | ||
5 | |||
6 | Signed-off-by: Tim Yamin <plasm@roo.me.uk> | ||
7 | --- | ||
8 | drivers/power/Kconfig | 7 + | ||
9 | drivers/power/Makefile | 1 + | ||
10 | drivers/power/twl4030_bci_battery.c | 1307 +++++++++++++++++++++++++++++++++++ | ||
11 | include/linux/i2c/twl.h | 1 + | ||
12 | 4 files changed, 1316 insertions(+), 0 deletions(-) | ||
13 | create mode 100644 drivers/power/twl4030_bci_battery.c | ||
14 | |||
15 | diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig | ||
16 | index d4b3d67..8345b3f 100644 | ||
17 | --- a/drivers/power/Kconfig | ||
18 | +++ b/drivers/power/Kconfig | ||
19 | @@ -124,4 +124,11 @@ config CHARGER_PCF50633 | ||
20 | help | ||
21 | Say Y to include support for NXP PCF50633 Main Battery Charger. | ||
22 | |||
23 | +config TWL4030_BCI_BATTERY | ||
24 | + tristate "OMAP TWL4030 BCI Battery driver" | ||
25 | + depends on TWL4030_CORE && TWL4030_MADC | ||
26 | + help | ||
27 | + Support for OMAP TWL4030 BCI Battery driver. | ||
28 | + This driver can give support for TWL4030 Battery Charge Interface. | ||
29 | + | ||
30 | endif # POWER_SUPPLY | ||
31 | diff --git a/drivers/power/Makefile b/drivers/power/Makefile | ||
32 | index 573597c..7801da7 100644 | ||
33 | --- a/drivers/power/Makefile | ||
34 | +++ b/drivers/power/Makefile | ||
35 | @@ -31,3 +31,4 @@ obj-$(CONFIG_BATTERY_BQ27x00) += bq27x00_battery.o | ||
36 | obj-$(CONFIG_BATTERY_DA9030) += da9030_battery.o | ||
37 | obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o | ||
38 | obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o | ||
39 | +obj-$(CONFIG_TWL4030_BCI_BATTERY) += twl4030_bci_battery.o | ||
40 | diff --git a/drivers/power/twl4030_bci_battery.c b/drivers/power/twl4030_bci_battery.c | ||
41 | new file mode 100644 | ||
42 | index 0000000..0876fc3 | ||
43 | --- /dev/null | ||
44 | +++ b/drivers/power/twl4030_bci_battery.c | ||
45 | @@ -0,0 +1,1307 @@ | ||
46 | +/* | ||
47 | + * linux/drivers/power/twl4030_bci_battery.c | ||
48 | + * | ||
49 | + * OMAP2430/3430 BCI battery driver for Linux | ||
50 | + * | ||
51 | + * Copyright (C) 2008 Texas Instruments, Inc. | ||
52 | + * Author: Texas Instruments, Inc. | ||
53 | + * | ||
54 | + * Copyright (C) 2010 Always Innovating | ||
55 | + * Author: Tim Yamin <plasm@roo.me.uk> | ||
56 | + * | ||
57 | + * This package is free software; you can redistribute it and/or modify | ||
58 | + * it under the terms of the GNU General Public License version 2 as | ||
59 | + * published by the Free Software Foundation. | ||
60 | + * | ||
61 | + * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR | ||
62 | + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED | ||
63 | + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. | ||
64 | + */ | ||
65 | + | ||
66 | +/* Boot with automatic charge */ | ||
67 | +#define CHARGE_MODE 1 | ||
68 | + | ||
69 | +#include <linux/init.h> | ||
70 | +#include <linux/module.h> | ||
71 | +#include <linux/device.h> | ||
72 | +#include <linux/interrupt.h> | ||
73 | +#include <linux/delay.h> | ||
74 | +#include <linux/platform_device.h> | ||
75 | +#include <linux/i2c/twl.h> | ||
76 | +#include <linux/power_supply.h> | ||
77 | +#include <linux/i2c/twl4030-madc.h> | ||
78 | + | ||
79 | +#define T2_BATTERY_VOLT 0x04 | ||
80 | +#define T2_BATTERY_TEMP 0x06 | ||
81 | +#define T2_BATTERY_CUR 0x08 | ||
82 | + | ||
83 | +/* charger constants */ | ||
84 | +#define NO_PW_CONN 0 | ||
85 | +#define AC_PW_CONN 0x01 | ||
86 | +#define USB_PW_CONN 0x02 | ||
87 | + | ||
88 | +/* TWL4030_MODULE_USB */ | ||
89 | +#define REG_POWER_CTRL 0x0AC | ||
90 | +#define OTG_EN 0x020 | ||
91 | +#define REG_PHY_CLK_CTRL 0x0FE | ||
92 | +#define REG_PHY_CLK_CTRL_STS 0x0FF | ||
93 | +#define PHY_DPLL_CLK 0x01 | ||
94 | + | ||
95 | +#define REG_BCICTL1 0x023 | ||
96 | +#define REG_BCICTL2 0x024 | ||
97 | +#define CGAIN 0x020 | ||
98 | +#define ITHEN 0x010 | ||
99 | +#define ITHSENS 0x007 | ||
100 | + | ||
101 | +/* Boot BCI flag bits */ | ||
102 | +#define BCIAUTOWEN 0x020 | ||
103 | +#define CONFIG_DONE 0x010 | ||
104 | +#define CVENAC 0x004 | ||
105 | +#define BCIAUTOUSB 0x002 | ||
106 | +#define BCIAUTOAC 0x001 | ||
107 | +#define BCIMSTAT_MASK 0x03F | ||
108 | + | ||
109 | +/* Boot BCI register */ | ||
110 | +#define REG_BOOT_BCI 0x007 | ||
111 | +#define REG_CTRL1 0x00 | ||
112 | +#define REG_SW1SELECT_MSB 0x07 | ||
113 | +#define SW1_CH9_SEL 0x02 | ||
114 | +#define REG_CTRL_SW1 0x012 | ||
115 | +#define SW1_TRIGGER 0x020 | ||
116 | +#define EOC_SW1 0x002 | ||
117 | +#define REG_GPCH9 0x049 | ||
118 | +#define REG_STS_HW_CONDITIONS 0x0F | ||
119 | +#define STS_VBUS 0x080 | ||
120 | +#define STS_CHG 0x02 | ||
121 | +#define REG_BCIMSTATEC 0x02 | ||
122 | +#define REG_BCIMFSTS4 0x010 | ||
123 | +#define REG_BCIMFSTS2 0x00E | ||
124 | +#define REG_BCIMFSTS3 0x00F | ||
125 | +#define REG_BCIMFSTS1 0x001 | ||
126 | +#define USBFASTMCHG 0x004 | ||
127 | +#define BATSTSPCHG 0x004 | ||
128 | +#define BATSTSMCHG 0x040 | ||
129 | +#define VBATOV4 0x020 | ||
130 | +#define VBATOV3 0x010 | ||
131 | +#define VBATOV2 0x008 | ||
132 | +#define VBATOV1 0x004 | ||
133 | +#define REG_BB_CFG 0x012 | ||
134 | +#define BBCHEN 0x010 | ||
135 | + | ||
136 | +/* GPBR */ | ||
137 | +#define REG_GPBR1 0x0c | ||
138 | +#define MADC_HFCLK_EN 0x80 | ||
139 | +#define DEFAULT_MADC_CLK_EN 0x10 | ||
140 | + | ||
141 | +/* Power supply charge interrupt */ | ||
142 | +#define REG_PWR_ISR1 0x00 | ||
143 | +#define REG_PWR_IMR1 0x01 | ||
144 | +#define REG_PWR_EDR1 0x05 | ||
145 | +#define REG_PWR_SIH_CTRL 0x007 | ||
146 | + | ||
147 | +#define USB_PRES 0x004 | ||
148 | +#define CHG_PRES 0x002 | ||
149 | + | ||
150 | +#define USB_PRES_RISING 0x020 | ||
151 | +#define USB_PRES_FALLING 0x010 | ||
152 | +#define CHG_PRES_RISING 0x008 | ||
153 | +#define CHG_PRES_FALLING 0x004 | ||
154 | +#define AC_STATEC 0x20 | ||
155 | +#define COR 0x004 | ||
156 | + | ||
157 | +/* interrupt status registers */ | ||
158 | +#define REG_BCIISR1A 0x0 | ||
159 | +#define REG_BCIISR2A 0x01 | ||
160 | + | ||
161 | +/* Interrupt flags bits BCIISR1 */ | ||
162 | +#define BATSTS_ISR1 0x080 | ||
163 | +#define VBATLVL_ISR1 0x001 | ||
164 | + | ||
165 | +/* Interrupt mask registers for int1*/ | ||
166 | +#define REG_BCIIMR1A 0x002 | ||
167 | +#define REG_BCIIMR2A 0x003 | ||
168 | + | ||
169 | + /* Interrupt masks for BCIIMR1 */ | ||
170 | +#define BATSTS_IMR1 0x080 | ||
171 | +#define VBATLVL_IMR1 0x001 | ||
172 | + | ||
173 | +/* Interrupt edge detection register */ | ||
174 | +#define REG_BCIEDR1 0x00A | ||
175 | +#define REG_BCIEDR2 0x00B | ||
176 | +#define REG_BCIEDR3 0x00C | ||
177 | + | ||
178 | +/* BCIEDR2 */ | ||
179 | +#define BATSTS_EDRRISIN 0x080 | ||
180 | +#define BATSTS_EDRFALLING 0x040 | ||
181 | + | ||
182 | +/* BCIEDR3 */ | ||
183 | +#define VBATLVL_EDRRISIN 0x02 | ||
184 | + | ||
185 | +/* BCIIREF1 */ | ||
186 | +#define REG_BCIIREF1 0x027 | ||
187 | +#define REG_BCIIREF2 0x028 | ||
188 | + | ||
189 | +/* BCIMFTH1 */ | ||
190 | +#define REG_BCIMFTH1 0x016 | ||
191 | + | ||
192 | +/* Key */ | ||
193 | +#define KEY_IIREF 0xE7 | ||
194 | +#define KEY_FTH1 0xD2 | ||
195 | +#define REG_BCIMFKEY 0x011 | ||
196 | + | ||
197 | +/* Step size and prescaler ratio */ | ||
198 | +#define TEMP_STEP_SIZE 147 | ||
199 | +#define TEMP_PSR_R 100 | ||
200 | + | ||
201 | +#define VOLT_STEP_SIZE 588 | ||
202 | +#define VOLT_PSR_R 100 | ||
203 | + | ||
204 | +#define CURR_STEP_SIZE 147 | ||
205 | +#define CURR_PSR_R1 44 | ||
206 | +#define CURR_PSR_R2 80 | ||
207 | + | ||
208 | +#define BK_VOLT_STEP_SIZE 441 | ||
209 | +#define BK_VOLT_PSR_R 100 | ||
210 | + | ||
211 | +#define ENABLE 1 | ||
212 | +#define DISABLE 1 | ||
213 | + | ||
214 | +struct twl4030_bci_device_info { | ||
215 | + struct device *dev; | ||
216 | + | ||
217 | + unsigned long update_time; | ||
218 | + int voltage_uV; | ||
219 | + int bk_voltage_uV; | ||
220 | + int current_uA; | ||
221 | + int temp_C; | ||
222 | + int charge_rsoc; | ||
223 | + int charge_status; | ||
224 | + | ||
225 | + struct power_supply bat; | ||
226 | + struct power_supply bk_bat; | ||
227 | + struct delayed_work twl4030_bci_monitor_work; | ||
228 | + struct delayed_work twl4030_bk_bci_monitor_work; | ||
229 | + | ||
230 | + struct twl4030_bci_platform_data *pdata; | ||
231 | +}; | ||
232 | + | ||
233 | +static int usb_charger_flag; | ||
234 | +static int LVL_1, LVL_2, LVL_3, LVL_4; | ||
235 | + | ||
236 | +static int read_bci_val(u8 reg_1); | ||
237 | +static inline int clear_n_set(u8 mod_no, u8 clear, u8 set, u8 reg); | ||
238 | +static int twl4030charger_presence(void); | ||
239 | + | ||
240 | +/* | ||
241 | + * Report and clear the charger presence event. | ||
242 | + */ | ||
243 | +static inline int twl4030charger_presence_evt(void) | ||
244 | +{ | ||
245 | + int ret; | ||
246 | + u8 chg_sts, set = 0, clear = 0; | ||
247 | + | ||
248 | + /* read charger power supply status */ | ||
249 | + ret = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &chg_sts, | ||
250 | + REG_STS_HW_CONDITIONS); | ||
251 | + if (ret) | ||
252 | + return IRQ_NONE; | ||
253 | + | ||
254 | + if (chg_sts & STS_CHG) { /* If the AC charger have been connected */ | ||
255 | + /* configuring falling edge detection for CHG_PRES */ | ||
256 | + set = CHG_PRES_FALLING; | ||
257 | + clear = CHG_PRES_RISING; | ||
258 | + } else { /* If the AC charger have been disconnected */ | ||
259 | + /* configuring rising edge detection for CHG_PRES */ | ||
260 | + set = CHG_PRES_RISING; | ||
261 | + clear = CHG_PRES_FALLING; | ||
262 | + } | ||
263 | + | ||
264 | + /* Update the interrupt edge detection register */ | ||
265 | + clear_n_set(TWL4030_MODULE_INT, clear, set, REG_PWR_EDR1); | ||
266 | + | ||
267 | + return 0; | ||
268 | +} | ||
269 | + | ||
270 | +/* | ||
271 | + * Interrupt service routine | ||
272 | + * | ||
273 | + * Attends to TWL 4030 power module interruptions events, specifically | ||
274 | + * USB_PRES (USB charger presence) CHG_PRES (AC charger presence) events | ||
275 | + * | ||
276 | + */ | ||
277 | +static irqreturn_t twl4030charger_interrupt(int irq, void *_di) | ||
278 | +{ | ||
279 | + struct twl4030_bci_device_info *di = _di; | ||
280 | + | ||
281 | +#ifdef CONFIG_LOCKDEP | ||
282 | + /* WORKAROUND for lockdep forcing IRQF_DISABLED on us, which | ||
283 | + * we don't want and can't tolerate. Although it might be | ||
284 | + * friendlier not to borrow this thread context... | ||
285 | + */ | ||
286 | + local_irq_enable(); | ||
287 | +#endif | ||
288 | + | ||
289 | + twl4030charger_presence_evt(); | ||
290 | + power_supply_changed(&di->bat); | ||
291 | + | ||
292 | + return IRQ_HANDLED; | ||
293 | +} | ||
294 | + | ||
295 | +/* | ||
296 | + * This function handles the twl4030 battery presence interrupt | ||
297 | + */ | ||
298 | +static int twl4030battery_presence_evt(void) | ||
299 | +{ | ||
300 | + int ret; | ||
301 | + u8 batstsmchg, batstspchg; | ||
302 | + | ||
303 | + /* check for the battery presence in main charge*/ | ||
304 | + ret = twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, | ||
305 | + &batstsmchg, REG_BCIMFSTS3); | ||
306 | + if (ret) | ||
307 | + return ret; | ||
308 | + | ||
309 | + /* check for the battery presence in precharge */ | ||
310 | + ret = twl_i2c_read_u8(TWL4030_MODULE_PRECHARGE, | ||
311 | + &batstspchg, REG_BCIMFSTS1); | ||
312 | + if (ret) | ||
313 | + return ret; | ||
314 | + | ||
315 | + /* | ||
316 | + * REVISIT: Physically inserting/removing the batt | ||
317 | + * does not seem to generate an int on 3430ES2 SDP. | ||
318 | + */ | ||
319 | + if ((batstspchg & BATSTSPCHG) || (batstsmchg & BATSTSMCHG)) { | ||
320 | + /* In case of the battery insertion event */ | ||
321 | + ret = clear_n_set(TWL4030_MODULE_INTERRUPTS, BATSTS_EDRRISIN, | ||
322 | + BATSTS_EDRFALLING, REG_BCIEDR2); | ||
323 | + if (ret) | ||
324 | + return ret; | ||
325 | + } else { | ||
326 | + /* In case of the battery removal event */ | ||
327 | + ret = clear_n_set(TWL4030_MODULE_INTERRUPTS, BATSTS_EDRFALLING, | ||
328 | + BATSTS_EDRRISIN, REG_BCIEDR2); | ||
329 | + if (ret) | ||
330 | + return ret; | ||
331 | + } | ||
332 | + | ||
333 | + return 0; | ||
334 | +} | ||
335 | + | ||
336 | +/* | ||
337 | + * This function handles the twl4030 battery voltage level interrupt. | ||
338 | + */ | ||
339 | +static int twl4030battery_level_evt(void) | ||
340 | +{ | ||
341 | + int ret; | ||
342 | + u8 mfst; | ||
343 | + | ||
344 | + /* checking for threshold event */ | ||
345 | + ret = twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, | ||
346 | + &mfst, REG_BCIMFSTS2); | ||
347 | + if (ret) | ||
348 | + return ret; | ||
349 | + | ||
350 | + /* REVISIT could use a bitmap */ | ||
351 | + if (mfst & VBATOV4) { | ||
352 | + LVL_4 = 1; | ||
353 | + LVL_3 = 0; | ||
354 | + LVL_2 = 0; | ||
355 | + LVL_1 = 0; | ||
356 | + } else if (mfst & VBATOV3) { | ||
357 | + LVL_4 = 0; | ||
358 | + LVL_3 = 1; | ||
359 | + LVL_2 = 0; | ||
360 | + LVL_1 = 0; | ||
361 | + } else if (mfst & VBATOV2) { | ||
362 | + LVL_4 = 0; | ||
363 | + LVL_3 = 0; | ||
364 | + LVL_2 = 1; | ||
365 | + LVL_1 = 0; | ||
366 | + } else { | ||
367 | + LVL_4 = 0; | ||
368 | + LVL_3 = 0; | ||
369 | + LVL_2 = 0; | ||
370 | + LVL_1 = 1; | ||
371 | + } | ||
372 | + | ||
373 | + return 0; | ||
374 | +} | ||
375 | + | ||
376 | +/* | ||
377 | + * Interrupt service routine | ||
378 | + * | ||
379 | + * Attends to BCI interruptions events, | ||
380 | + * specifically BATSTS (battery connection and removal) | ||
381 | + * VBATOV (main battery voltage threshold) events | ||
382 | + * | ||
383 | + */ | ||
384 | +static irqreturn_t twl4030battery_interrupt(int irq, void *_di) | ||
385 | +{ | ||
386 | + u8 isr1a_val, isr2a_val, clear_2a, clear_1a; | ||
387 | + int ret; | ||
388 | + | ||
389 | +#ifdef CONFIG_LOCKDEP | ||
390 | + /* WORKAROUND for lockdep forcing IRQF_DISABLED on us, which | ||
391 | + * we don't want and can't tolerate. Although it might be | ||
392 | + * friendlier not to borrow this thread context... | ||
393 | + */ | ||
394 | + local_irq_enable(); | ||
395 | +#endif | ||
396 | + | ||
397 | + ret = twl_i2c_read_u8(TWL4030_MODULE_INTERRUPTS, &isr1a_val, | ||
398 | + REG_BCIISR1A); | ||
399 | + if (ret) | ||
400 | + return IRQ_NONE; | ||
401 | + | ||
402 | + ret = twl_i2c_read_u8(TWL4030_MODULE_INTERRUPTS, &isr2a_val, | ||
403 | + REG_BCIISR2A); | ||
404 | + if (ret) | ||
405 | + return IRQ_NONE; | ||
406 | + | ||
407 | + clear_2a = (isr2a_val & VBATLVL_ISR1) ? (VBATLVL_ISR1) : 0; | ||
408 | + clear_1a = (isr1a_val & BATSTS_ISR1) ? (BATSTS_ISR1) : 0; | ||
409 | + | ||
410 | + /* cleaning BCI interrupt status flags */ | ||
411 | + ret = twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, | ||
412 | + clear_1a , REG_BCIISR1A); | ||
413 | + if (ret) | ||
414 | + return IRQ_NONE; | ||
415 | + | ||
416 | + ret = twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, | ||
417 | + clear_2a , REG_BCIISR2A); | ||
418 | + if (ret) | ||
419 | + return IRQ_NONE; | ||
420 | + | ||
421 | + /* battery connetion or removal event */ | ||
422 | + if (isr1a_val & BATSTS_ISR1) | ||
423 | + twl4030battery_presence_evt(); | ||
424 | + /* battery voltage threshold event*/ | ||
425 | + else if (isr2a_val & VBATLVL_ISR1) | ||
426 | + twl4030battery_level_evt(); | ||
427 | + else | ||
428 | + return IRQ_NONE; | ||
429 | + | ||
430 | + return IRQ_HANDLED; | ||
431 | +} | ||
432 | + | ||
433 | +/* | ||
434 | + * Enable/Disable hardware battery level event notifications. | ||
435 | + */ | ||
436 | +static int twl4030battery_hw_level_en(int enable) | ||
437 | +{ | ||
438 | + int ret; | ||
439 | + | ||
440 | + if (enable) { | ||
441 | + /* unmask VBATOV interrupt for INT1 */ | ||
442 | + ret = clear_n_set(TWL4030_MODULE_INTERRUPTS, VBATLVL_IMR1, | ||
443 | + 0, REG_BCIIMR2A); | ||
444 | + if (ret) | ||
445 | + return ret; | ||
446 | + | ||
447 | + /* configuring interrupt edge detection for VBATOv */ | ||
448 | + ret = clear_n_set(TWL4030_MODULE_INTERRUPTS, 0, | ||
449 | + VBATLVL_EDRRISIN, REG_BCIEDR3); | ||
450 | + if (ret) | ||
451 | + return ret; | ||
452 | + } else { | ||
453 | + /* mask VBATOV interrupt for INT1 */ | ||
454 | + ret = clear_n_set(TWL4030_MODULE_INTERRUPTS, 0, | ||
455 | + VBATLVL_IMR1, REG_BCIIMR2A); | ||
456 | + if (ret) | ||
457 | + return ret; | ||
458 | + } | ||
459 | + | ||
460 | + return 0; | ||
461 | +} | ||
462 | + | ||
463 | +/* | ||
464 | + * Enable/disable hardware battery presence event notifications. | ||
465 | + */ | ||
466 | +static int twl4030battery_hw_presence_en(int enable) | ||
467 | +{ | ||
468 | + int ret; | ||
469 | + | ||
470 | + if (enable) { | ||
471 | + /* unmask BATSTS interrupt for INT1 */ | ||
472 | + ret = clear_n_set(TWL4030_MODULE_INTERRUPTS, BATSTS_IMR1, | ||
473 | + 0, REG_BCIIMR1A); | ||
474 | + if (ret) | ||
475 | + return ret; | ||
476 | + | ||
477 | + /* configuring interrupt edge for BATSTS */ | ||
478 | + ret = clear_n_set(TWL4030_MODULE_INTERRUPTS, 0, | ||
479 | + BATSTS_EDRRISIN | BATSTS_EDRFALLING, REG_BCIEDR2); | ||
480 | + if (ret) | ||
481 | + return ret; | ||
482 | + } else { | ||
483 | + /* mask BATSTS interrupt for INT1 */ | ||
484 | + ret = clear_n_set(TWL4030_MODULE_INTERRUPTS, 0, | ||
485 | + BATSTS_IMR1, REG_BCIIMR1A); | ||
486 | + if (ret) | ||
487 | + return ret; | ||
488 | + } | ||
489 | + | ||
490 | + return 0; | ||
491 | +} | ||
492 | + | ||
493 | +/* | ||
494 | + * Enable/Disable AC Charge funtionality. | ||
495 | + */ | ||
496 | +static int twl4030charger_ac_en(int enable, int automatic) | ||
497 | +{ | ||
498 | + int ret; | ||
499 | + | ||
500 | + if (enable) { | ||
501 | + /* forcing the field BCIAUTOAC (BOOT_BCI[0) to 1 */ | ||
502 | + if(!automatic) { | ||
503 | + ret = clear_n_set(TWL4030_MODULE_PM_MASTER, BCIAUTOAC | CVENAC, | ||
504 | + (CONFIG_DONE | BCIAUTOWEN), | ||
505 | + REG_BOOT_BCI); | ||
506 | + } else { | ||
507 | + ret = clear_n_set(TWL4030_MODULE_PM_MASTER, 0, | ||
508 | + (CONFIG_DONE | BCIAUTOWEN | BCIAUTOAC | CVENAC), | ||
509 | + REG_BOOT_BCI); | ||
510 | + } | ||
511 | + if (ret) | ||
512 | + return ret; | ||
513 | + } else { | ||
514 | + /* forcing the field BCIAUTOAC (BOOT_BCI[0) to 0*/ | ||
515 | + ret = clear_n_set(TWL4030_MODULE_PM_MASTER, BCIAUTOAC, | ||
516 | + (CONFIG_DONE | BCIAUTOWEN), | ||
517 | + REG_BOOT_BCI); | ||
518 | + if (ret) | ||
519 | + return ret; | ||
520 | + } | ||
521 | + | ||
522 | + return 0; | ||
523 | +} | ||
524 | + | ||
525 | +/* | ||
526 | + * Enable/Disable USB Charge funtionality. | ||
527 | + */ | ||
528 | +int twl4030charger_usb_en(int enable) | ||
529 | +{ | ||
530 | + u8 value; | ||
531 | + int ret; | ||
532 | + unsigned long timeout; | ||
533 | + | ||
534 | + if (enable) { | ||
535 | + /* Check for USB charger conneted */ | ||
536 | + ret = twl4030charger_presence(); | ||
537 | + if (ret < 0) | ||
538 | + return ret; | ||
539 | + | ||
540 | + if (!(ret & USB_PW_CONN)) | ||
541 | + return -ENXIO; | ||
542 | + | ||
543 | + /* forcing the field BCIAUTOUSB (BOOT_BCI[1]) to 1 */ | ||
544 | + ret = clear_n_set(TWL4030_MODULE_PM_MASTER, 0, | ||
545 | + (CONFIG_DONE | BCIAUTOWEN | BCIAUTOUSB), | ||
546 | + REG_BOOT_BCI); | ||
547 | + if (ret) | ||
548 | + return ret; | ||
549 | + | ||
550 | + ret = clear_n_set(TWL4030_MODULE_USB, 0, PHY_DPLL_CLK, | ||
551 | + REG_PHY_CLK_CTRL); | ||
552 | + if (ret) | ||
553 | + return ret; | ||
554 | + | ||
555 | + value = 0; | ||
556 | + timeout = jiffies + msecs_to_jiffies(50); | ||
557 | + | ||
558 | + while ((!(value & PHY_DPLL_CLK)) && | ||
559 | + time_before(jiffies, timeout)) { | ||
560 | + udelay(10); | ||
561 | + ret = twl_i2c_read_u8(TWL4030_MODULE_USB, &value, | ||
562 | + REG_PHY_CLK_CTRL_STS); | ||
563 | + if (ret) | ||
564 | + return ret; | ||
565 | + } | ||
566 | + | ||
567 | + /* OTG_EN (POWER_CTRL[5]) to 1 */ | ||
568 | + ret = clear_n_set(TWL4030_MODULE_USB, 0, OTG_EN, | ||
569 | + REG_POWER_CTRL); | ||
570 | + if (ret) | ||
571 | + return ret; | ||
572 | + | ||
573 | + mdelay(50); | ||
574 | + | ||
575 | + /* forcing USBFASTMCHG(BCIMFSTS4[2]) to 1 */ | ||
576 | + ret = clear_n_set(TWL4030_MODULE_MAIN_CHARGE, 0, | ||
577 | + USBFASTMCHG, REG_BCIMFSTS4); | ||
578 | + if (ret) | ||
579 | + return ret; | ||
580 | + } else { | ||
581 | + twl4030charger_presence(); | ||
582 | + ret = clear_n_set(TWL4030_MODULE_PM_MASTER, BCIAUTOUSB, | ||
583 | + (CONFIG_DONE | BCIAUTOWEN), REG_BOOT_BCI); | ||
584 | + if (ret) | ||
585 | + return ret; | ||
586 | + } | ||
587 | + | ||
588 | + return 0; | ||
589 | +} | ||
590 | + | ||
591 | +/* | ||
592 | + * Return battery temperature | ||
593 | + * Or < 0 on failure. | ||
594 | + */ | ||
595 | +static int twl4030battery_temperature(struct twl4030_bci_device_info *di) | ||
596 | +{ | ||
597 | + u8 val; | ||
598 | + int temp, curr, volt, res, ret; | ||
599 | + | ||
600 | + /* Is a temperature table specified? */ | ||
601 | + if (!di->pdata->tblsize) | ||
602 | + return 0; | ||
603 | + | ||
604 | + /* Getting and calculating the thermistor voltage */ | ||
605 | + ret = read_bci_val(T2_BATTERY_TEMP); | ||
606 | + if (ret < 0) | ||
607 | + return ret; | ||
608 | + | ||
609 | + volt = (ret * TEMP_STEP_SIZE) / TEMP_PSR_R; | ||
610 | + | ||
611 | + /* Getting and calculating the supply current in micro ampers */ | ||
612 | + ret = twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, &val, | ||
613 | + REG_BCICTL2); | ||
614 | + if (ret) | ||
615 | + return 0; | ||
616 | + | ||
617 | + curr = ((val & ITHSENS) + 1) * 10; | ||
618 | + | ||
619 | + /* Getting and calculating the thermistor resistance in ohms*/ | ||
620 | + res = volt * 1000 / curr; | ||
621 | + | ||
622 | + /*calculating temperature*/ | ||
623 | + for (temp = 58; temp >= 0; temp--) { | ||
624 | + int actual = di->pdata->battery_tmp_tbl[temp]; | ||
625 | + if ((actual - res) >= 0) | ||
626 | + break; | ||
627 | + } | ||
628 | + | ||
629 | + /* Negative temperature */ | ||
630 | + if (temp < 3) { | ||
631 | + if (temp == 2) | ||
632 | + temp = -1; | ||
633 | + else if (temp == 1) | ||
634 | + temp = -2; | ||
635 | + else | ||
636 | + temp = -3; | ||
637 | + } | ||
638 | + | ||
639 | + return temp + 1; | ||
640 | +} | ||
641 | + | ||
642 | +/* | ||
643 | + * Return battery voltage | ||
644 | + * Or < 0 on failure. | ||
645 | + */ | ||
646 | +static int twl4030battery_voltage(void) | ||
647 | +{ | ||
648 | + int volt = read_bci_val(T2_BATTERY_VOLT); | ||
649 | + return (volt * VOLT_STEP_SIZE) / VOLT_PSR_R; | ||
650 | +} | ||
651 | + | ||
652 | +/* | ||
653 | + * Get latest battery voltage (using MADC) | ||
654 | + * | ||
655 | + * When the BCI is not charging, the BCI voltage registers are not | ||
656 | + * updated and are 'frozen' but the data can be read through the | ||
657 | + * MADC. | ||
658 | + */ | ||
659 | +static int twl4030battery_voltage_madc(void) | ||
660 | +{ | ||
661 | + struct twl4030_madc_request req; | ||
662 | + | ||
663 | + req.channels = (1 << 12); | ||
664 | + req.do_avg = 0; | ||
665 | + req.method = TWL4030_MADC_SW1; | ||
666 | + req.active = 0; | ||
667 | + req.func_cb = NULL; | ||
668 | + twl4030_madc_conversion(&req); | ||
669 | + | ||
670 | + return (((int) req.rbuf[12]) * VOLT_STEP_SIZE) / VOLT_PSR_R; | ||
671 | +} | ||
672 | + | ||
673 | +/* | ||
674 | + * Return the battery current | ||
675 | + * Or < 0 on failure. | ||
676 | + */ | ||
677 | +static int twl4030battery_current(void) | ||
678 | +{ | ||
679 | + int ret, curr = read_bci_val(T2_BATTERY_CUR); | ||
680 | + u8 val; | ||
681 | + | ||
682 | + ret = twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, &val, | ||
683 | + REG_BCICTL1); | ||
684 | + if (ret) | ||
685 | + return ret; | ||
686 | + | ||
687 | + if (val & CGAIN) /* slope of 0.44 mV/mA */ | ||
688 | + return (curr * CURR_STEP_SIZE) / CURR_PSR_R1; | ||
689 | + else /* slope of 0.88 mV/mA */ | ||
690 | + return (curr * CURR_STEP_SIZE) / CURR_PSR_R2; | ||
691 | +} | ||
692 | + | ||
693 | +/* | ||
694 | + * Return the battery backup voltage | ||
695 | + * Or < 0 on failure. | ||
696 | + */ | ||
697 | +static int twl4030backupbatt_voltage(void) | ||
698 | +{ | ||
699 | + struct twl4030_madc_request req; | ||
700 | + int temp; | ||
701 | + | ||
702 | + req.channels = (1 << 9); | ||
703 | + req.do_avg = 0; | ||
704 | + req.method = TWL4030_MADC_SW1; | ||
705 | + req.active = 0; | ||
706 | + req.func_cb = NULL; | ||
707 | + twl4030_madc_conversion(&req); | ||
708 | + temp = (u16)req.rbuf[9]; | ||
709 | + | ||
710 | + return (temp * BK_VOLT_STEP_SIZE) / BK_VOLT_PSR_R; | ||
711 | +} | ||
712 | + | ||
713 | +/* | ||
714 | + * Returns an integer value, that means, | ||
715 | + * NO_PW_CONN no power supply is connected | ||
716 | + * AC_PW_CONN if the AC power supply is connected | ||
717 | + * USB_PW_CONN if the USB power supply is connected | ||
718 | + * AC_PW_CONN + USB_PW_CONN if USB and AC power supplies are both connected | ||
719 | + * | ||
720 | + * Or < 0 on failure. | ||
721 | + */ | ||
722 | +static int twl4030charger_presence(void) | ||
723 | +{ | ||
724 | + int ret; | ||
725 | + u8 hwsts; | ||
726 | + | ||
727 | + ret = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &hwsts, | ||
728 | + REG_STS_HW_CONDITIONS); | ||
729 | + if (ret) { | ||
730 | + pr_err("twl4030_bci: error reading STS_HW_CONDITIONS\n"); | ||
731 | + return ret; | ||
732 | + } | ||
733 | + | ||
734 | + ret = (hwsts & STS_CHG) ? AC_PW_CONN : NO_PW_CONN; | ||
735 | + ret += (hwsts & STS_VBUS) ? USB_PW_CONN : NO_PW_CONN; | ||
736 | + | ||
737 | + if (ret & USB_PW_CONN) | ||
738 | + usb_charger_flag = 1; | ||
739 | + else | ||
740 | + usb_charger_flag = 0; | ||
741 | + | ||
742 | + return ret; | ||
743 | + | ||
744 | +} | ||
745 | + | ||
746 | +/* | ||
747 | + * Returns the main charge FSM status | ||
748 | + * Or < 0 on failure. | ||
749 | + */ | ||
750 | +static int twl4030bci_status(void) | ||
751 | +{ | ||
752 | + int ret; | ||
753 | + u8 status; | ||
754 | + | ||
755 | + ret = twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, | ||
756 | + &status, REG_BCIMSTATEC); | ||
757 | + if (ret) { | ||
758 | + pr_err("twl4030_bci: error reading BCIMSTATEC\n"); | ||
759 | + return ret; | ||
760 | + } | ||
761 | + | ||
762 | +#ifdef DEBUG | ||
763 | + printk("BCI DEBUG: BCIMSTATEC Charge state is 0x%x\n", status); | ||
764 | +#endif | ||
765 | + return (int) (status & BCIMSTAT_MASK); | ||
766 | +} | ||
767 | + | ||
768 | +static int read_bci_val(u8 reg) | ||
769 | +{ | ||
770 | + int ret, temp; | ||
771 | + u8 val; | ||
772 | + | ||
773 | + /* reading MSB */ | ||
774 | + ret = twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, &val, | ||
775 | + reg + 1); | ||
776 | + if (ret) | ||
777 | + return ret; | ||
778 | + | ||
779 | + temp = ((int)(val & 0x03)) << 8; | ||
780 | + | ||
781 | + /* reading LSB */ | ||
782 | + ret = twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, &val, | ||
783 | + reg); | ||
784 | + if (ret) | ||
785 | + return ret; | ||
786 | + | ||
787 | + return temp | val; | ||
788 | +} | ||
789 | + | ||
790 | +/* | ||
791 | + * Settup the twl4030 BCI module to enable backup | ||
792 | + * battery charging. | ||
793 | + */ | ||
794 | +static int twl4030backupbatt_voltage_setup(void) | ||
795 | +{ | ||
796 | + int ret; | ||
797 | + | ||
798 | + /* Starting backup batery charge */ | ||
799 | + ret = clear_n_set(TWL4030_MODULE_PM_RECEIVER, 0, BBCHEN, | ||
800 | + REG_BB_CFG); | ||
801 | + if (ret) | ||
802 | + return ret; | ||
803 | + | ||
804 | + return 0; | ||
805 | +} | ||
806 | + | ||
807 | +/* | ||
808 | + * Settup the twl4030 BCI module to measure battery | ||
809 | + * temperature | ||
810 | + */ | ||
811 | +static int twl4030battery_temp_setup(void) | ||
812 | +{ | ||
813 | +#ifdef DEBUG | ||
814 | + u8 i; | ||
815 | +#endif | ||
816 | + u8 ret; | ||
817 | + | ||
818 | + /* Enabling thermistor current */ | ||
819 | + ret = clear_n_set(TWL4030_MODULE_MAIN_CHARGE, 0, 0x1B, | ||
820 | + REG_BCICTL1); | ||
821 | + if (ret) | ||
822 | + return ret; | ||
823 | + | ||
824 | +#ifdef DEBUG | ||
825 | + twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &ret, REG_BOOT_BCI); | ||
826 | + printk("BCI DEBUG: BOOT_BCI Value is 0x%x\n", ret); | ||
827 | + | ||
828 | + twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &ret, REG_STS_HW_CONDITIONS); | ||
829 | + printk("BCI DEBUG: STS_HW_CONDITIONS Value is 0x%x\n", ret); | ||
830 | + | ||
831 | + twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, &ret, REG_BCICTL1); | ||
832 | + printk("BCI DEBUG: BCICTL1 Value is 0x%x\n", ret); | ||
833 | + | ||
834 | + twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, &ret, REG_BCICTL2); | ||
835 | + printk("BCI DEBUG: BCICTL2 Value is 0x%x\n", ret); | ||
836 | + | ||
837 | + twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, &ret, 0x0); | ||
838 | + printk("BCI DEBUG: BCIMDEN Value is 0x%x\n", ret); | ||
839 | + | ||
840 | + twl_i2c_read_u8(TWL4030_MODULE_INTBR, &ret, REG_GPBR1); | ||
841 | + printk("BCI DEBUG: GPBR1 Value is 0x%x\n", ret); | ||
842 | + | ||
843 | + for(i = 0x0; i <= 0x32; i++) | ||
844 | + { | ||
845 | + twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, &ret, i); | ||
846 | + printk("BCI DEBUG: BCI 0x%x Value is 0x%x\n", i, ret); | ||
847 | + } | ||
848 | +#endif | ||
849 | + | ||
850 | + return 0; | ||
851 | +} | ||
852 | + | ||
853 | +/* | ||
854 | + * Sets and clears bits on an given register on a given module | ||
855 | + */ | ||
856 | +static inline int clear_n_set(u8 mod_no, u8 clear, u8 set, u8 reg) | ||
857 | +{ | ||
858 | + int ret; | ||
859 | + u8 val = 0; | ||
860 | + | ||
861 | + /* Gets the initial register value */ | ||
862 | + ret = twl_i2c_read_u8(mod_no, &val, reg); | ||
863 | + if (ret) | ||
864 | + return ret; | ||
865 | + /* Clearing all those bits to clear */ | ||
866 | + val &= ~(clear); | ||
867 | + | ||
868 | + /* Setting all those bits to set */ | ||
869 | + val |= set; | ||
870 | + | ||
871 | + /* Update the register */ | ||
872 | + ret = twl_i2c_write_u8(mod_no, val, reg); | ||
873 | + if (ret) | ||
874 | + return ret; | ||
875 | + | ||
876 | + return 0; | ||
877 | +} | ||
878 | + | ||
879 | +static enum power_supply_property twl4030_bci_battery_props[] = { | ||
880 | + POWER_SUPPLY_PROP_STATUS, | ||
881 | + POWER_SUPPLY_PROP_ONLINE, | ||
882 | + POWER_SUPPLY_PROP_VOLTAGE_NOW, | ||
883 | + POWER_SUPPLY_PROP_CURRENT_NOW, | ||
884 | + POWER_SUPPLY_PROP_CAPACITY, | ||
885 | + POWER_SUPPLY_PROP_TEMP, | ||
886 | +}; | ||
887 | + | ||
888 | +static enum power_supply_property twl4030_bk_bci_battery_props[] = { | ||
889 | + POWER_SUPPLY_PROP_VOLTAGE_NOW, | ||
890 | +}; | ||
891 | + | ||
892 | +static void | ||
893 | +twl4030_bk_bci_battery_read_status(struct twl4030_bci_device_info *di) | ||
894 | +{ | ||
895 | + di->bk_voltage_uV = twl4030backupbatt_voltage(); | ||
896 | +} | ||
897 | + | ||
898 | +static void twl4030_bk_bci_battery_work(struct work_struct *work) | ||
899 | +{ | ||
900 | + struct twl4030_bci_device_info *di = container_of(work, | ||
901 | + struct twl4030_bci_device_info, | ||
902 | + twl4030_bk_bci_monitor_work.work); | ||
903 | + | ||
904 | + if(!di->pdata->no_backup_battery) | ||
905 | + twl4030_bk_bci_battery_read_status(di); | ||
906 | + schedule_delayed_work(&di->twl4030_bk_bci_monitor_work, 500); | ||
907 | +} | ||
908 | + | ||
909 | +static void twl4030_bci_battery_read_status(struct twl4030_bci_device_info *di) | ||
910 | +{ | ||
911 | + if(di->charge_status != POWER_SUPPLY_STATUS_DISCHARGING) { | ||
912 | + di->temp_C = twl4030battery_temperature(di); | ||
913 | + di->voltage_uV = twl4030battery_voltage(); | ||
914 | + di->current_uA = twl4030battery_current(); | ||
915 | + } | ||
916 | +} | ||
917 | + | ||
918 | +static void | ||
919 | +twl4030_bci_battery_update_status(struct twl4030_bci_device_info *di) | ||
920 | +{ | ||
921 | + if (power_supply_am_i_supplied(&di->bat)) | ||
922 | + di->charge_status = POWER_SUPPLY_STATUS_CHARGING; | ||
923 | + else | ||
924 | + di->charge_status = POWER_SUPPLY_STATUS_DISCHARGING; | ||
925 | + twl4030_bci_battery_read_status(di); | ||
926 | +} | ||
927 | + | ||
928 | +static void twl4030_bci_battery_work(struct work_struct *work) | ||
929 | +{ | ||
930 | + struct twl4030_bci_device_info *di = container_of(work, | ||
931 | + struct twl4030_bci_device_info, twl4030_bci_monitor_work.work); | ||
932 | + | ||
933 | + twl4030_bci_battery_update_status(di); | ||
934 | + schedule_delayed_work(&di->twl4030_bci_monitor_work, 100); | ||
935 | +} | ||
936 | + | ||
937 | + | ||
938 | +#define to_twl4030_bci_device_info(x) container_of((x), \ | ||
939 | + struct twl4030_bci_device_info, bat); | ||
940 | + | ||
941 | +static void twl4030_bci_battery_external_power_changed(struct power_supply *psy) | ||
942 | +{ | ||
943 | + struct twl4030_bci_device_info *di = to_twl4030_bci_device_info(psy); | ||
944 | + | ||
945 | + cancel_delayed_work(&di->twl4030_bci_monitor_work); | ||
946 | + schedule_delayed_work(&di->twl4030_bci_monitor_work, 0); | ||
947 | +} | ||
948 | + | ||
949 | +#define to_twl4030_bk_bci_device_info(x) container_of((x), \ | ||
950 | + struct twl4030_bci_device_info, bk_bat); | ||
951 | + | ||
952 | +static ssize_t | ||
953 | +show_charge_current(struct device *dev, struct device_attribute *attr, char *buf) | ||
954 | +{ | ||
955 | + u8 ctl; | ||
956 | + int ret = read_bci_val(REG_BCIIREF1) & 0x1FF; | ||
957 | + twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, &ctl, REG_BCICTL1); | ||
958 | + | ||
959 | + if (ctl & CGAIN) | ||
960 | + ret |= 0x200; | ||
961 | + | ||
962 | +#ifdef DEBUG | ||
963 | + /* Dump debug */ | ||
964 | + twl4030battery_temp_setup(); | ||
965 | +#endif | ||
966 | + | ||
967 | + return sprintf(buf, "%d\n", ret); | ||
968 | +} | ||
969 | + | ||
970 | +static ssize_t | ||
971 | +set_charge_current(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) | ||
972 | +{ | ||
973 | + unsigned long newCurrent; | ||
974 | + int ret; | ||
975 | + | ||
976 | + ret = strict_strtoul(buf, 10, &newCurrent); | ||
977 | + if (ret) | ||
978 | + return -EINVAL; | ||
979 | + | ||
980 | + ret = twl_i2c_write_u8(TWL4030_MODULE_MAIN_CHARGE, KEY_IIREF, REG_BCIMFKEY); | ||
981 | + if (ret) | ||
982 | + return ret; | ||
983 | + | ||
984 | + ret = twl_i2c_write_u8(TWL4030_MODULE_MAIN_CHARGE, newCurrent & 0xff, REG_BCIIREF1); | ||
985 | + if (ret) | ||
986 | + return ret; | ||
987 | + | ||
988 | + ret = twl_i2c_write_u8(TWL4030_MODULE_MAIN_CHARGE, KEY_IIREF, REG_BCIMFKEY); | ||
989 | + if (ret) | ||
990 | + return ret; | ||
991 | + | ||
992 | + ret = twl_i2c_write_u8(TWL4030_MODULE_MAIN_CHARGE, (newCurrent >> 8) & 0x1, REG_BCIIREF2); | ||
993 | + if (ret) | ||
994 | + return ret; | ||
995 | + | ||
996 | + /* Set software-controlled charge */ | ||
997 | + twl4030charger_ac_en(ENABLE, 0); | ||
998 | + | ||
999 | + /* Set CGAIN = 0 or 1 */ | ||
1000 | + if(newCurrent > 511) { | ||
1001 | + u8 tmp; | ||
1002 | + | ||
1003 | + /* Set CGAIN = 1 -- need to wait until automatic charge turns off */ | ||
1004 | + while(!ret) { | ||
1005 | + clear_n_set(TWL4030_MODULE_MAIN_CHARGE, 0, CGAIN | 0x1B, REG_BCICTL1); | ||
1006 | + twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, &tmp, REG_BCICTL1); | ||
1007 | + | ||
1008 | + ret = tmp & CGAIN; | ||
1009 | + if(!ret) | ||
1010 | + mdelay(50); | ||
1011 | + } | ||
1012 | + } else { | ||
1013 | + u8 tmp; | ||
1014 | + | ||
1015 | + /* Set CGAIN = 0 -- need to wait until automatic charge turns off */ | ||
1016 | + while(!ret) { | ||
1017 | + clear_n_set(TWL4030_MODULE_MAIN_CHARGE, CGAIN, 0x1B, REG_BCICTL1); | ||
1018 | + twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, &tmp, REG_BCICTL1); | ||
1019 | + | ||
1020 | + ret = !(tmp & CGAIN); | ||
1021 | + if(!ret) | ||
1022 | + mdelay(50); | ||
1023 | + } | ||
1024 | + } | ||
1025 | + | ||
1026 | + /* Set automatic charge (CGAIN = 0/1 persists) */ | ||
1027 | + twl4030charger_ac_en(ENABLE, 1); | ||
1028 | + | ||
1029 | + return count; | ||
1030 | +} | ||
1031 | + | ||
1032 | +static ssize_t | ||
1033 | +show_voltage(struct device *dev, struct device_attribute *attr, char *buf) | ||
1034 | +{ | ||
1035 | + return sprintf(buf, "%d\n", twl4030battery_voltage_madc()); | ||
1036 | +} | ||
1037 | + | ||
1038 | +static DEVICE_ATTR(charge_current, S_IRUGO | S_IWUGO, show_charge_current, set_charge_current); | ||
1039 | +static DEVICE_ATTR(voltage_now_madc, S_IRUGO, show_voltage, NULL); | ||
1040 | + | ||
1041 | +static int twl4030_bk_bci_battery_get_property(struct power_supply *psy, | ||
1042 | + enum power_supply_property psp, | ||
1043 | + union power_supply_propval *val) | ||
1044 | +{ | ||
1045 | + struct twl4030_bci_device_info *di = to_twl4030_bk_bci_device_info(psy); | ||
1046 | + | ||
1047 | + switch (psp) { | ||
1048 | + case POWER_SUPPLY_PROP_VOLTAGE_NOW: | ||
1049 | + val->intval = di->bk_voltage_uV; | ||
1050 | + break; | ||
1051 | + default: | ||
1052 | + return -EINVAL; | ||
1053 | + } | ||
1054 | + | ||
1055 | + return 0; | ||
1056 | +} | ||
1057 | + | ||
1058 | +static int twl4030_bci_battery_get_property(struct power_supply *psy, | ||
1059 | + enum power_supply_property psp, | ||
1060 | + union power_supply_propval *val) | ||
1061 | +{ | ||
1062 | + struct twl4030_bci_device_info *di; | ||
1063 | + int status = 0; | ||
1064 | + | ||
1065 | + di = to_twl4030_bci_device_info(psy); | ||
1066 | + | ||
1067 | + switch (psp) { | ||
1068 | + case POWER_SUPPLY_PROP_STATUS: | ||
1069 | + val->intval = di->charge_status; | ||
1070 | + return 0; | ||
1071 | + default: | ||
1072 | + break; | ||
1073 | + } | ||
1074 | + | ||
1075 | + switch (psp) { | ||
1076 | + case POWER_SUPPLY_PROP_VOLTAGE_NOW: | ||
1077 | + { | ||
1078 | + /* Get latest data from MADC -- not done periodically by | ||
1079 | + worker as this is more expensive, so only do it when we | ||
1080 | + are actually asked for the data... */ | ||
1081 | + if(di->charge_status == POWER_SUPPLY_STATUS_DISCHARGING) | ||
1082 | + val->intval = twl4030battery_voltage_madc(); | ||
1083 | + else | ||
1084 | + val->intval = di->voltage_uV; | ||
1085 | + | ||
1086 | + break; | ||
1087 | + } | ||
1088 | + case POWER_SUPPLY_PROP_CURRENT_NOW: | ||
1089 | + /* FIXME: Get from MADC */ | ||
1090 | + if(di->charge_status == POWER_SUPPLY_STATUS_DISCHARGING) | ||
1091 | + val->intval = 0; | ||
1092 | + else | ||
1093 | + val->intval = di->current_uA; | ||
1094 | + break; | ||
1095 | + case POWER_SUPPLY_PROP_TEMP: | ||
1096 | + val->intval = di->temp_C; | ||
1097 | + break; | ||
1098 | + case POWER_SUPPLY_PROP_ONLINE: | ||
1099 | + status = twl4030bci_status(); | ||
1100 | + if ((status & AC_STATEC) == AC_STATEC) | ||
1101 | + val->intval = POWER_SUPPLY_TYPE_MAINS; | ||
1102 | + else if (usb_charger_flag) | ||
1103 | + val->intval = POWER_SUPPLY_TYPE_USB; | ||
1104 | + else | ||
1105 | + val->intval = 0; | ||
1106 | + break; | ||
1107 | + case POWER_SUPPLY_PROP_CAPACITY: | ||
1108 | + /* Get latest data from MADC -- not done periodically by | ||
1109 | + worker as this is more expensive, so only do it when we | ||
1110 | + are actually asked for the data... */ | ||
1111 | + if(di->charge_status == POWER_SUPPLY_STATUS_DISCHARGING) | ||
1112 | + di->voltage_uV = twl4030battery_voltage_madc(); | ||
1113 | + | ||
1114 | + /* | ||
1115 | + * need to get the correct percentage value per the | ||
1116 | + * battery characteristics. Approx values for now. | ||
1117 | + */ | ||
1118 | + if (di->voltage_uV < 2894 || LVL_1) { | ||
1119 | + val->intval = 5; | ||
1120 | + LVL_1 = 0; | ||
1121 | + } else if ((di->voltage_uV < 3451 && di->voltage_uV > 2894) | ||
1122 | + || LVL_2) { | ||
1123 | + val->intval = 20; | ||
1124 | + LVL_2 = 0; | ||
1125 | + } else if ((di->voltage_uV < 3902 && di->voltage_uV > 3451) | ||
1126 | + || LVL_3) { | ||
1127 | + val->intval = 50; | ||
1128 | + LVL_3 = 0; | ||
1129 | + } else if ((di->voltage_uV < 3949 && di->voltage_uV > 3902) | ||
1130 | + || LVL_4) { | ||
1131 | + val->intval = 75; | ||
1132 | + LVL_4 = 0; | ||
1133 | + } else if (di->voltage_uV > 3949) | ||
1134 | + val->intval = 90; | ||
1135 | + break; | ||
1136 | + default: | ||
1137 | + return -EINVAL; | ||
1138 | + } | ||
1139 | + return 0; | ||
1140 | +} | ||
1141 | + | ||
1142 | +static char *twl4030_bci_supplied_to[] = { | ||
1143 | + "twl4030_bci_battery", | ||
1144 | +}; | ||
1145 | + | ||
1146 | +static int __init twl4030_bci_battery_probe(struct platform_device *pdev) | ||
1147 | +{ | ||
1148 | + struct twl4030_bci_platform_data *pdata = pdev->dev.platform_data; | ||
1149 | + struct twl4030_bci_device_info *di; | ||
1150 | + int irq; | ||
1151 | + int ret; | ||
1152 | + | ||
1153 | + di = kzalloc(sizeof(*di), GFP_KERNEL); | ||
1154 | + if (!di) | ||
1155 | + return -ENOMEM; | ||
1156 | + | ||
1157 | + di->dev = &pdev->dev; | ||
1158 | + di->bat.name = "twl4030_bci_battery"; | ||
1159 | + di->bat.supplied_to = twl4030_bci_supplied_to; | ||
1160 | + di->bat.num_supplicants = ARRAY_SIZE(twl4030_bci_supplied_to); | ||
1161 | + di->bat.type = POWER_SUPPLY_TYPE_BATTERY; | ||
1162 | + di->bat.properties = twl4030_bci_battery_props; | ||
1163 | + di->bat.num_properties = ARRAY_SIZE(twl4030_bci_battery_props); | ||
1164 | + di->bat.get_property = twl4030_bci_battery_get_property; | ||
1165 | + di->bat.external_power_changed = | ||
1166 | + twl4030_bci_battery_external_power_changed; | ||
1167 | + | ||
1168 | + di->charge_status = POWER_SUPPLY_STATUS_UNKNOWN; | ||
1169 | + | ||
1170 | + di->bk_bat.name = "twl4030_bci_bk_battery"; | ||
1171 | + di->bk_bat.type = POWER_SUPPLY_TYPE_BATTERY; | ||
1172 | + di->bk_bat.properties = twl4030_bk_bci_battery_props; | ||
1173 | + di->bk_bat.num_properties = ARRAY_SIZE(twl4030_bk_bci_battery_props); | ||
1174 | + di->bk_bat.get_property = twl4030_bk_bci_battery_get_property; | ||
1175 | + di->bk_bat.external_power_changed = NULL; | ||
1176 | + di->pdata = pdata; | ||
1177 | + | ||
1178 | + /* Set up clocks */ | ||
1179 | + twl_i2c_write_u8(TWL4030_MODULE_INTBR, MADC_HFCLK_EN | DEFAULT_MADC_CLK_EN, REG_GPBR1); | ||
1180 | + | ||
1181 | + twl4030charger_ac_en(ENABLE, CHARGE_MODE); | ||
1182 | + twl4030charger_usb_en(ENABLE); | ||
1183 | + twl4030battery_hw_level_en(ENABLE); | ||
1184 | + twl4030battery_hw_presence_en(ENABLE); | ||
1185 | + | ||
1186 | + platform_set_drvdata(pdev, di); | ||
1187 | + | ||
1188 | + /* settings for temperature sensing */ | ||
1189 | + ret = twl4030battery_temp_setup(); | ||
1190 | + if (ret) | ||
1191 | + goto temp_setup_fail; | ||
1192 | + | ||
1193 | + /* enabling GPCH09 for read back battery voltage */ | ||
1194 | + if(!di->pdata->no_backup_battery) | ||
1195 | + { | ||
1196 | + ret = twl4030backupbatt_voltage_setup(); | ||
1197 | + if (ret) | ||
1198 | + goto voltage_setup_fail; | ||
1199 | + } | ||
1200 | + | ||
1201 | + /* REVISIT do we need to request both IRQs ?? */ | ||
1202 | + | ||
1203 | + /* request BCI interruption */ | ||
1204 | + irq = platform_get_irq(pdev, 1); | ||
1205 | + ret = request_irq(irq, twl4030battery_interrupt, | ||
1206 | + 0, pdev->name, NULL); | ||
1207 | + if (ret) { | ||
1208 | + dev_dbg(&pdev->dev, "could not request irq %d, status %d\n", | ||
1209 | + irq, ret); | ||
1210 | + goto batt_irq_fail; | ||
1211 | + } | ||
1212 | + | ||
1213 | + /* request Power interruption */ | ||
1214 | + irq = platform_get_irq(pdev, 0); | ||
1215 | + ret = request_irq(irq, twl4030charger_interrupt, | ||
1216 | + 0, pdev->name, di); | ||
1217 | + | ||
1218 | + if (ret) { | ||
1219 | + dev_dbg(&pdev->dev, "could not request irq %d, status %d\n", | ||
1220 | + irq, ret); | ||
1221 | + goto chg_irq_fail; | ||
1222 | + } | ||
1223 | + | ||
1224 | + ret = power_supply_register(&pdev->dev, &di->bat); | ||
1225 | + if (ret) { | ||
1226 | + dev_dbg(&pdev->dev, "failed to register main battery\n"); | ||
1227 | + goto batt_failed; | ||
1228 | + } | ||
1229 | + | ||
1230 | + INIT_DELAYED_WORK_DEFERRABLE(&di->twl4030_bci_monitor_work, | ||
1231 | + twl4030_bci_battery_work); | ||
1232 | + schedule_delayed_work(&di->twl4030_bci_monitor_work, 0); | ||
1233 | + | ||
1234 | + if(!pdata->no_backup_battery) | ||
1235 | + { | ||
1236 | + ret = power_supply_register(&pdev->dev, &di->bk_bat); | ||
1237 | + if (ret) { | ||
1238 | + dev_dbg(&pdev->dev, "failed to register backup battery\n"); | ||
1239 | + goto bk_batt_failed; | ||
1240 | + } | ||
1241 | + } | ||
1242 | + | ||
1243 | + ret = device_create_file(di->bat.dev, &dev_attr_voltage_now_madc); | ||
1244 | + ret = device_create_file(di->bat.dev, &dev_attr_charge_current); | ||
1245 | + if (ret) { | ||
1246 | + dev_err(&pdev->dev, "failed to create sysfs entries\n"); | ||
1247 | + goto bk_batt_failed; | ||
1248 | + } | ||
1249 | + | ||
1250 | + INIT_DELAYED_WORK_DEFERRABLE(&di->twl4030_bk_bci_monitor_work, | ||
1251 | + twl4030_bk_bci_battery_work); | ||
1252 | + schedule_delayed_work(&di->twl4030_bk_bci_monitor_work, 500); | ||
1253 | + | ||
1254 | + set_charge_current (NULL, NULL, "1023", 4); | ||
1255 | + return 0; | ||
1256 | + | ||
1257 | +bk_batt_failed: | ||
1258 | + if(!pdata->no_backup_battery) | ||
1259 | + power_supply_unregister(&di->bat); | ||
1260 | +batt_failed: | ||
1261 | + free_irq(irq, di); | ||
1262 | +chg_irq_fail: | ||
1263 | + irq = platform_get_irq(pdev, 1); | ||
1264 | + free_irq(irq, NULL); | ||
1265 | +batt_irq_fail: | ||
1266 | +voltage_setup_fail: | ||
1267 | +temp_setup_fail: | ||
1268 | + twl4030charger_ac_en(DISABLE, CHARGE_MODE); | ||
1269 | + twl4030charger_usb_en(DISABLE); | ||
1270 | + twl4030battery_hw_level_en(DISABLE); | ||
1271 | + twl4030battery_hw_presence_en(DISABLE); | ||
1272 | + kfree(di); | ||
1273 | + | ||
1274 | + return ret; | ||
1275 | +} | ||
1276 | + | ||
1277 | +static int __exit twl4030_bci_battery_remove(struct platform_device *pdev) | ||
1278 | +{ | ||
1279 | + struct twl4030_bci_device_info *di = platform_get_drvdata(pdev); | ||
1280 | + int irq; | ||
1281 | + | ||
1282 | + twl4030charger_ac_en(DISABLE, CHARGE_MODE); | ||
1283 | + twl4030charger_usb_en(DISABLE); | ||
1284 | + twl4030battery_hw_level_en(DISABLE); | ||
1285 | + twl4030battery_hw_presence_en(DISABLE); | ||
1286 | + | ||
1287 | + irq = platform_get_irq(pdev, 0); | ||
1288 | + free_irq(irq, di); | ||
1289 | + | ||
1290 | + irq = platform_get_irq(pdev, 1); | ||
1291 | + free_irq(irq, NULL); | ||
1292 | + | ||
1293 | + flush_scheduled_work(); | ||
1294 | + power_supply_unregister(&di->bat); | ||
1295 | + power_supply_unregister(&di->bk_bat); | ||
1296 | + platform_set_drvdata(pdev, NULL); | ||
1297 | + kfree(di); | ||
1298 | + | ||
1299 | + return 0; | ||
1300 | +} | ||
1301 | + | ||
1302 | +#ifdef CONFIG_PM | ||
1303 | +static int twl4030_bci_battery_suspend(struct platform_device *pdev, | ||
1304 | + pm_message_t state) | ||
1305 | +{ | ||
1306 | + struct twl4030_bci_device_info *di = platform_get_drvdata(pdev); | ||
1307 | + | ||
1308 | + di->charge_status = POWER_SUPPLY_STATUS_UNKNOWN; | ||
1309 | + cancel_delayed_work(&di->twl4030_bci_monitor_work); | ||
1310 | + cancel_delayed_work(&di->twl4030_bk_bci_monitor_work); | ||
1311 | + return 0; | ||
1312 | +} | ||
1313 | + | ||
1314 | +static int twl4030_bci_battery_resume(struct platform_device *pdev) | ||
1315 | +{ | ||
1316 | + struct twl4030_bci_device_info *di = platform_get_drvdata(pdev); | ||
1317 | + | ||
1318 | + schedule_delayed_work(&di->twl4030_bci_monitor_work, 0); | ||
1319 | + schedule_delayed_work(&di->twl4030_bk_bci_monitor_work, 50); | ||
1320 | + return 0; | ||
1321 | +} | ||
1322 | +#else | ||
1323 | +#define twl4030_bci_battery_suspend NULL | ||
1324 | +#define twl4030_bci_battery_resume NULL | ||
1325 | +#endif /* CONFIG_PM */ | ||
1326 | + | ||
1327 | +static struct platform_driver twl4030_bci_battery_driver = { | ||
1328 | + .probe = twl4030_bci_battery_probe, | ||
1329 | + .remove = __exit_p(twl4030_bci_battery_remove), | ||
1330 | + .suspend = twl4030_bci_battery_suspend, | ||
1331 | + .resume = twl4030_bci_battery_resume, | ||
1332 | + .driver = { | ||
1333 | + .name = "twl4030_bci", | ||
1334 | + }, | ||
1335 | +}; | ||
1336 | + | ||
1337 | +MODULE_LICENSE("GPL"); | ||
1338 | +MODULE_ALIAS("platform:twl4030_bci"); | ||
1339 | +MODULE_AUTHOR("Texas Instruments Inc"); | ||
1340 | + | ||
1341 | +static int __init twl4030_battery_init(void) | ||
1342 | +{ | ||
1343 | + return platform_driver_register(&twl4030_bci_battery_driver); | ||
1344 | +} | ||
1345 | +module_init(twl4030_battery_init); | ||
1346 | + | ||
1347 | +static void __exit twl4030_battery_exit(void) | ||
1348 | +{ | ||
1349 | + platform_driver_unregister(&twl4030_bci_battery_driver); | ||
1350 | +} | ||
1351 | +module_exit(twl4030_battery_exit); | ||
1352 | + | ||
1353 | diff --git a/include/linux/i2c/twl.h b/include/linux/i2c/twl.h | ||
1354 | index d975c5b..a3470ce 100644 | ||
1355 | --- a/include/linux/i2c/twl.h | ||
1356 | +++ b/include/linux/i2c/twl.h | ||
1357 | @@ -442,6 +442,7 @@ struct twl4030_clock_init_data { | ||
1358 | struct twl4030_bci_platform_data { | ||
1359 | int *battery_tmp_tbl; | ||
1360 | unsigned int tblsize; | ||
1361 | + bool no_backup_battery; | ||
1362 | }; | ||
1363 | |||
1364 | /* TWL4030_GPIO_MAX (18) GPIOs, with interrupts */ | ||
1365 | -- | ||
1366 | 1.6.6.1 | ||
1367 | |||