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

