aboutsummaryrefslogtreecommitdiffstats
path: root/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
diff options
context:
space:
mode:
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.patch1367
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 @@
+From c3a08f3d696866508ef2b5e2fd065b8295b3e1a8 Mon Sep 17 00:00:00 2001
+From: Tim Yamin <plasm@roo.me.uk>
+Date: Sun, 9 May 2010 10:14:23 +0200
+Subject: [PATCH 15/17] Forward port TWL4030 BCI driver from 2.6.29 to 2.6.31 with AI enhancements.
+
+Signed-off-by: Tim Yamin <plasm@roo.me.uk>
+---
+ drivers/power/Kconfig | 7 +
+ drivers/power/Makefile | 1 +
+ drivers/power/twl4030_bci_battery.c | 1307 +++++++++++++++++++++++++++++++++++
+ include/linux/i2c/twl.h | 1 +
+ 4 files changed, 1316 insertions(+), 0 deletions(-)
+ create mode 100644 drivers/power/twl4030_bci_battery.c
+
+diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
+index d4b3d67..8345b3f 100644
+--- a/drivers/power/Kconfig
++++ b/drivers/power/Kconfig
+@@ -124,4 +124,11 @@ config CHARGER_PCF50633
+ help
+ Say Y to include support for NXP PCF50633 Main Battery Charger.
+
++config TWL4030_BCI_BATTERY
++ tristate "OMAP TWL4030 BCI Battery driver"
++ depends on TWL4030_CORE && TWL4030_MADC
++ help
++ Support for OMAP TWL4030 BCI Battery driver.
++ This driver can give support for TWL4030 Battery Charge Interface.
++
+ endif # POWER_SUPPLY
+diff --git a/drivers/power/Makefile b/drivers/power/Makefile
+index 573597c..7801da7 100644
+--- a/drivers/power/Makefile
++++ b/drivers/power/Makefile
+@@ -31,3 +31,4 @@ obj-$(CONFIG_BATTERY_BQ27x00) += bq27x00_battery.o
+ obj-$(CONFIG_BATTERY_DA9030) += da9030_battery.o
+ obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o
+ obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o
++obj-$(CONFIG_TWL4030_BCI_BATTERY) += twl4030_bci_battery.o
+diff --git a/drivers/power/twl4030_bci_battery.c b/drivers/power/twl4030_bci_battery.c
+new file mode 100644
+index 0000000..0876fc3
+--- /dev/null
++++ b/drivers/power/twl4030_bci_battery.c
+@@ -0,0 +1,1307 @@
++/*
++ * linux/drivers/power/twl4030_bci_battery.c
++ *
++ * OMAP2430/3430 BCI battery driver for Linux
++ *
++ * Copyright (C) 2008 Texas Instruments, Inc.
++ * Author: Texas Instruments, Inc.
++ *
++ * Copyright (C) 2010 Always Innovating
++ * Author: Tim Yamin <plasm@roo.me.uk>
++ *
++ * This package is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
++ */
++
++/* Boot with automatic charge */
++#define CHARGE_MODE 1
++
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/device.h>
++#include <linux/interrupt.h>
++#include <linux/delay.h>
++#include <linux/platform_device.h>
++#include <linux/i2c/twl.h>
++#include <linux/power_supply.h>
++#include <linux/i2c/twl4030-madc.h>
++
++#define T2_BATTERY_VOLT 0x04
++#define T2_BATTERY_TEMP 0x06
++#define T2_BATTERY_CUR 0x08
++
++/* charger constants */
++#define NO_PW_CONN 0
++#define AC_PW_CONN 0x01
++#define USB_PW_CONN 0x02
++
++/* TWL4030_MODULE_USB */
++#define REG_POWER_CTRL 0x0AC
++#define OTG_EN 0x020
++#define REG_PHY_CLK_CTRL 0x0FE
++#define REG_PHY_CLK_CTRL_STS 0x0FF
++#define PHY_DPLL_CLK 0x01
++
++#define REG_BCICTL1 0x023
++#define REG_BCICTL2 0x024
++#define CGAIN 0x020
++#define ITHEN 0x010
++#define ITHSENS 0x007
++
++/* Boot BCI flag bits */
++#define BCIAUTOWEN 0x020
++#define CONFIG_DONE 0x010
++#define CVENAC 0x004
++#define BCIAUTOUSB 0x002
++#define BCIAUTOAC 0x001
++#define BCIMSTAT_MASK 0x03F
++
++/* Boot BCI register */
++#define REG_BOOT_BCI 0x007
++#define REG_CTRL1 0x00
++#define REG_SW1SELECT_MSB 0x07
++#define SW1_CH9_SEL 0x02
++#define REG_CTRL_SW1 0x012
++#define SW1_TRIGGER 0x020
++#define EOC_SW1 0x002
++#define REG_GPCH9 0x049
++#define REG_STS_HW_CONDITIONS 0x0F
++#define STS_VBUS 0x080
++#define STS_CHG 0x02
++#define REG_BCIMSTATEC 0x02
++#define REG_BCIMFSTS4 0x010
++#define REG_BCIMFSTS2 0x00E
++#define REG_BCIMFSTS3 0x00F
++#define REG_BCIMFSTS1 0x001
++#define USBFASTMCHG 0x004
++#define BATSTSPCHG 0x004
++#define BATSTSMCHG 0x040
++#define VBATOV4 0x020
++#define VBATOV3 0x010
++#define VBATOV2 0x008
++#define VBATOV1 0x004
++#define REG_BB_CFG 0x012
++#define BBCHEN 0x010
++
++/* GPBR */
++#define REG_GPBR1 0x0c
++#define MADC_HFCLK_EN 0x80
++#define DEFAULT_MADC_CLK_EN 0x10
++
++/* Power supply charge interrupt */
++#define REG_PWR_ISR1 0x00
++#define REG_PWR_IMR1 0x01
++#define REG_PWR_EDR1 0x05
++#define REG_PWR_SIH_CTRL 0x007
++
++#define USB_PRES 0x004
++#define CHG_PRES 0x002
++
++#define USB_PRES_RISING 0x020
++#define USB_PRES_FALLING 0x010
++#define CHG_PRES_RISING 0x008
++#define CHG_PRES_FALLING 0x004
++#define AC_STATEC 0x20
++#define COR 0x004
++
++/* interrupt status registers */
++#define REG_BCIISR1A 0x0
++#define REG_BCIISR2A 0x01
++
++/* Interrupt flags bits BCIISR1 */
++#define BATSTS_ISR1 0x080
++#define VBATLVL_ISR1 0x001
++
++/* Interrupt mask registers for int1*/
++#define REG_BCIIMR1A 0x002
++#define REG_BCIIMR2A 0x003
++
++ /* Interrupt masks for BCIIMR1 */
++#define BATSTS_IMR1 0x080
++#define VBATLVL_IMR1 0x001
++
++/* Interrupt edge detection register */
++#define REG_BCIEDR1 0x00A
++#define REG_BCIEDR2 0x00B
++#define REG_BCIEDR3 0x00C
++
++/* BCIEDR2 */
++#define BATSTS_EDRRISIN 0x080
++#define BATSTS_EDRFALLING 0x040
++
++/* BCIEDR3 */
++#define VBATLVL_EDRRISIN 0x02
++
++/* BCIIREF1 */
++#define REG_BCIIREF1 0x027
++#define REG_BCIIREF2 0x028
++
++/* BCIMFTH1 */
++#define REG_BCIMFTH1 0x016
++
++/* Key */
++#define KEY_IIREF 0xE7
++#define KEY_FTH1 0xD2
++#define REG_BCIMFKEY 0x011
++
++/* Step size and prescaler ratio */
++#define TEMP_STEP_SIZE 147
++#define TEMP_PSR_R 100
++
++#define VOLT_STEP_SIZE 588
++#define VOLT_PSR_R 100
++
++#define CURR_STEP_SIZE 147
++#define CURR_PSR_R1 44
++#define CURR_PSR_R2 80
++
++#define BK_VOLT_STEP_SIZE 441
++#define BK_VOLT_PSR_R 100
++
++#define ENABLE 1
++#define DISABLE 1
++
++struct twl4030_bci_device_info {
++ struct device *dev;
++
++ unsigned long update_time;
++ int voltage_uV;
++ int bk_voltage_uV;
++ int current_uA;
++ int temp_C;
++ int charge_rsoc;
++ int charge_status;
++
++ struct power_supply bat;
++ struct power_supply bk_bat;
++ struct delayed_work twl4030_bci_monitor_work;
++ struct delayed_work twl4030_bk_bci_monitor_work;
++
++ struct twl4030_bci_platform_data *pdata;
++};
++
++static int usb_charger_flag;
++static int LVL_1, LVL_2, LVL_3, LVL_4;
++
++static int read_bci_val(u8 reg_1);
++static inline int clear_n_set(u8 mod_no, u8 clear, u8 set, u8 reg);
++static int twl4030charger_presence(void);
++
++/*
++ * Report and clear the charger presence event.
++ */
++static inline int twl4030charger_presence_evt(void)
++{
++ int ret;
++ u8 chg_sts, set = 0, clear = 0;
++
++ /* read charger power supply status */
++ ret = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &chg_sts,
++ REG_STS_HW_CONDITIONS);
++ if (ret)
++ return IRQ_NONE;
++
++ if (chg_sts & STS_CHG) { /* If the AC charger have been connected */
++ /* configuring falling edge detection for CHG_PRES */
++ set = CHG_PRES_FALLING;
++ clear = CHG_PRES_RISING;
++ } else { /* If the AC charger have been disconnected */
++ /* configuring rising edge detection for CHG_PRES */
++ set = CHG_PRES_RISING;
++ clear = CHG_PRES_FALLING;
++ }
++
++ /* Update the interrupt edge detection register */
++ clear_n_set(TWL4030_MODULE_INT, clear, set, REG_PWR_EDR1);
++
++ return 0;
++}
++
++/*
++ * Interrupt service routine
++ *
++ * Attends to TWL 4030 power module interruptions events, specifically
++ * USB_PRES (USB charger presence) CHG_PRES (AC charger presence) events
++ *
++ */
++static irqreturn_t twl4030charger_interrupt(int irq, void *_di)
++{
++ struct twl4030_bci_device_info *di = _di;
++
++#ifdef CONFIG_LOCKDEP
++ /* WORKAROUND for lockdep forcing IRQF_DISABLED on us, which
++ * we don't want and can't tolerate. Although it might be
++ * friendlier not to borrow this thread context...
++ */
++ local_irq_enable();
++#endif
++
++ twl4030charger_presence_evt();
++ power_supply_changed(&di->bat);
++
++ return IRQ_HANDLED;
++}
++
++/*
++ * This function handles the twl4030 battery presence interrupt
++ */
++static int twl4030battery_presence_evt(void)
++{
++ int ret;
++ u8 batstsmchg, batstspchg;
++
++ /* check for the battery presence in main charge*/
++ ret = twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE,
++ &batstsmchg, REG_BCIMFSTS3);
++ if (ret)
++ return ret;
++
++ /* check for the battery presence in precharge */
++ ret = twl_i2c_read_u8(TWL4030_MODULE_PRECHARGE,
++ &batstspchg, REG_BCIMFSTS1);
++ if (ret)
++ return ret;
++
++ /*
++ * REVISIT: Physically inserting/removing the batt
++ * does not seem to generate an int on 3430ES2 SDP.
++ */
++ if ((batstspchg & BATSTSPCHG) || (batstsmchg & BATSTSMCHG)) {
++ /* In case of the battery insertion event */
++ ret = clear_n_set(TWL4030_MODULE_INTERRUPTS, BATSTS_EDRRISIN,
++ BATSTS_EDRFALLING, REG_BCIEDR2);
++ if (ret)
++ return ret;
++ } else {
++ /* In case of the battery removal event */
++ ret = clear_n_set(TWL4030_MODULE_INTERRUPTS, BATSTS_EDRFALLING,
++ BATSTS_EDRRISIN, REG_BCIEDR2);
++ if (ret)
++ return ret;
++ }
++
++ return 0;
++}
++
++/*
++ * This function handles the twl4030 battery voltage level interrupt.
++ */
++static int twl4030battery_level_evt(void)
++{
++ int ret;
++ u8 mfst;
++
++ /* checking for threshold event */
++ ret = twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE,
++ &mfst, REG_BCIMFSTS2);
++ if (ret)
++ return ret;
++
++ /* REVISIT could use a bitmap */
++ if (mfst & VBATOV4) {
++ LVL_4 = 1;
++ LVL_3 = 0;
++ LVL_2 = 0;
++ LVL_1 = 0;
++ } else if (mfst & VBATOV3) {
++ LVL_4 = 0;
++ LVL_3 = 1;
++ LVL_2 = 0;
++ LVL_1 = 0;
++ } else if (mfst & VBATOV2) {
++ LVL_4 = 0;
++ LVL_3 = 0;
++ LVL_2 = 1;
++ LVL_1 = 0;
++ } else {
++ LVL_4 = 0;
++ LVL_3 = 0;
++ LVL_2 = 0;
++ LVL_1 = 1;
++ }
++
++ return 0;
++}
++
++/*
++ * Interrupt service routine
++ *
++ * Attends to BCI interruptions events,
++ * specifically BATSTS (battery connection and removal)
++ * VBATOV (main battery voltage threshold) events
++ *
++ */
++static irqreturn_t twl4030battery_interrupt(int irq, void *_di)
++{
++ u8 isr1a_val, isr2a_val, clear_2a, clear_1a;
++ int ret;
++
++#ifdef CONFIG_LOCKDEP
++ /* WORKAROUND for lockdep forcing IRQF_DISABLED on us, which
++ * we don't want and can't tolerate. Although it might be
++ * friendlier not to borrow this thread context...
++ */
++ local_irq_enable();
++#endif
++
++ ret = twl_i2c_read_u8(TWL4030_MODULE_INTERRUPTS, &isr1a_val,
++ REG_BCIISR1A);
++ if (ret)
++ return IRQ_NONE;
++
++ ret = twl_i2c_read_u8(TWL4030_MODULE_INTERRUPTS, &isr2a_val,
++ REG_BCIISR2A);
++ if (ret)
++ return IRQ_NONE;
++
++ clear_2a = (isr2a_val & VBATLVL_ISR1) ? (VBATLVL_ISR1) : 0;
++ clear_1a = (isr1a_val & BATSTS_ISR1) ? (BATSTS_ISR1) : 0;
++
++ /* cleaning BCI interrupt status flags */
++ ret = twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS,
++ clear_1a , REG_BCIISR1A);
++ if (ret)
++ return IRQ_NONE;
++
++ ret = twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS,
++ clear_2a , REG_BCIISR2A);
++ if (ret)
++ return IRQ_NONE;
++
++ /* battery connetion or removal event */
++ if (isr1a_val & BATSTS_ISR1)
++ twl4030battery_presence_evt();
++ /* battery voltage threshold event*/
++ else if (isr2a_val & VBATLVL_ISR1)
++ twl4030battery_level_evt();
++ else
++ return IRQ_NONE;
++
++ return IRQ_HANDLED;
++}
++
++/*
++ * Enable/Disable hardware battery level event notifications.
++ */
++static int twl4030battery_hw_level_en(int enable)
++{
++ int ret;
++
++ if (enable) {
++ /* unmask VBATOV interrupt for INT1 */
++ ret = clear_n_set(TWL4030_MODULE_INTERRUPTS, VBATLVL_IMR1,
++ 0, REG_BCIIMR2A);
++ if (ret)
++ return ret;
++
++ /* configuring interrupt edge detection for VBATOv */
++ ret = clear_n_set(TWL4030_MODULE_INTERRUPTS, 0,
++ VBATLVL_EDRRISIN, REG_BCIEDR3);
++ if (ret)
++ return ret;
++ } else {
++ /* mask VBATOV interrupt for INT1 */
++ ret = clear_n_set(TWL4030_MODULE_INTERRUPTS, 0,
++ VBATLVL_IMR1, REG_BCIIMR2A);
++ if (ret)
++ return ret;
++ }
++
++ return 0;
++}
++
++/*
++ * Enable/disable hardware battery presence event notifications.
++ */
++static int twl4030battery_hw_presence_en(int enable)
++{
++ int ret;
++
++ if (enable) {
++ /* unmask BATSTS interrupt for INT1 */
++ ret = clear_n_set(TWL4030_MODULE_INTERRUPTS, BATSTS_IMR1,
++ 0, REG_BCIIMR1A);
++ if (ret)
++ return ret;
++
++ /* configuring interrupt edge for BATSTS */
++ ret = clear_n_set(TWL4030_MODULE_INTERRUPTS, 0,
++ BATSTS_EDRRISIN | BATSTS_EDRFALLING, REG_BCIEDR2);
++ if (ret)
++ return ret;
++ } else {
++ /* mask BATSTS interrupt for INT1 */
++ ret = clear_n_set(TWL4030_MODULE_INTERRUPTS, 0,
++ BATSTS_IMR1, REG_BCIIMR1A);
++ if (ret)
++ return ret;
++ }
++
++ return 0;
++}
++
++/*
++ * Enable/Disable AC Charge funtionality.
++ */
++static int twl4030charger_ac_en(int enable, int automatic)
++{
++ int ret;
++
++ if (enable) {
++ /* forcing the field BCIAUTOAC (BOOT_BCI[0) to 1 */
++ if(!automatic) {
++ ret = clear_n_set(TWL4030_MODULE_PM_MASTER, BCIAUTOAC | CVENAC,
++ (CONFIG_DONE | BCIAUTOWEN),
++ REG_BOOT_BCI);
++ } else {
++ ret = clear_n_set(TWL4030_MODULE_PM_MASTER, 0,
++ (CONFIG_DONE | BCIAUTOWEN | BCIAUTOAC | CVENAC),
++ REG_BOOT_BCI);
++ }
++ if (ret)
++ return ret;
++ } else {
++ /* forcing the field BCIAUTOAC (BOOT_BCI[0) to 0*/
++ ret = clear_n_set(TWL4030_MODULE_PM_MASTER, BCIAUTOAC,
++ (CONFIG_DONE | BCIAUTOWEN),
++ REG_BOOT_BCI);
++ if (ret)
++ return ret;
++ }
++
++ return 0;
++}
++
++/*
++ * Enable/Disable USB Charge funtionality.
++ */
++int twl4030charger_usb_en(int enable)
++{
++ u8 value;
++ int ret;
++ unsigned long timeout;
++
++ if (enable) {
++ /* Check for USB charger conneted */
++ ret = twl4030charger_presence();
++ if (ret < 0)
++ return ret;
++
++ if (!(ret & USB_PW_CONN))
++ return -ENXIO;
++
++ /* forcing the field BCIAUTOUSB (BOOT_BCI[1]) to 1 */
++ ret = clear_n_set(TWL4030_MODULE_PM_MASTER, 0,
++ (CONFIG_DONE | BCIAUTOWEN | BCIAUTOUSB),
++ REG_BOOT_BCI);
++ if (ret)
++ return ret;
++
++ ret = clear_n_set(TWL4030_MODULE_USB, 0, PHY_DPLL_CLK,
++ REG_PHY_CLK_CTRL);
++ if (ret)
++ return ret;
++
++ value = 0;
++ timeout = jiffies + msecs_to_jiffies(50);
++
++ while ((!(value & PHY_DPLL_CLK)) &&
++ time_before(jiffies, timeout)) {
++ udelay(10);
++ ret = twl_i2c_read_u8(TWL4030_MODULE_USB, &value,
++ REG_PHY_CLK_CTRL_STS);
++ if (ret)
++ return ret;
++ }
++
++ /* OTG_EN (POWER_CTRL[5]) to 1 */
++ ret = clear_n_set(TWL4030_MODULE_USB, 0, OTG_EN,
++ REG_POWER_CTRL);
++ if (ret)
++ return ret;
++
++ mdelay(50);
++
++ /* forcing USBFASTMCHG(BCIMFSTS4[2]) to 1 */
++ ret = clear_n_set(TWL4030_MODULE_MAIN_CHARGE, 0,
++ USBFASTMCHG, REG_BCIMFSTS4);
++ if (ret)
++ return ret;
++ } else {
++ twl4030charger_presence();
++ ret = clear_n_set(TWL4030_MODULE_PM_MASTER, BCIAUTOUSB,
++ (CONFIG_DONE | BCIAUTOWEN), REG_BOOT_BCI);
++ if (ret)
++ return ret;
++ }
++
++ return 0;
++}
++
++/*
++ * Return battery temperature
++ * Or < 0 on failure.
++ */
++static int twl4030battery_temperature(struct twl4030_bci_device_info *di)
++{
++ u8 val;
++ int temp, curr, volt, res, ret;
++
++ /* Is a temperature table specified? */
++ if (!di->pdata->tblsize)
++ return 0;
++
++ /* Getting and calculating the thermistor voltage */
++ ret = read_bci_val(T2_BATTERY_TEMP);
++ if (ret < 0)
++ return ret;
++
++ volt = (ret * TEMP_STEP_SIZE) / TEMP_PSR_R;
++
++ /* Getting and calculating the supply current in micro ampers */
++ ret = twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, &val,
++ REG_BCICTL2);
++ if (ret)
++ return 0;
++
++ curr = ((val & ITHSENS) + 1) * 10;
++
++ /* Getting and calculating the thermistor resistance in ohms*/
++ res = volt * 1000 / curr;
++
++ /*calculating temperature*/
++ for (temp = 58; temp >= 0; temp--) {
++ int actual = di->pdata->battery_tmp_tbl[temp];
++ if ((actual - res) >= 0)
++ break;
++ }
++
++ /* Negative temperature */
++ if (temp < 3) {
++ if (temp == 2)
++ temp = -1;
++ else if (temp == 1)
++ temp = -2;
++ else
++ temp = -3;
++ }
++
++ return temp + 1;
++}
++
++/*
++ * Return battery voltage
++ * Or < 0 on failure.
++ */
++static int twl4030battery_voltage(void)
++{
++ int volt = read_bci_val(T2_BATTERY_VOLT);
++ return (volt * VOLT_STEP_SIZE) / VOLT_PSR_R;
++}
++
++/*
++ * Get latest battery voltage (using MADC)
++ *
++ * When the BCI is not charging, the BCI voltage registers are not
++ * updated and are 'frozen' but the data can be read through the
++ * MADC.
++ */
++static int twl4030battery_voltage_madc(void)
++{
++ struct twl4030_madc_request req;
++
++ req.channels = (1 << 12);
++ req.do_avg = 0;
++ req.method = TWL4030_MADC_SW1;
++ req.active = 0;
++ req.func_cb = NULL;
++ twl4030_madc_conversion(&req);
++
++ return (((int) req.rbuf[12]) * VOLT_STEP_SIZE) / VOLT_PSR_R;
++}
++
++/*
++ * Return the battery current
++ * Or < 0 on failure.
++ */
++static int twl4030battery_current(void)
++{
++ int ret, curr = read_bci_val(T2_BATTERY_CUR);
++ u8 val;
++
++ ret = twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, &val,
++ REG_BCICTL1);
++ if (ret)
++ return ret;
++
++ if (val & CGAIN) /* slope of 0.44 mV/mA */
++ return (curr * CURR_STEP_SIZE) / CURR_PSR_R1;
++ else /* slope of 0.88 mV/mA */
++ return (curr * CURR_STEP_SIZE) / CURR_PSR_R2;
++}
++
++/*
++ * Return the battery backup voltage
++ * Or < 0 on failure.
++ */
++static int twl4030backupbatt_voltage(void)
++{
++ struct twl4030_madc_request req;
++ int temp;
++
++ req.channels = (1 << 9);
++ req.do_avg = 0;
++ req.method = TWL4030_MADC_SW1;
++ req.active = 0;
++ req.func_cb = NULL;
++ twl4030_madc_conversion(&req);
++ temp = (u16)req.rbuf[9];
++
++ return (temp * BK_VOLT_STEP_SIZE) / BK_VOLT_PSR_R;
++}
++
++/*
++ * Returns an integer value, that means,
++ * NO_PW_CONN no power supply is connected
++ * AC_PW_CONN if the AC power supply is connected
++ * USB_PW_CONN if the USB power supply is connected
++ * AC_PW_CONN + USB_PW_CONN if USB and AC power supplies are both connected
++ *
++ * Or < 0 on failure.
++ */
++static int twl4030charger_presence(void)
++{
++ int ret;
++ u8 hwsts;
++
++ ret = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &hwsts,
++ REG_STS_HW_CONDITIONS);
++ if (ret) {
++ pr_err("twl4030_bci: error reading STS_HW_CONDITIONS\n");
++ return ret;
++ }
++
++ ret = (hwsts & STS_CHG) ? AC_PW_CONN : NO_PW_CONN;
++ ret += (hwsts & STS_VBUS) ? USB_PW_CONN : NO_PW_CONN;
++
++ if (ret & USB_PW_CONN)
++ usb_charger_flag = 1;
++ else
++ usb_charger_flag = 0;
++
++ return ret;
++
++}
++
++/*
++ * Returns the main charge FSM status
++ * Or < 0 on failure.
++ */
++static int twl4030bci_status(void)
++{
++ int ret;
++ u8 status;
++
++ ret = twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE,
++ &status, REG_BCIMSTATEC);
++ if (ret) {
++ pr_err("twl4030_bci: error reading BCIMSTATEC\n");
++ return ret;
++ }
++
++#ifdef DEBUG
++ printk("BCI DEBUG: BCIMSTATEC Charge state is 0x%x\n", status);
++#endif
++ return (int) (status & BCIMSTAT_MASK);
++}
++
++static int read_bci_val(u8 reg)
++{
++ int ret, temp;
++ u8 val;
++
++ /* reading MSB */
++ ret = twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, &val,
++ reg + 1);
++ if (ret)
++ return ret;
++
++ temp = ((int)(val & 0x03)) << 8;
++
++ /* reading LSB */
++ ret = twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, &val,
++ reg);
++ if (ret)
++ return ret;
++
++ return temp | val;
++}
++
++/*
++ * Settup the twl4030 BCI module to enable backup
++ * battery charging.
++ */
++static int twl4030backupbatt_voltage_setup(void)
++{
++ int ret;
++
++ /* Starting backup batery charge */
++ ret = clear_n_set(TWL4030_MODULE_PM_RECEIVER, 0, BBCHEN,
++ REG_BB_CFG);
++ if (ret)
++ return ret;
++
++ return 0;
++}
++
++/*
++ * Settup the twl4030 BCI module to measure battery
++ * temperature
++ */
++static int twl4030battery_temp_setup(void)
++{
++#ifdef DEBUG
++ u8 i;
++#endif
++ u8 ret;
++
++ /* Enabling thermistor current */
++ ret = clear_n_set(TWL4030_MODULE_MAIN_CHARGE, 0, 0x1B,
++ REG_BCICTL1);
++ if (ret)
++ return ret;
++
++#ifdef DEBUG
++ twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &ret, REG_BOOT_BCI);
++ printk("BCI DEBUG: BOOT_BCI Value is 0x%x\n", ret);
++
++ twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &ret, REG_STS_HW_CONDITIONS);
++ printk("BCI DEBUG: STS_HW_CONDITIONS Value is 0x%x\n", ret);
++
++ twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, &ret, REG_BCICTL1);
++ printk("BCI DEBUG: BCICTL1 Value is 0x%x\n", ret);
++
++ twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, &ret, REG_BCICTL2);
++ printk("BCI DEBUG: BCICTL2 Value is 0x%x\n", ret);
++
++ twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, &ret, 0x0);
++ printk("BCI DEBUG: BCIMDEN Value is 0x%x\n", ret);
++
++ twl_i2c_read_u8(TWL4030_MODULE_INTBR, &ret, REG_GPBR1);
++ printk("BCI DEBUG: GPBR1 Value is 0x%x\n", ret);
++
++ for(i = 0x0; i <= 0x32; i++)
++ {
++ twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, &ret, i);
++ printk("BCI DEBUG: BCI 0x%x Value is 0x%x\n", i, ret);
++ }
++#endif
++
++ return 0;
++}
++
++/*
++ * Sets and clears bits on an given register on a given module
++ */
++static inline int clear_n_set(u8 mod_no, u8 clear, u8 set, u8 reg)
++{
++ int ret;
++ u8 val = 0;
++
++ /* Gets the initial register value */
++ ret = twl_i2c_read_u8(mod_no, &val, reg);
++ if (ret)
++ return ret;
++ /* Clearing all those bits to clear */
++ val &= ~(clear);
++
++ /* Setting all those bits to set */
++ val |= set;
++
++ /* Update the register */
++ ret = twl_i2c_write_u8(mod_no, val, reg);
++ if (ret)
++ return ret;
++
++ return 0;
++}
++
++static enum power_supply_property twl4030_bci_battery_props[] = {
++ POWER_SUPPLY_PROP_STATUS,
++ POWER_SUPPLY_PROP_ONLINE,
++ POWER_SUPPLY_PROP_VOLTAGE_NOW,
++ POWER_SUPPLY_PROP_CURRENT_NOW,
++ POWER_SUPPLY_PROP_CAPACITY,
++ POWER_SUPPLY_PROP_TEMP,
++};
++
++static enum power_supply_property twl4030_bk_bci_battery_props[] = {
++ POWER_SUPPLY_PROP_VOLTAGE_NOW,
++};
++
++static void
++twl4030_bk_bci_battery_read_status(struct twl4030_bci_device_info *di)
++{
++ di->bk_voltage_uV = twl4030backupbatt_voltage();
++}
++
++static void twl4030_bk_bci_battery_work(struct work_struct *work)
++{
++ struct twl4030_bci_device_info *di = container_of(work,
++ struct twl4030_bci_device_info,
++ twl4030_bk_bci_monitor_work.work);
++
++ if(!di->pdata->no_backup_battery)
++ twl4030_bk_bci_battery_read_status(di);
++ schedule_delayed_work(&di->twl4030_bk_bci_monitor_work, 500);
++}
++
++static void twl4030_bci_battery_read_status(struct twl4030_bci_device_info *di)
++{
++ if(di->charge_status != POWER_SUPPLY_STATUS_DISCHARGING) {
++ di->temp_C = twl4030battery_temperature(di);
++ di->voltage_uV = twl4030battery_voltage();
++ di->current_uA = twl4030battery_current();
++ }
++}
++
++static void
++twl4030_bci_battery_update_status(struct twl4030_bci_device_info *di)
++{
++ if (power_supply_am_i_supplied(&di->bat))
++ di->charge_status = POWER_SUPPLY_STATUS_CHARGING;
++ else
++ di->charge_status = POWER_SUPPLY_STATUS_DISCHARGING;
++ twl4030_bci_battery_read_status(di);
++}
++
++static void twl4030_bci_battery_work(struct work_struct *work)
++{
++ struct twl4030_bci_device_info *di = container_of(work,
++ struct twl4030_bci_device_info, twl4030_bci_monitor_work.work);
++
++ twl4030_bci_battery_update_status(di);
++ schedule_delayed_work(&di->twl4030_bci_monitor_work, 100);
++}
++
++
++#define to_twl4030_bci_device_info(x) container_of((x), \
++ struct twl4030_bci_device_info, bat);
++
++static void twl4030_bci_battery_external_power_changed(struct power_supply *psy)
++{
++ struct twl4030_bci_device_info *di = to_twl4030_bci_device_info(psy);
++
++ cancel_delayed_work(&di->twl4030_bci_monitor_work);
++ schedule_delayed_work(&di->twl4030_bci_monitor_work, 0);
++}
++
++#define to_twl4030_bk_bci_device_info(x) container_of((x), \
++ struct twl4030_bci_device_info, bk_bat);
++
++static ssize_t
++show_charge_current(struct device *dev, struct device_attribute *attr, char *buf)
++{
++ u8 ctl;
++ int ret = read_bci_val(REG_BCIIREF1) & 0x1FF;
++ twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, &ctl, REG_BCICTL1);
++
++ if (ctl & CGAIN)
++ ret |= 0x200;
++
++#ifdef DEBUG
++ /* Dump debug */
++ twl4030battery_temp_setup();
++#endif
++
++ return sprintf(buf, "%d\n", ret);
++}
++
++static ssize_t
++set_charge_current(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
++{
++ unsigned long newCurrent;
++ int ret;
++
++ ret = strict_strtoul(buf, 10, &newCurrent);
++ if (ret)
++ return -EINVAL;
++
++ ret = twl_i2c_write_u8(TWL4030_MODULE_MAIN_CHARGE, KEY_IIREF, REG_BCIMFKEY);
++ if (ret)
++ return ret;
++
++ ret = twl_i2c_write_u8(TWL4030_MODULE_MAIN_CHARGE, newCurrent & 0xff, REG_BCIIREF1);
++ if (ret)
++ return ret;
++
++ ret = twl_i2c_write_u8(TWL4030_MODULE_MAIN_CHARGE, KEY_IIREF, REG_BCIMFKEY);
++ if (ret)
++ return ret;
++
++ ret = twl_i2c_write_u8(TWL4030_MODULE_MAIN_CHARGE, (newCurrent >> 8) & 0x1, REG_BCIIREF2);
++ if (ret)
++ return ret;
++
++ /* Set software-controlled charge */
++ twl4030charger_ac_en(ENABLE, 0);
++
++ /* Set CGAIN = 0 or 1 */
++ if(newCurrent > 511) {
++ u8 tmp;
++
++ /* Set CGAIN = 1 -- need to wait until automatic charge turns off */
++ while(!ret) {
++ clear_n_set(TWL4030_MODULE_MAIN_CHARGE, 0, CGAIN | 0x1B, REG_BCICTL1);
++ twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, &tmp, REG_BCICTL1);
++
++ ret = tmp & CGAIN;
++ if(!ret)
++ mdelay(50);
++ }
++ } else {
++ u8 tmp;
++
++ /* Set CGAIN = 0 -- need to wait until automatic charge turns off */
++ while(!ret) {
++ clear_n_set(TWL4030_MODULE_MAIN_CHARGE, CGAIN, 0x1B, REG_BCICTL1);
++ twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, &tmp, REG_BCICTL1);
++
++ ret = !(tmp & CGAIN);
++ if(!ret)
++ mdelay(50);
++ }
++ }
++
++ /* Set automatic charge (CGAIN = 0/1 persists) */
++ twl4030charger_ac_en(ENABLE, 1);
++
++ return count;
++}
++
++static ssize_t
++show_voltage(struct device *dev, struct device_attribute *attr, char *buf)
++{
++ return sprintf(buf, "%d\n", twl4030battery_voltage_madc());
++}
++
++static DEVICE_ATTR(charge_current, S_IRUGO | S_IWUGO, show_charge_current, set_charge_current);
++static DEVICE_ATTR(voltage_now_madc, S_IRUGO, show_voltage, NULL);
++
++static int twl4030_bk_bci_battery_get_property(struct power_supply *psy,
++ enum power_supply_property psp,
++ union power_supply_propval *val)
++{
++ struct twl4030_bci_device_info *di = to_twl4030_bk_bci_device_info(psy);
++
++ switch (psp) {
++ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
++ val->intval = di->bk_voltage_uV;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++static int twl4030_bci_battery_get_property(struct power_supply *psy,
++ enum power_supply_property psp,
++ union power_supply_propval *val)
++{
++ struct twl4030_bci_device_info *di;
++ int status = 0;
++
++ di = to_twl4030_bci_device_info(psy);
++
++ switch (psp) {
++ case POWER_SUPPLY_PROP_STATUS:
++ val->intval = di->charge_status;
++ return 0;
++ default:
++ break;
++ }
++
++ switch (psp) {
++ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
++ {
++ /* Get latest data from MADC -- not done periodically by
++ worker as this is more expensive, so only do it when we
++ are actually asked for the data... */
++ if(di->charge_status == POWER_SUPPLY_STATUS_DISCHARGING)
++ val->intval = twl4030battery_voltage_madc();
++ else
++ val->intval = di->voltage_uV;
++
++ break;
++ }
++ case POWER_SUPPLY_PROP_CURRENT_NOW:
++ /* FIXME: Get from MADC */
++ if(di->charge_status == POWER_SUPPLY_STATUS_DISCHARGING)
++ val->intval = 0;
++ else
++ val->intval = di->current_uA;
++ break;
++ case POWER_SUPPLY_PROP_TEMP:
++ val->intval = di->temp_C;
++ break;
++ case POWER_SUPPLY_PROP_ONLINE:
++ status = twl4030bci_status();
++ if ((status & AC_STATEC) == AC_STATEC)
++ val->intval = POWER_SUPPLY_TYPE_MAINS;
++ else if (usb_charger_flag)
++ val->intval = POWER_SUPPLY_TYPE_USB;
++ else
++ val->intval = 0;
++ break;
++ case POWER_SUPPLY_PROP_CAPACITY:
++ /* Get latest data from MADC -- not done periodically by
++ worker as this is more expensive, so only do it when we
++ are actually asked for the data... */
++ if(di->charge_status == POWER_SUPPLY_STATUS_DISCHARGING)
++ di->voltage_uV = twl4030battery_voltage_madc();
++
++ /*
++ * need to get the correct percentage value per the
++ * battery characteristics. Approx values for now.
++ */
++ if (di->voltage_uV < 2894 || LVL_1) {
++ val->intval = 5;
++ LVL_1 = 0;
++ } else if ((di->voltage_uV < 3451 && di->voltage_uV > 2894)
++ || LVL_2) {
++ val->intval = 20;
++ LVL_2 = 0;
++ } else if ((di->voltage_uV < 3902 && di->voltage_uV > 3451)
++ || LVL_3) {
++ val->intval = 50;
++ LVL_3 = 0;
++ } else if ((di->voltage_uV < 3949 && di->voltage_uV > 3902)
++ || LVL_4) {
++ val->intval = 75;
++ LVL_4 = 0;
++ } else if (di->voltage_uV > 3949)
++ val->intval = 90;
++ break;
++ default:
++ return -EINVAL;
++ }
++ return 0;
++}
++
++static char *twl4030_bci_supplied_to[] = {
++ "twl4030_bci_battery",
++};
++
++static int __init twl4030_bci_battery_probe(struct platform_device *pdev)
++{
++ struct twl4030_bci_platform_data *pdata = pdev->dev.platform_data;
++ struct twl4030_bci_device_info *di;
++ int irq;
++ int ret;
++
++ di = kzalloc(sizeof(*di), GFP_KERNEL);
++ if (!di)
++ return -ENOMEM;
++
++ di->dev = &pdev->dev;
++ di->bat.name = "twl4030_bci_battery";
++ di->bat.supplied_to = twl4030_bci_supplied_to;
++ di->bat.num_supplicants = ARRAY_SIZE(twl4030_bci_supplied_to);
++ di->bat.type = POWER_SUPPLY_TYPE_BATTERY;
++ di->bat.properties = twl4030_bci_battery_props;
++ di->bat.num_properties = ARRAY_SIZE(twl4030_bci_battery_props);
++ di->bat.get_property = twl4030_bci_battery_get_property;
++ di->bat.external_power_changed =
++ twl4030_bci_battery_external_power_changed;
++
++ di->charge_status = POWER_SUPPLY_STATUS_UNKNOWN;
++
++ di->bk_bat.name = "twl4030_bci_bk_battery";
++ di->bk_bat.type = POWER_SUPPLY_TYPE_BATTERY;
++ di->bk_bat.properties = twl4030_bk_bci_battery_props;
++ di->bk_bat.num_properties = ARRAY_SIZE(twl4030_bk_bci_battery_props);
++ di->bk_bat.get_property = twl4030_bk_bci_battery_get_property;
++ di->bk_bat.external_power_changed = NULL;
++ di->pdata = pdata;
++
++ /* Set up clocks */
++ twl_i2c_write_u8(TWL4030_MODULE_INTBR, MADC_HFCLK_EN | DEFAULT_MADC_CLK_EN, REG_GPBR1);
++
++ twl4030charger_ac_en(ENABLE, CHARGE_MODE);
++ twl4030charger_usb_en(ENABLE);
++ twl4030battery_hw_level_en(ENABLE);
++ twl4030battery_hw_presence_en(ENABLE);
++
++ platform_set_drvdata(pdev, di);
++
++ /* settings for temperature sensing */
++ ret = twl4030battery_temp_setup();
++ if (ret)
++ goto temp_setup_fail;
++
++ /* enabling GPCH09 for read back battery voltage */
++ if(!di->pdata->no_backup_battery)
++ {
++ ret = twl4030backupbatt_voltage_setup();
++ if (ret)
++ goto voltage_setup_fail;
++ }
++
++ /* REVISIT do we need to request both IRQs ?? */
++
++ /* request BCI interruption */
++ irq = platform_get_irq(pdev, 1);
++ ret = request_irq(irq, twl4030battery_interrupt,
++ 0, pdev->name, NULL);
++ if (ret) {
++ dev_dbg(&pdev->dev, "could not request irq %d, status %d\n",
++ irq, ret);
++ goto batt_irq_fail;
++ }
++
++ /* request Power interruption */
++ irq = platform_get_irq(pdev, 0);
++ ret = request_irq(irq, twl4030charger_interrupt,
++ 0, pdev->name, di);
++
++ if (ret) {
++ dev_dbg(&pdev->dev, "could not request irq %d, status %d\n",
++ irq, ret);
++ goto chg_irq_fail;
++ }
++
++ ret = power_supply_register(&pdev->dev, &di->bat);
++ if (ret) {
++ dev_dbg(&pdev->dev, "failed to register main battery\n");
++ goto batt_failed;
++ }
++
++ INIT_DELAYED_WORK_DEFERRABLE(&di->twl4030_bci_monitor_work,
++ twl4030_bci_battery_work);
++ schedule_delayed_work(&di->twl4030_bci_monitor_work, 0);
++
++ if(!pdata->no_backup_battery)
++ {
++ ret = power_supply_register(&pdev->dev, &di->bk_bat);
++ if (ret) {
++ dev_dbg(&pdev->dev, "failed to register backup battery\n");
++ goto bk_batt_failed;
++ }
++ }
++
++ ret = device_create_file(di->bat.dev, &dev_attr_voltage_now_madc);
++ ret = device_create_file(di->bat.dev, &dev_attr_charge_current);
++ if (ret) {
++ dev_err(&pdev->dev, "failed to create sysfs entries\n");
++ goto bk_batt_failed;
++ }
++
++ INIT_DELAYED_WORK_DEFERRABLE(&di->twl4030_bk_bci_monitor_work,
++ twl4030_bk_bci_battery_work);
++ schedule_delayed_work(&di->twl4030_bk_bci_monitor_work, 500);
++
++ set_charge_current (NULL, NULL, "1023", 4);
++ return 0;
++
++bk_batt_failed:
++ if(!pdata->no_backup_battery)
++ power_supply_unregister(&di->bat);
++batt_failed:
++ free_irq(irq, di);
++chg_irq_fail:
++ irq = platform_get_irq(pdev, 1);
++ free_irq(irq, NULL);
++batt_irq_fail:
++voltage_setup_fail:
++temp_setup_fail:
++ twl4030charger_ac_en(DISABLE, CHARGE_MODE);
++ twl4030charger_usb_en(DISABLE);
++ twl4030battery_hw_level_en(DISABLE);
++ twl4030battery_hw_presence_en(DISABLE);
++ kfree(di);
++
++ return ret;
++}
++
++static int __exit twl4030_bci_battery_remove(struct platform_device *pdev)
++{
++ struct twl4030_bci_device_info *di = platform_get_drvdata(pdev);
++ int irq;
++
++ twl4030charger_ac_en(DISABLE, CHARGE_MODE);
++ twl4030charger_usb_en(DISABLE);
++ twl4030battery_hw_level_en(DISABLE);
++ twl4030battery_hw_presence_en(DISABLE);
++
++ irq = platform_get_irq(pdev, 0);
++ free_irq(irq, di);
++
++ irq = platform_get_irq(pdev, 1);
++ free_irq(irq, NULL);
++
++ flush_scheduled_work();
++ power_supply_unregister(&di->bat);
++ power_supply_unregister(&di->bk_bat);
++ platform_set_drvdata(pdev, NULL);
++ kfree(di);
++
++ return 0;
++}
++
++#ifdef CONFIG_PM
++static int twl4030_bci_battery_suspend(struct platform_device *pdev,
++ pm_message_t state)
++{
++ struct twl4030_bci_device_info *di = platform_get_drvdata(pdev);
++
++ di->charge_status = POWER_SUPPLY_STATUS_UNKNOWN;
++ cancel_delayed_work(&di->twl4030_bci_monitor_work);
++ cancel_delayed_work(&di->twl4030_bk_bci_monitor_work);
++ return 0;
++}
++
++static int twl4030_bci_battery_resume(struct platform_device *pdev)
++{
++ struct twl4030_bci_device_info *di = platform_get_drvdata(pdev);
++
++ schedule_delayed_work(&di->twl4030_bci_monitor_work, 0);
++ schedule_delayed_work(&di->twl4030_bk_bci_monitor_work, 50);
++ return 0;
++}
++#else
++#define twl4030_bci_battery_suspend NULL
++#define twl4030_bci_battery_resume NULL
++#endif /* CONFIG_PM */
++
++static struct platform_driver twl4030_bci_battery_driver = {
++ .probe = twl4030_bci_battery_probe,
++ .remove = __exit_p(twl4030_bci_battery_remove),
++ .suspend = twl4030_bci_battery_suspend,
++ .resume = twl4030_bci_battery_resume,
++ .driver = {
++ .name = "twl4030_bci",
++ },
++};
++
++MODULE_LICENSE("GPL");
++MODULE_ALIAS("platform:twl4030_bci");
++MODULE_AUTHOR("Texas Instruments Inc");
++
++static int __init twl4030_battery_init(void)
++{
++ return platform_driver_register(&twl4030_bci_battery_driver);
++}
++module_init(twl4030_battery_init);
++
++static void __exit twl4030_battery_exit(void)
++{
++ platform_driver_unregister(&twl4030_bci_battery_driver);
++}
++module_exit(twl4030_battery_exit);
++
+diff --git a/include/linux/i2c/twl.h b/include/linux/i2c/twl.h
+index d975c5b..a3470ce 100644
+--- a/include/linux/i2c/twl.h
++++ b/include/linux/i2c/twl.h
+@@ -442,6 +442,7 @@ struct twl4030_clock_init_data {
+ struct twl4030_bci_platform_data {
+ int *battery_tmp_tbl;
+ unsigned int tblsize;
++ bool no_backup_battery;
+ };
+
+ /* TWL4030_GPIO_MAX (18) GPIOs, with interrupts */
+--
+1.6.6.1
+