Initial Contribution

msm-2.6.38: tag AU_LINUX_ANDROID_GINGERBREAD.02.03.04.00.142

Signed-off-by: Bryan Huntsman <bryanh@codeaurora.org>
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index e57b50b..179a4ac 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -235,4 +235,106 @@
 	  This driver can be build as a module. If so, the module will be
 	  called gpio-charger.
 
+config BATTERY_MSM
+	tristate "MSM battery"
+	depends on ARCH_MSM
+	default m
+	help
+	  Say Y to enable support for the battery in Qualcomm MSM.
+
+config BATTERY_MSM8X60
+	tristate "MSM8X60 battery"
+	select PMIC8058_BATTALARM
+	help
+	  Some MSM boards have dual charging paths to charge the battery.
+	  Say Y to enable support for the battery charging in
+	  such devices.
+
+config PM8058_CHARGER
+	tristate "pmic8058 charger"
+	depends on BATTERY_MSM8X60
+	depends on PMIC8058
+	help
+	  Say Y to enable support for battery charging from the pmic8058.
+	  pmic8058 provides a linear charging circuit connected to the usb
+	  cable on Qualcomm's msm8x60 surf board.
+
+config ISL9519_CHARGER
+	tristate "isl9519 charger"
+	depends on BATTERY_MSM8X60
+	default n
+	help
+	  The isl9519q charger chip from intersil is connected to an external
+	  charger cable and is preferred way of charging the battery because
+	  of its high current rating.
+	  Choose Y if you are compiling for Qualcomm's msm8x60 surf/ffa board.
+
+config SMB137B_CHARGER
+	tristate "smb137b charger"
+	default n
+	depends on I2C
+	help
+	  The smb137b charger chip from summit is a switching mode based
+	  charging solution.
+	  Choose Y if you are compiling for Qualcomm's msm8x60 fluid board.
+	  To compile this driver as a module, choose M here: the module will
+	  be called smb137b.
+
+config BATTERY_MSM_FAKE
+	tristate "Fake MSM battery"
+	depends on ARCH_MSM && BATTERY_MSM
+	default n
+	help
+	  Say Y to bypass actual battery queries.
+
+config PM8058_FIX_USB
+	tristate "pmic8058 software workaround for usb removal"
+	depends on PMIC8058
+	depends on !PM8058_CHARGER
+	help
+	  Say Y to enable the software workaround to USB Vbus line
+	  staying high even when USB cable is removed. This option
+	  is in lieu of a complete pm8058 charging driver.
+
+config BATTERY_QCIBAT
+	tristate "Quanta Computer Inc. Battery"
+	depends on SENSORS_WPCE775X
+	default n
+	help
+	  Say Y here if you want to use the Quanta battery driver for ST15
+	  platform.
+
+config BATTERY_BQ27520
+	tristate "BQ27520 battery driver"
+	depends on I2C
+	default n
+	help
+	  Say Y here to enable support for batteries with BQ27520 (I2C) chips.
+
+config BATTERY_BQ27541
+	tristate "BQ27541 battery driver"
+	depends on I2C
+	default n
+	help
+	  Say Y here to enable support for batteries with BQ27541 (I2C) chips.
+
+config BQ27520_TEST_ENABLE
+	bool "Enable BQ27520 Fuel Gauge Chip Test"
+	depends on BATTERY_BQ27520
+	default n
+	help
+	  Say Y here to enable Test sysfs Interface for BQ27520 Drivers.
+
+config PM8921_CHARGER
+	tristate "PM8921 Charger driver"
+	depends on MFD_PM8921_CORE
+	help
+	  Say Y here to enable support for pm8921 chip charger subdevice
+
+config PM8921_BMS
+	tristate "PM8921 Battery Monitoring System driver"
+	depends on MFD_PM8921_CORE
+	help
+	  Say Y here to enable support for pm8921 chip bms subdevice
+
 endif # POWER_SUPPLY
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index 009a90f..f61c88a 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -36,3 +36,14 @@
 obj-$(CONFIG_CHARGER_MAX8903)	+= max8903_charger.o
 obj-$(CONFIG_CHARGER_TWL4030)	+= twl4030_charger.o
 obj-$(CONFIG_CHARGER_GPIO)	+= gpio-charger.o
+obj-$(CONFIG_BATTERY_MSM)       += msm_battery.o
+obj-$(CONFIG_BATTERY_MSM8X60)   += msm_charger.o
+obj-$(CONFIG_PM8058_CHARGER)    += pmic8058-charger.o
+obj-$(CONFIG_ISL9519_CHARGER)   += isl9519q.o
+obj-$(CONFIG_PM8058_FIX_USB)    += pm8058_usb_fix.o
+obj-$(CONFIG_BATTERY_QCIBAT)    += qci_battery.o
+obj-$(CONFIG_BATTERY_BQ27520)	+= bq27520_fuelgauger.o
+obj-$(CONFIG_BATTERY_BQ27541)	+= bq27541_fuelgauger.o
+obj-$(CONFIG_SMB137B_CHARGER)   += smb137b.o
+obj-$(CONFIG_PM8921_BMS)	+= pm8921-bms.o
+obj-$(CONFIG_PM8921_CHARGER)	+= pm8921-charger.o
diff --git a/drivers/power/bq27520_fuelgauger.c b/drivers/power/bq27520_fuelgauger.c
new file mode 100644
index 0000000..284b134
--- /dev/null
+++ b/drivers/power/bq27520_fuelgauger.c
@@ -0,0 +1,960 @@
+/* Copyright (C) 2008 Rodolfo Giometti <giometti@linux.it>
+ * Copyright (C) 2008 Eurotech S.p.A. <info@eurotech.it>
+ * Based on a previous work by Copyright (C) 2008 Texas Instruments, Inc.
+ *
+ * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/param.h>
+#include <linux/jiffies.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/idr.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <asm/unaligned.h>
+#include <linux/time.h>
+#include <linux/i2c/bq27520.h>
+#include <linux/mfd/pmic8058.h>
+#include <linux/regulator/pmic8058-regulator.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/regulator/machine.h>
+#include <linux/err.h>
+#include <linux/msm-charger.h>
+
+#define DRIVER_VERSION			"1.1.0"
+/* Bq27520 standard data commands */
+#define BQ27520_REG_CNTL		0x00
+#define BQ27520_REG_AR			0x02
+#define BQ27520_REG_ARTTE		0x04
+#define BQ27520_REG_TEMP		0x06
+#define BQ27520_REG_VOLT		0x08
+#define BQ27520_REG_FLAGS		0x0A
+#define BQ27520_REG_NAC			0x0C
+#define BQ27520_REG_FAC			0x0e
+#define BQ27520_REG_RM			0x10
+#define BQ27520_REG_FCC			0x12
+#define BQ27520_REG_AI			0x14
+#define BQ27520_REG_TTE			0x16
+#define BQ27520_REG_TTF			0x18
+#define BQ27520_REG_SI			0x1a
+#define BQ27520_REG_STTE		0x1c
+#define BQ27520_REG_MLI			0x1e
+#define BQ27520_REG_MLTTE		0x20
+#define BQ27520_REG_AE			0x22
+#define BQ27520_REG_AP			0x24
+#define BQ27520_REG_TTECP		0x26
+#define BQ27520_REG_SOH			0x28
+#define BQ27520_REG_SOC			0x2c
+#define BQ27520_REG_NIC			0x2e
+#define BQ27520_REG_ICR			0x30
+#define BQ27520_REG_LOGIDX		0x32
+#define BQ27520_REG_LOGBUF		0x34
+#define BQ27520_FLAG_DSC		BIT(0)
+#define BQ27520_FLAG_FC			BIT(9)
+#define BQ27520_FLAG_BAT_DET		BIT(3)
+#define BQ27520_CS_DLOGEN		BIT(15)
+#define BQ27520_CS_SS		    BIT(13)
+/* Control subcommands */
+#define BQ27520_SUBCMD_CTNL_STATUS  0x0000
+#define BQ27520_SUBCMD_DEVCIE_TYPE  0x0001
+#define BQ27520_SUBCMD_FW_VER  0x0002
+#define BQ27520_SUBCMD_HW_VER  0x0003
+#define BQ27520_SUBCMD_DF_CSUM  0x0004
+#define BQ27520_SUBCMD_PREV_MACW   0x0007
+#define BQ27520_SUBCMD_CHEM_ID   0x0008
+#define BQ27520_SUBCMD_BD_OFFSET   0x0009
+#define BQ27520_SUBCMD_INT_OFFSET  0x000a
+#define BQ27520_SUBCMD_CC_VER   0x000b
+#define BQ27520_SUBCMD_OCV  0x000c
+#define BQ27520_SUBCMD_BAT_INS   0x000d
+#define BQ27520_SUBCMD_BAT_REM   0x000e
+#define BQ27520_SUBCMD_SET_HIB   0x0011
+#define BQ27520_SUBCMD_CLR_HIB   0x0012
+#define BQ27520_SUBCMD_SET_SLP   0x0013
+#define BQ27520_SUBCMD_CLR_SLP   0x0014
+#define BQ27520_SUBCMD_FCT_RES   0x0015
+#define BQ27520_SUBCMD_ENABLE_DLOG  0x0018
+#define BQ27520_SUBCMD_DISABLE_DLOG 0x0019
+#define BQ27520_SUBCMD_SEALED   0x0020
+#define BQ27520_SUBCMD_ENABLE_IT    0x0021
+#define BQ27520_SUBCMD_DISABLE_IT   0x0023
+#define BQ27520_SUBCMD_CAL_MODE  0x0040
+#define BQ27520_SUBCMD_RESET   0x0041
+
+#define ZERO_DEGREE_CELSIUS_IN_TENTH_KELVIN   (-2731)
+#define BQ27520_INIT_DELAY ((HZ)*1)
+#define BQ27520_POLLING_STATUS ((HZ)*3)
+#define BQ27520_COULOMB_POLL ((HZ)*30)
+
+/* If the system has several batteries we need a different name for each
+ * of them...
+ */
+static DEFINE_IDR(battery_id);
+static DEFINE_MUTEX(battery_mutex);
+
+struct bq27520_device_info;
+struct bq27520_access_methods {
+	int (*read)(u8 reg, int *rt_value, int b_single,
+		struct bq27520_device_info *di);
+};
+
+struct bq27520_device_info {
+	struct device				*dev;
+	int					id;
+	struct bq27520_access_methods		*bus;
+	struct i2c_client			*client;
+	const struct bq27520_platform_data	*pdata;
+	struct work_struct			counter;
+	/* 300ms delay is needed after bq27520 is powered up
+	 * and before any successful I2C transaction
+	 */
+	struct  delayed_work			hw_config;
+	uint32_t				irq;
+};
+
+enum {
+	GET_BATTERY_STATUS,
+	GET_BATTERY_TEMPERATURE,
+	GET_BATTERY_VOLTAGE,
+	GET_BATTERY_CAPACITY,
+	NUM_OF_STATUS,
+};
+
+struct bq27520_status {
+	/* Informations owned and maintained by Bq27520 driver, updated
+	 * by poller or SOC_INT interrupt, decoupling from I/Oing
+	 * hardware directly
+	 */
+	int			status[NUM_OF_STATUS];
+	spinlock_t		lock;
+	struct delayed_work	poller;
+};
+
+static struct bq27520_status current_battery_status;
+static struct bq27520_device_info *bq27520_di;
+static int coulomb_counter;
+static spinlock_t lock; /* protect access to coulomb_counter */
+static struct timer_list timer; /* charge counter timer every 30 secs */
+
+static int bq27520_i2c_txsubcmd(u8 reg, unsigned short subcmd,
+		struct bq27520_device_info *di);
+
+static int bq27520_read(u8 reg, int *rt_value, int b_single,
+			struct bq27520_device_info *di)
+{
+	return di->bus->read(reg, rt_value, b_single, di);
+}
+
+/*
+ * Return the battery temperature in tenths of degree Celsius
+ * Or < 0 if something fails.
+ */
+static int bq27520_battery_temperature(struct bq27520_device_info *di)
+{
+	int ret, temp = 0;
+
+	ret = bq27520_read(BQ27520_REG_TEMP, &temp, 0, di);
+	if (ret) {
+		dev_err(di->dev, "error %d reading temperature\n", ret);
+		return ret;
+	}
+
+	return temp + ZERO_DEGREE_CELSIUS_IN_TENTH_KELVIN;
+}
+
+/*
+ * Return the battery Voltage in milivolts
+ * Or < 0 if something fails.
+ */
+static int bq27520_battery_voltage(struct bq27520_device_info *di)
+{
+	int ret, volt = 0;
+
+	ret = bq27520_read(BQ27520_REG_VOLT, &volt, 0, di);
+	if (ret) {
+		dev_err(di->dev, "error %d reading voltage\n", ret);
+		return ret;
+	}
+
+	return volt;
+}
+
+/*
+ * Return the battery Relative State-of-Charge
+ * Or < 0 if something fails.
+ */
+static int bq27520_battery_rsoc(struct bq27520_device_info *di)
+{
+	int ret, rsoc = 0;
+
+	ret = bq27520_read(BQ27520_REG_SOC, &rsoc, 0, di);
+
+	if (ret) {
+		dev_err(di->dev,
+			"error %d reading relative State-of-Charge\n", ret);
+		return ret;
+	}
+
+	return rsoc;
+}
+
+static void bq27520_cntl_cmd(struct bq27520_device_info *di,
+				int subcmd)
+{
+	bq27520_i2c_txsubcmd(BQ27520_REG_CNTL, subcmd, di);
+}
+
+/*
+ * i2c specific code
+ */
+static int bq27520_i2c_txsubcmd(u8 reg, unsigned short subcmd,
+		struct bq27520_device_info *di)
+{
+	struct i2c_msg msg;
+	unsigned char data[3];
+
+	if (!di->client)
+		return -ENODEV;
+
+	memset(data, 0, sizeof(data));
+	data[0] = reg;
+	data[1] = subcmd & 0x00FF;
+	data[2] = (subcmd & 0xFF00) >> 8;
+
+	msg.addr = di->client->addr;
+	msg.flags = 0;
+	msg.len = 3;
+	msg.buf = data;
+
+	if (i2c_transfer(di->client->adapter, &msg, 1) < 0)
+		return -EIO;
+
+	return 0;
+}
+
+static int bq27520_chip_config(struct bq27520_device_info *di)
+{
+	int flags = 0, ret = 0;
+
+	bq27520_cntl_cmd(di, BQ27520_SUBCMD_CTNL_STATUS);
+	udelay(66);
+	ret = bq27520_read(BQ27520_REG_CNTL, &flags, 0, di);
+	if (ret < 0) {
+		dev_err(di->dev, "error %d reading register %02x\n",
+			 ret, BQ27520_REG_CNTL);
+		return ret;
+	}
+	udelay(66);
+
+	bq27520_cntl_cmd(di, BQ27520_SUBCMD_ENABLE_IT);
+	udelay(66);
+
+	if (di->pdata->enable_dlog && !(flags & BQ27520_CS_DLOGEN)) {
+		bq27520_cntl_cmd(di, BQ27520_SUBCMD_ENABLE_DLOG);
+		udelay(66);
+	}
+
+	return 0;
+}
+
+static void bq27520_every_30secs(unsigned long data)
+{
+	struct bq27520_device_info *di = (struct bq27520_device_info *)data;
+
+	schedule_work(&di->counter);
+	mod_timer(&timer, jiffies + BQ27520_COULOMB_POLL);
+}
+
+static void bq27520_coulomb_counter_work(struct work_struct *work)
+{
+	int value = 0, temp = 0, index = 0, ret = 0, count = 0;
+	struct bq27520_device_info *di;
+	unsigned long flags;
+
+	di = container_of(work, struct bq27520_device_info, counter);
+
+	/* retrieve 30 values from FIFO of coulomb data logging buffer
+	 * and average over time
+	 */
+	do {
+		ret = bq27520_read(BQ27520_REG_LOGBUF, &temp, 0, di);
+		if (ret < 0)
+			break;
+		if (temp != 0x7FFF) {
+			++count;
+			value += temp;
+		}
+		udelay(66);
+		ret = bq27520_read(BQ27520_REG_LOGIDX, &index, 0, di);
+		if (ret < 0)
+			break;
+		udelay(66);
+	} while (index != 0 || temp != 0x7FFF);
+
+	if (ret < 0) {
+		dev_err(di->dev, "Error %d reading datalog register\n", ret);
+		return;
+	}
+
+	if (count) {
+		spin_lock_irqsave(&lock, flags);
+		coulomb_counter = value/count;
+		spin_unlock_irqrestore(&lock, flags);
+	}
+}
+
+static int bq27520_is_battery_present(void)
+{
+	return 1;
+}
+
+static int bq27520_is_battery_temp_within_range(void)
+{
+	return 1;
+}
+
+static int bq27520_is_battery_id_valid(void)
+{
+	return 1;
+}
+
+static int bq27520_status_getter(int function)
+{
+	int status = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&current_battery_status.lock, flags);
+	status = current_battery_status.status[function];
+	spin_unlock_irqrestore(&current_battery_status.lock, flags);
+
+	return status;
+}
+
+static int bq27520_get_battery_mvolts(void)
+{
+	return bq27520_status_getter(GET_BATTERY_VOLTAGE);
+}
+
+static int bq27520_get_battery_temperature(void)
+{
+	return bq27520_status_getter(GET_BATTERY_TEMPERATURE);
+}
+
+static int bq27520_get_battery_status(void)
+{
+	return bq27520_status_getter(GET_BATTERY_STATUS);
+}
+
+static int bq27520_get_remaining_capacity(void)
+{
+	return bq27520_status_getter(GET_BATTERY_CAPACITY);
+}
+
+static struct msm_battery_gauge bq27520_batt_gauge = {
+	.get_battery_mvolts		= bq27520_get_battery_mvolts,
+	.get_battery_temperature	= bq27520_get_battery_temperature,
+	.is_battery_present		= bq27520_is_battery_present,
+	.is_battery_temp_within_range	= bq27520_is_battery_temp_within_range,
+	.is_battery_id_valid		= bq27520_is_battery_id_valid,
+	.get_battery_status		= bq27520_get_battery_status,
+	.get_batt_remaining_capacity	= bq27520_get_remaining_capacity,
+};
+
+static void update_current_battery_status(int data)
+{
+	int status[4], ret = 0;
+	unsigned long flag;
+
+	memset(status, 0, sizeof status);
+	ret = bq27520_battery_rsoc(bq27520_di);
+	status[GET_BATTERY_CAPACITY] = (ret < 0) ? 0 : ret;
+
+	status[GET_BATTERY_VOLTAGE] = bq27520_battery_voltage(bq27520_di);
+	status[GET_BATTERY_TEMPERATURE] =
+				bq27520_battery_temperature(bq27520_di);
+
+	spin_lock_irqsave(&current_battery_status.lock, flag);
+	current_battery_status.status[GET_BATTERY_STATUS] = data;
+	current_battery_status.status[GET_BATTERY_VOLTAGE] =
+						status[GET_BATTERY_VOLTAGE];
+	current_battery_status.status[GET_BATTERY_TEMPERATURE] =
+						status[GET_BATTERY_TEMPERATURE];
+	current_battery_status.status[GET_BATTERY_CAPACITY] =
+						status[GET_BATTERY_CAPACITY];
+	spin_unlock_irqrestore(&current_battery_status.lock, flag);
+}
+
+/* only if battery charging satus changes then notify msm_charger. otherwise
+ * only refresh current_batter_status
+ */
+static int if_notify_msm_charger(int *data)
+{
+	int ret = 0, flags = 0, status = 0;
+	unsigned long flag;
+
+	ret = bq27520_read(BQ27520_REG_FLAGS, &flags, 0, bq27520_di);
+	if (ret < 0) {
+		dev_err(bq27520_di->dev, "error %d reading register %02x\n",
+			ret, BQ27520_REG_FLAGS);
+		status = POWER_SUPPLY_STATUS_UNKNOWN;
+	} else {
+		if (flags & BQ27520_FLAG_FC)
+			status = POWER_SUPPLY_STATUS_FULL;
+		else if (flags & BQ27520_FLAG_DSC)
+			status = POWER_SUPPLY_STATUS_DISCHARGING;
+		else
+			status = POWER_SUPPLY_STATUS_CHARGING;
+	}
+
+	*data = status;
+	spin_lock_irqsave(&current_battery_status.lock, flag);
+	ret = (status != current_battery_status.status[GET_BATTERY_STATUS]);
+	spin_unlock_irqrestore(&current_battery_status.lock, flag);
+	return ret;
+}
+
+static void battery_status_poller(struct work_struct *work)
+{
+	int status = 0, temp = 0;
+
+	temp = if_notify_msm_charger(&status);
+	update_current_battery_status(status);
+	if (temp)
+		msm_charger_notify_event(NULL, CHG_BATT_STATUS_CHANGE);
+
+	schedule_delayed_work(&current_battery_status.poller,
+				BQ27520_POLLING_STATUS);
+}
+
+static void bq27520_hw_config(struct work_struct *work)
+{
+	int ret = 0, flags = 0, type = 0, fw_ver = 0, status = 0;
+	struct bq27520_device_info *di;
+
+	di  = container_of(work, struct bq27520_device_info, hw_config.work);
+
+	pr_debug(KERN_INFO "Enter bq27520_hw_config\n");
+	ret = bq27520_chip_config(di);
+	if (ret) {
+		dev_err(di->dev, "Failed to config Bq27520 ret = %d\n", ret);
+		return;
+	}
+	/* bq27520 is ready for access, update current_battery_status by reading
+	 * from hardware
+	 */
+	if_notify_msm_charger(&status);
+	update_current_battery_status(status);
+	msm_charger_notify_event(NULL, CHG_BATT_STATUS_CHANGE);
+
+	enable_irq(di->irq);
+
+	/* poll battery status every 3 seconds, if charging status changes,
+	 * notify msm_charger
+	 */
+	schedule_delayed_work(&current_battery_status.poller,
+				BQ27520_POLLING_STATUS);
+
+	if (di->pdata->enable_dlog) {
+		schedule_work(&di->counter);
+		init_timer(&timer);
+		timer.function = &bq27520_every_30secs;
+		timer.data = (unsigned long)di;
+		timer.expires = jiffies + BQ27520_COULOMB_POLL;
+		add_timer(&timer);
+	}
+
+	bq27520_cntl_cmd(di, BQ27520_SUBCMD_CTNL_STATUS);
+	udelay(66);
+	bq27520_read(BQ27520_REG_CNTL, &flags, 0, di);
+	bq27520_cntl_cmd(di, BQ27520_SUBCMD_DEVCIE_TYPE);
+	udelay(66);
+	bq27520_read(BQ27520_REG_CNTL, &type, 0, di);
+	bq27520_cntl_cmd(di, BQ27520_SUBCMD_FW_VER);
+	udelay(66);
+	bq27520_read(BQ27520_REG_CNTL, &fw_ver, 0, di);
+
+	dev_info(di->dev, "DEVICE_TYPE is 0x%02X, FIRMWARE_VERSION\
+		is 0x%02X\n", type, fw_ver);
+	dev_info(di->dev, "Complete bq27520 configuration 0x%02X\n", flags);
+}
+
+static int bq27520_read_i2c(u8 reg, int *rt_value, int b_single,
+			struct bq27520_device_info *di)
+{
+	struct i2c_client *client = di->client;
+	struct i2c_msg msg[1];
+	unsigned char data[2];
+	int err;
+
+	if (!client->adapter)
+		return -ENODEV;
+
+	msg->addr = client->addr;
+	msg->flags = 0;
+	msg->len = 1;
+	msg->buf = data;
+
+	data[0] = reg;
+	err = i2c_transfer(client->adapter, msg, 1);
+
+	if (err >= 0) {
+		if (!b_single)
+			msg->len = 2;
+		else
+			msg->len = 1;
+
+		msg->flags = I2C_M_RD;
+		err = i2c_transfer(client->adapter, msg, 1);
+		if (err >= 0) {
+			if (!b_single)
+				*rt_value = get_unaligned_le16(data);
+			else
+				*rt_value = data[0];
+
+			return 0;
+		}
+	}
+	return err;
+}
+
+#ifdef CONFIG_BQ27520_TEST_ENABLE
+static int reg;
+static int subcmd;
+static ssize_t bq27520_read_stdcmd(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	int ret;
+	int temp = 0;
+	struct platform_device *client;
+	struct bq27520_device_info *di;
+
+	client = to_platform_device(dev);
+	di = platform_get_drvdata(client);
+
+	if (reg <= BQ27520_REG_ICR && reg > 0x00) {
+		ret = bq27520_read(reg, &temp, 0, di);
+		if (ret)
+			ret = snprintf(buf, PAGE_SIZE, "Read Error!\n");
+		else
+			ret = snprintf(buf, PAGE_SIZE, "0x%02x\n", temp);
+	} else
+		ret = snprintf(buf, PAGE_SIZE, "Register Error!\n");
+
+	return ret;
+}
+
+static ssize_t bq27520_write_stdcmd(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	ssize_t ret = strnlen(buf, PAGE_SIZE);
+	int cmd;
+
+	sscanf(buf, "%x", &cmd);
+	reg = cmd;
+	dev_info(dev, "recv'd cmd is 0x%02X\n", reg);
+	return ret;
+}
+
+static ssize_t bq27520_read_subcmd(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	int ret, temp = 0;
+	struct platform_device *client;
+	struct bq27520_device_info *di;
+
+	client = to_platform_device(dev);
+	di = platform_get_drvdata(client);
+
+	if (subcmd == BQ27520_SUBCMD_DEVCIE_TYPE ||
+		 subcmd == BQ27520_SUBCMD_FW_VER ||
+		 subcmd == BQ27520_SUBCMD_HW_VER ||
+		 subcmd == BQ27520_SUBCMD_CHEM_ID) {
+
+		bq27520_cntl_cmd(di, subcmd);/* Retrieve Chip status */
+		udelay(66);
+		ret = bq27520_read(BQ27520_REG_CNTL, &temp, 0, di);
+
+		if (ret)
+			ret = snprintf(buf, PAGE_SIZE, "Read Error!\n");
+		else
+			ret = snprintf(buf, PAGE_SIZE, "0x%02x\n", temp);
+	} else
+		ret = snprintf(buf, PAGE_SIZE, "Register Error!\n");
+
+	return ret;
+}
+
+static ssize_t bq27520_write_subcmd(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	ssize_t ret = strnlen(buf, PAGE_SIZE);
+	int cmd;
+
+	sscanf(buf, "%x", &cmd);
+	subcmd = cmd;
+	return ret;
+}
+
+static DEVICE_ATTR(std_cmd, S_IRUGO|S_IWUGO, bq27520_read_stdcmd,
+	bq27520_write_stdcmd);
+static DEVICE_ATTR(sub_cmd, S_IRUGO|S_IWUGO, bq27520_read_subcmd,
+	bq27520_write_subcmd);
+static struct attribute *fs_attrs[] = {
+	&dev_attr_std_cmd.attr,
+	&dev_attr_sub_cmd.attr,
+	NULL,
+};
+static struct attribute_group fs_attr_group = {
+	.attrs = fs_attrs,
+};
+
+static struct platform_device this_device = {
+	.name = "bq27520-test",
+	.id = -1,
+	.dev.platform_data = NULL,
+};
+#endif
+
+static irqreturn_t soc_irqhandler(int irq, void *dev_id)
+{
+	int status = 0, temp = 0;
+
+	temp = if_notify_msm_charger(&status);
+	update_current_battery_status(status);
+	if (temp)
+		msm_charger_notify_event(NULL, CHG_BATT_STATUS_CHANGE);
+	return IRQ_HANDLED;
+}
+
+static struct regulator *vreg_bq27520;
+static int bq27520_power(bool enable, struct bq27520_device_info *di)
+{
+	int rc = 0, ret;
+	const struct bq27520_platform_data *platdata;
+
+	platdata = di->pdata;
+	if (enable) {
+		/* switch on Vreg_S3 */
+		rc = regulator_enable(vreg_bq27520);
+		if (rc < 0) {
+			dev_err(di->dev, "%s: vreg %s %s failed (%d)\n",
+				__func__, platdata->vreg_name, "enable", rc);
+			goto vreg_fail;
+		}
+
+		/* Battery gauge enable and switch on onchip 2.5V LDO */
+		rc = gpio_request(platdata->chip_en, "GAUGE_EN");
+		if (rc) {
+			dev_err(di->dev, "%s: fail to request gpio %d (%d)\n",
+				__func__, platdata->chip_en, rc);
+			goto vreg_fail;
+		}
+
+		gpio_direction_output(platdata->chip_en, 0);
+		gpio_set_value(platdata->chip_en, 1);
+		rc = gpio_request(platdata->soc_int, "GAUGE_SOC_INT");
+		if (rc) {
+			dev_err(di->dev, "%s: fail to request gpio %d (%d)\n",
+				__func__, platdata->soc_int, rc);
+			goto gpio_fail;
+		}
+		gpio_direction_input(platdata->soc_int);
+		di->irq = gpio_to_irq(platdata->soc_int);
+		rc = request_threaded_irq(di->irq, NULL, soc_irqhandler,
+				IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,
+				"BQ27520_IRQ", di);
+		if (rc) {
+			dev_err(di->dev, "%s: fail to request irq %d (%d)\n",
+				__func__, platdata->soc_int, rc);
+			goto irqreq_fail;
+		} else {
+			disable_irq_nosync(di->irq);
+		}
+	} else {
+		free_irq(di->irq, di);
+		gpio_free(platdata->soc_int);
+		/* switch off on-chip 2.5V LDO and disable Battery gauge */
+		gpio_set_value(platdata->chip_en, 0);
+		gpio_free(platdata->chip_en);
+		/* switch off Vreg_S3 */
+		rc = regulator_disable(vreg_bq27520);
+		if (rc < 0) {
+			dev_err(di->dev, "%s: vreg %s %s failed (%d)\n",
+				__func__, platdata->vreg_name, "disable", rc);
+			goto vreg_fail;
+		}
+	}
+	return rc;
+
+irqreq_fail:
+	gpio_free(platdata->soc_int);
+gpio_fail:
+	gpio_set_value(platdata->chip_en, 0);
+	gpio_free(platdata->chip_en);
+vreg_fail:
+	ret = !enable ? regulator_enable(vreg_bq27520) :
+		regulator_disable(vreg_bq27520);
+	if (ret < 0) {
+		dev_err(di->dev, "%s: vreg %s %s failed (%d) in err path\n",
+			__func__, platdata->vreg_name,
+			!enable ? "enable" : "disable", ret);
+	}
+	return rc;
+}
+
+static int bq27520_dev_setup(bool enable, struct bq27520_device_info *di)
+{
+	int rc;
+	const struct bq27520_platform_data *platdata;
+
+	platdata = di->pdata;
+	if (enable) {
+		/* enable and set voltage Vreg_S3 */
+		vreg_bq27520 = regulator_get(NULL,
+				platdata->vreg_name);
+		if (IS_ERR(vreg_bq27520)) {
+			dev_err(di->dev, "%s: regulator get of %s\
+				failed (%ld)\n", __func__, platdata->vreg_name,
+				PTR_ERR(vreg_bq27520));
+			rc = PTR_ERR(vreg_bq27520);
+			goto vreg_get_fail;
+		}
+		rc = regulator_set_voltage(vreg_bq27520,
+			platdata->vreg_value, platdata->vreg_value);
+		if (rc) {
+			dev_err(di->dev, "%s: regulator_set_voltage(%s) failed\
+				 (%d)\n", __func__, platdata->vreg_name, rc);
+			goto vreg_get_fail;
+		}
+	} else {
+		regulator_put(vreg_bq27520);
+	}
+	return 0;
+
+vreg_get_fail:
+	regulator_put(vreg_bq27520);
+	return rc;
+}
+
+static int bq27520_battery_probe(struct i2c_client *client,
+				 const struct i2c_device_id *id)
+{
+	struct bq27520_device_info *di;
+	struct bq27520_access_methods *bus;
+	const struct bq27520_platform_data  *pdata;
+	int num, retval = 0;
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+		return -ENODEV;
+
+	pdata = client->dev.platform_data;
+
+	/* Get new ID for the new battery device */
+	retval = idr_pre_get(&battery_id, GFP_KERNEL);
+	if (retval == 0)
+		return -ENOMEM;
+	mutex_lock(&battery_mutex);
+	retval = idr_get_new(&battery_id, client, &num);
+	mutex_unlock(&battery_mutex);
+	if (retval < 0)
+		return retval;
+
+	di = kzalloc(sizeof(*di), GFP_KERNEL);
+	if (!di) {
+		dev_err(&client->dev, "failed to allocate device info data\n");
+		retval = -ENOMEM;
+		goto batt_failed_1;
+	}
+	di->id = num;
+	di->pdata = pdata;
+
+	bus = kzalloc(sizeof(*bus), GFP_KERNEL);
+	if (!bus) {
+		dev_err(&client->dev, "failed to allocate data\n");
+		retval = -ENOMEM;
+		goto batt_failed_2;
+	}
+
+	i2c_set_clientdata(client, di);
+	di->dev = &client->dev;
+	bus->read = &bq27520_read_i2c;
+	di->bus = bus;
+	di->client = client;
+
+#ifdef CONFIG_BQ27520_TEST_ENABLE
+	platform_set_drvdata(&this_device, di);
+	retval = platform_device_register(&this_device);
+	if (!retval) {
+		retval = sysfs_create_group(&this_device.dev.kobj,
+			 &fs_attr_group);
+		if (retval)
+			goto batt_failed_3;
+	} else
+		goto batt_failed_3;
+#endif
+
+	retval = bq27520_dev_setup(true, di);
+	if (retval) {
+		dev_err(&client->dev, "failed to setup ret = %d\n", retval);
+		goto batt_failed_3;
+	}
+
+	retval = bq27520_power(true, di);
+	if (retval) {
+		dev_err(&client->dev, "failed to powerup ret = %d\n", retval);
+		goto batt_failed_3;
+	}
+
+	spin_lock_init(&lock);
+
+	bq27520_di = di;
+	if (pdata->enable_dlog)
+		INIT_WORK(&di->counter, bq27520_coulomb_counter_work);
+
+	INIT_DELAYED_WORK(&current_battery_status.poller,
+			battery_status_poller);
+	INIT_DELAYED_WORK(&di->hw_config, bq27520_hw_config);
+	schedule_delayed_work(&di->hw_config, BQ27520_INIT_DELAY);
+
+	return 0;
+
+batt_failed_3:
+	kfree(bus);
+batt_failed_2:
+	kfree(di);
+batt_failed_1:
+	mutex_lock(&battery_mutex);
+	idr_remove(&battery_id, num);
+	mutex_unlock(&battery_mutex);
+
+	return retval;
+}
+
+static int bq27520_battery_remove(struct i2c_client *client)
+{
+	struct bq27520_device_info *di = i2c_get_clientdata(client);
+
+	if (di->pdata->enable_dlog) {
+		del_timer_sync(&timer);
+		cancel_work_sync(&di->counter);
+		bq27520_cntl_cmd(di, BQ27520_SUBCMD_DISABLE_DLOG);
+		udelay(66);
+	}
+
+	bq27520_cntl_cmd(di, BQ27520_SUBCMD_DISABLE_IT);
+	cancel_delayed_work_sync(&di->hw_config);
+	cancel_delayed_work_sync(&current_battery_status.poller);
+
+	bq27520_dev_setup(false, di);
+	bq27520_power(false, di);
+
+	kfree(di->bus);
+
+	mutex_lock(&battery_mutex);
+	idr_remove(&battery_id, di->id);
+	mutex_unlock(&battery_mutex);
+
+	kfree(di);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int bq27520_suspend(struct device *dev)
+{
+	struct bq27520_device_info *di = dev_get_drvdata(dev);
+
+	disable_irq_nosync(di->irq);
+	if (di->pdata->enable_dlog) {
+		del_timer_sync(&timer);
+		cancel_work_sync(&di->counter);
+	}
+
+	cancel_delayed_work_sync(&current_battery_status.poller);
+	return 0;
+}
+
+static int bq27520_resume(struct device *dev)
+{
+	struct bq27520_device_info *di = dev_get_drvdata(dev);
+
+	enable_irq(di->irq);
+	if (di->pdata->enable_dlog)
+		add_timer(&timer);
+
+	schedule_delayed_work(&current_battery_status.poller,
+				BQ27520_POLLING_STATUS);
+	return 0;
+}
+
+static const struct dev_pm_ops bq27520_pm_ops = {
+	.suspend = bq27520_suspend,
+	.resume = bq27520_resume,
+};
+#endif
+
+static const struct i2c_device_id bq27520_id[] = {
+	{ "bq27520", 1 },
+	{},
+};
+MODULE_DEVICE_TABLE(i2c, BQ27520_id);
+
+static struct i2c_driver bq27520_battery_driver = {
+	.driver = {
+		.name = "bq27520-battery",
+		.owner = THIS_MODULE,
+#ifdef CONFIG_PM
+		.pm = &bq27520_pm_ops,
+#endif
+	},
+	.probe = bq27520_battery_probe,
+	.remove = bq27520_battery_remove,
+	.id_table = bq27520_id,
+};
+
+static void init_battery_status(void)
+{
+	spin_lock_init(&current_battery_status.lock);
+	current_battery_status.status[GET_BATTERY_STATUS] =
+			POWER_SUPPLY_STATUS_UNKNOWN;
+}
+
+static int __init bq27520_battery_init(void)
+{
+	int ret;
+
+	/* initialize current_battery_status, and register with msm-charger */
+	init_battery_status();
+	msm_battery_gauge_register(&bq27520_batt_gauge);
+
+	ret = i2c_add_driver(&bq27520_battery_driver);
+	if (ret)
+		printk(KERN_ERR "Unable to register driver ret = %d\n", ret);
+
+	return ret;
+}
+module_init(bq27520_battery_init);
+
+static void __exit bq27520_battery_exit(void)
+{
+	i2c_del_driver(&bq27520_battery_driver);
+	msm_battery_gauge_unregister(&bq27520_batt_gauge);
+}
+module_exit(bq27520_battery_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Qualcomm Innovation Center, Inc.");
+MODULE_DESCRIPTION("BQ27520 battery monitor driver");
diff --git a/drivers/power/bq27541_fuelgauger.c b/drivers/power/bq27541_fuelgauger.c
new file mode 100644
index 0000000..516a861
--- /dev/null
+++ b/drivers/power/bq27541_fuelgauger.c
@@ -0,0 +1,623 @@
+/* Copyright (C) 2008 Rodolfo Giometti <giometti@linux.it>
+ * Copyright (C) 2008 Eurotech S.p.A. <info@eurotech.it>
+ * Based on a previous work by Copyright (C) 2008 Texas Instruments, Inc.
+ *
+ * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/param.h>
+#include <linux/jiffies.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/idr.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <asm/unaligned.h>
+#include <linux/time.h>
+#include <linux/mfd/pmic8058.h>
+#include <linux/regulator/pmic8058-regulator.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/regulator/machine.h>
+#include <linux/err.h>
+#include <linux/msm-charger.h>
+#include <linux/i2c/bq27520.h> /* use the same platform data as bq27520 */
+
+#define DRIVER_VERSION			"1.1.0"
+/* Bq27541 standard data commands */
+#define BQ27541_REG_CNTL		0x00
+#define BQ27541_REG_AR			0x02
+#define BQ27541_REG_ARTTE		0x04
+#define BQ27541_REG_TEMP		0x06
+#define BQ27541_REG_VOLT		0x08
+#define BQ27541_REG_FLAGS		0x0A
+#define BQ27541_REG_NAC			0x0C
+#define BQ27541_REG_FAC			0x0e
+#define BQ27541_REG_RM			0x10
+#define BQ27541_REG_FCC			0x12
+#define BQ27541_REG_AI			0x14
+#define BQ27541_REG_TTE			0x16
+#define BQ27541_REG_TTF			0x18
+#define BQ27541_REG_SI			0x1a
+#define BQ27541_REG_STTE		0x1c
+#define BQ27541_REG_MLI			0x1e
+#define BQ27541_REG_MLTTE		0x20
+#define BQ27541_REG_AE			0x22
+#define BQ27541_REG_AP			0x24
+#define BQ27541_REG_TTECP		0x26
+#define BQ27541_REG_SOH			0x28
+#define BQ27541_REG_SOC			0x2c
+#define BQ27541_REG_NIC			0x2e
+#define BQ27541_REG_ICR			0x30
+#define BQ27541_REG_LOGIDX		0x32
+#define BQ27541_REG_LOGBUF		0x34
+
+#define BQ27541_FLAG_DSC		BIT(0)
+#define BQ27541_FLAG_FC			BIT(9)
+
+#define BQ27541_CS_DLOGEN		BIT(15)
+#define BQ27541_CS_SS		    BIT(13)
+
+/* Control subcommands */
+#define BQ27541_SUBCMD_CTNL_STATUS  0x0000
+#define BQ27541_SUBCMD_DEVCIE_TYPE  0x0001
+#define BQ27541_SUBCMD_FW_VER  0x0002
+#define BQ27541_SUBCMD_HW_VER  0x0003
+#define BQ27541_SUBCMD_DF_CSUM  0x0004
+#define BQ27541_SUBCMD_PREV_MACW   0x0007
+#define BQ27541_SUBCMD_CHEM_ID   0x0008
+#define BQ27541_SUBCMD_BD_OFFSET   0x0009
+#define BQ27541_SUBCMD_INT_OFFSET  0x000a
+#define BQ27541_SUBCMD_CC_VER   0x000b
+#define BQ27541_SUBCMD_OCV  0x000c
+#define BQ27541_SUBCMD_BAT_INS   0x000d
+#define BQ27541_SUBCMD_BAT_REM   0x000e
+#define BQ27541_SUBCMD_SET_HIB   0x0011
+#define BQ27541_SUBCMD_CLR_HIB   0x0012
+#define BQ27541_SUBCMD_SET_SLP   0x0013
+#define BQ27541_SUBCMD_CLR_SLP   0x0014
+#define BQ27541_SUBCMD_FCT_RES   0x0015
+#define BQ27541_SUBCMD_ENABLE_DLOG  0x0018
+#define BQ27541_SUBCMD_DISABLE_DLOG 0x0019
+#define BQ27541_SUBCMD_SEALED   0x0020
+#define BQ27541_SUBCMD_ENABLE_IT    0x0021
+#define BQ27541_SUBCMD_DISABLE_IT   0x0023
+#define BQ27541_SUBCMD_CAL_MODE  0x0040
+#define BQ27541_SUBCMD_RESET   0x0041
+#define ZERO_DEGREE_CELSIUS_IN_TENTH_KELVIN   (-2731)
+#define BQ27541_INIT_DELAY   ((HZ)*1)
+
+/* If the system has several batteries we need a different name for each
+ * of them...
+ */
+static DEFINE_IDR(battery_id);
+static DEFINE_MUTEX(battery_mutex);
+
+struct bq27541_device_info;
+struct bq27541_access_methods {
+	int (*read)(u8 reg, int *rt_value, int b_single,
+		struct bq27541_device_info *di);
+};
+
+struct bq27541_device_info {
+	struct device			*dev;
+	int				id;
+	struct bq27541_access_methods	*bus;
+	struct i2c_client		*client;
+	struct work_struct		counter;
+	/* 300ms delay is needed after bq27541 is powered up
+	 * and before any successful I2C transaction
+	 */
+	struct  delayed_work		hw_config;
+};
+
+static int coulomb_counter;
+static spinlock_t lock; /* protect access to coulomb_counter */
+
+static int bq27541_i2c_txsubcmd(u8 reg, unsigned short subcmd,
+		struct bq27541_device_info *di);
+
+static int bq27541_read(u8 reg, int *rt_value, int b_single,
+			struct bq27541_device_info *di)
+{
+	return di->bus->read(reg, rt_value, b_single, di);
+}
+
+/*
+ * Return the battery temperature in tenths of degree Celsius
+ * Or < 0 if something fails.
+ */
+static int bq27541_battery_temperature(struct bq27541_device_info *di)
+{
+	int ret;
+	int temp = 0;
+
+	ret = bq27541_read(BQ27541_REG_TEMP, &temp, 0, di);
+	if (ret) {
+		dev_err(di->dev, "error reading temperature\n");
+		return ret;
+	}
+
+	return temp + ZERO_DEGREE_CELSIUS_IN_TENTH_KELVIN;
+}
+
+/*
+ * Return the battery Voltage in milivolts
+ * Or < 0 if something fails.
+ */
+static int bq27541_battery_voltage(struct bq27541_device_info *di)
+{
+	int ret;
+	int volt = 0;
+
+	ret = bq27541_read(BQ27541_REG_VOLT, &volt, 0, di);
+	if (ret) {
+		dev_err(di->dev, "error reading voltage\n");
+		return ret;
+	}
+
+	return volt * 1000;
+}
+
+static void bq27541_cntl_cmd(struct bq27541_device_info *di,
+				int subcmd)
+{
+	bq27541_i2c_txsubcmd(BQ27541_REG_CNTL, subcmd, di);
+}
+
+/*
+ * i2c specific code
+ */
+static int bq27541_i2c_txsubcmd(u8 reg, unsigned short subcmd,
+		struct bq27541_device_info *di)
+{
+	struct i2c_msg msg;
+	unsigned char data[3];
+	int ret;
+
+	if (!di->client)
+		return -ENODEV;
+
+	memset(data, 0, sizeof(data));
+	data[0] = reg;
+	data[1] = subcmd & 0x00FF;
+	data[2] = (subcmd & 0xFF00) >> 8;
+
+	msg.addr = di->client->addr;
+	msg.flags = 0;
+	msg.len = 3;
+	msg.buf = data;
+
+	ret = i2c_transfer(di->client->adapter, &msg, 1);
+	if (ret < 0)
+		return -EIO;
+
+	return 0;
+}
+
+static int bq27541_chip_config(struct bq27541_device_info *di)
+{
+	int flags = 0, ret = 0;
+
+	bq27541_cntl_cmd(di, BQ27541_SUBCMD_CTNL_STATUS);
+	udelay(66);
+	ret = bq27541_read(BQ27541_REG_CNTL, &flags, 0, di);
+	if (ret < 0) {
+		dev_err(di->dev, "error reading register %02x ret = %d\n",
+			 BQ27541_REG_CNTL, ret);
+		return ret;
+	}
+	udelay(66);
+
+	bq27541_cntl_cmd(di, BQ27541_SUBCMD_ENABLE_IT);
+	udelay(66);
+
+	if (!(flags & BQ27541_CS_DLOGEN)) {
+		bq27541_cntl_cmd(di, BQ27541_SUBCMD_ENABLE_DLOG);
+		udelay(66);
+	}
+
+	return 0;
+}
+
+static void bq27541_coulomb_counter_work(struct work_struct *work)
+{
+	int value = 0, temp = 0, index = 0, ret = 0;
+	struct bq27541_device_info *di;
+	unsigned long flags;
+	int count = 0;
+
+	di = container_of(work, struct bq27541_device_info, counter);
+
+	/* retrieve 30 values from FIFO of coulomb data logging buffer
+	 * and average over time
+	 */
+	do {
+		ret = bq27541_read(BQ27541_REG_LOGBUF, &temp, 0, di);
+		if (ret < 0)
+			break;
+		if (temp != 0x7FFF) {
+			++count;
+			value += temp;
+		}
+		/* delay 66uS, waiting time between continuous reading
+		 * results
+		 */
+		udelay(66);
+		ret = bq27541_read(BQ27541_REG_LOGIDX, &index, 0, di);
+		if (ret < 0)
+			break;
+		udelay(66);
+	} while (index != 0 || temp != 0x7FFF);
+
+	if (ret < 0) {
+		dev_err(di->dev, "Error reading datalog register\n");
+		return;
+	}
+
+	if (count) {
+		spin_lock_irqsave(&lock, flags);
+		coulomb_counter = value/count;
+		spin_unlock_irqrestore(&lock, flags);
+	}
+}
+
+struct bq27541_device_info *bq27541_di;
+
+static int bq27541_get_battery_mvolts(void)
+{
+	return bq27541_battery_voltage(bq27541_di);
+}
+
+static int bq27541_get_battery_temperature(void)
+{
+	return bq27541_battery_temperature(bq27541_di);
+}
+static int bq27541_is_battery_present(void)
+{
+	return 1;
+}
+static int bq27541_is_battery_temp_within_range(void)
+{
+	return 1;
+}
+static int bq27541_is_battery_id_valid(void)
+{
+	return 1;
+}
+
+static struct msm_battery_gauge bq27541_batt_gauge = {
+	.get_battery_mvolts		= bq27541_get_battery_mvolts,
+	.get_battery_temperature	= bq27541_get_battery_temperature,
+	.is_battery_present		= bq27541_is_battery_present,
+	.is_battery_temp_within_range	= bq27541_is_battery_temp_within_range,
+	.is_battery_id_valid		= bq27541_is_battery_id_valid,
+};
+static void bq27541_hw_config(struct work_struct *work)
+{
+	int ret = 0, flags = 0, type = 0, fw_ver = 0;
+	struct bq27541_device_info *di;
+
+	di  = container_of(work, struct bq27541_device_info, hw_config.work);
+	ret = bq27541_chip_config(di);
+	if (ret) {
+		dev_err(di->dev, "Failed to config Bq27541\n");
+		return;
+	}
+	msm_battery_gauge_register(&bq27541_batt_gauge);
+
+	bq27541_cntl_cmd(di, BQ27541_SUBCMD_CTNL_STATUS);
+	udelay(66);
+	bq27541_read(BQ27541_REG_CNTL, &flags, 0, di);
+	bq27541_cntl_cmd(di, BQ27541_SUBCMD_DEVCIE_TYPE);
+	udelay(66);
+	bq27541_read(BQ27541_REG_CNTL, &type, 0, di);
+	bq27541_cntl_cmd(di, BQ27541_SUBCMD_FW_VER);
+	udelay(66);
+	bq27541_read(BQ27541_REG_CNTL, &fw_ver, 0, di);
+
+	dev_info(di->dev, "DEVICE_TYPE is 0x%02X, FIRMWARE_VERSION is 0x%02X\n",
+			type, fw_ver);
+	dev_info(di->dev, "Complete bq27541 configuration 0x%02X\n", flags);
+}
+
+static int bq27541_read_i2c(u8 reg, int *rt_value, int b_single,
+			struct bq27541_device_info *di)
+{
+	struct i2c_client *client = di->client;
+	struct i2c_msg msg[1];
+	unsigned char data[2];
+	int err;
+
+	if (!client->adapter)
+		return -ENODEV;
+
+	msg->addr = client->addr;
+	msg->flags = 0;
+	msg->len = 1;
+	msg->buf = data;
+
+	data[0] = reg;
+	err = i2c_transfer(client->adapter, msg, 1);
+
+	if (err >= 0) {
+		if (!b_single)
+			msg->len = 2;
+		else
+			msg->len = 1;
+
+		msg->flags = I2C_M_RD;
+		err = i2c_transfer(client->adapter, msg, 1);
+		if (err >= 0) {
+			if (!b_single)
+				*rt_value = get_unaligned_le16(data);
+			else
+				*rt_value = data[0];
+
+			return 0;
+		}
+	}
+	return err;
+}
+
+#ifdef CONFIG_BQ27541_TEST_ENABLE
+static int reg;
+static int subcmd;
+static ssize_t bq27541_read_stdcmd(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	int ret;
+	int temp = 0;
+	struct platform_device *client;
+	struct bq27541_device_info *di;
+
+	client = to_platform_device(dev);
+	di = platform_get_drvdata(client);
+
+	if (reg <= BQ27541_REG_ICR && reg > 0x00) {
+		ret = bq27541_read(reg, &temp, 0, di);
+		if (ret)
+			ret = snprintf(buf, PAGE_SIZE, "Read Error!\n");
+		else
+			ret = snprintf(buf, PAGE_SIZE, "0x%02x\n", temp);
+	} else
+		ret = snprintf(buf, PAGE_SIZE, "Register Error!\n");
+
+	return ret;
+}
+
+static ssize_t bq27541_write_stdcmd(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	ssize_t ret = strnlen(buf, PAGE_SIZE);
+	int cmd;
+
+	sscanf(buf, "%x", &cmd);
+	reg = cmd;
+	return ret;
+}
+
+static ssize_t bq27541_read_subcmd(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	int ret;
+	int temp = 0;
+	struct platform_device *client;
+	struct bq27541_device_info *di;
+
+	client = to_platform_device(dev);
+	di = platform_get_drvdata(client);
+
+	if (subcmd == BQ27541_SUBCMD_DEVCIE_TYPE ||
+		 subcmd == BQ27541_SUBCMD_FW_VER ||
+		 subcmd == BQ27541_SUBCMD_HW_VER ||
+		 subcmd == BQ27541_SUBCMD_CHEM_ID) {
+
+		bq27541_cntl_cmd(di, subcmd); /* Retrieve Chip status */
+		udelay(66);
+		ret = bq27541_read(BQ27541_REG_CNTL, &temp, 0, di);
+
+		if (ret)
+			ret = snprintf(buf, PAGE_SIZE, "Read Error!\n");
+		else
+			ret = snprintf(buf, PAGE_SIZE, "0x%02x\n", temp);
+	} else
+		ret = snprintf(buf, PAGE_SIZE, "Register Error!\n");
+
+	return ret;
+}
+
+static ssize_t bq27541_write_subcmd(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	ssize_t ret = strnlen(buf, PAGE_SIZE);
+	int cmd;
+
+	sscanf(buf, "%x", &cmd);
+	subcmd = cmd;
+	return ret;
+}
+
+static DEVICE_ATTR(std_cmd, S_IRUGO|S_IWUGO, bq27541_read_stdcmd,
+	bq27541_write_stdcmd);
+static DEVICE_ATTR(sub_cmd, S_IRUGO|S_IWUGO, bq27541_read_subcmd,
+	bq27541_write_subcmd);
+static struct attribute *fs_attrs[] = {
+	&dev_attr_std_cmd.attr,
+	&dev_attr_sub_cmd.attr,
+	NULL,
+};
+static struct attribute_group fs_attr_group = {
+	.attrs = fs_attrs,
+};
+
+static struct platform_device this_device = {
+	.name			= "bq27541-test",
+	.id			= -1,
+	.dev.platform_data	= NULL,
+};
+#endif
+
+static int bq27541_battery_probe(struct i2c_client *client,
+				 const struct i2c_device_id *id)
+{
+	char *name;
+	struct bq27541_device_info *di;
+	struct bq27541_access_methods *bus;
+	int num;
+	int retval = 0;
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+		return -ENODEV;
+
+	/* Get new ID for the new battery device */
+	retval = idr_pre_get(&battery_id, GFP_KERNEL);
+	if (retval == 0)
+		return -ENOMEM;
+	mutex_lock(&battery_mutex);
+	retval = idr_get_new(&battery_id, client, &num);
+	mutex_unlock(&battery_mutex);
+	if (retval < 0)
+		return retval;
+
+	name = kasprintf(GFP_KERNEL, "%s-%d", id->name, num);
+	if (!name) {
+		dev_err(&client->dev, "failed to allocate device name\n");
+		retval = -ENOMEM;
+		goto batt_failed_1;
+	}
+
+	di = kzalloc(sizeof(*di), GFP_KERNEL);
+	if (!di) {
+		dev_err(&client->dev, "failed to allocate device info data\n");
+		retval = -ENOMEM;
+		goto batt_failed_2;
+	}
+	di->id = num;
+
+	bus = kzalloc(sizeof(*bus), GFP_KERNEL);
+	if (!bus) {
+		dev_err(&client->dev, "failed to allocate access method "
+					"data\n");
+		retval = -ENOMEM;
+		goto batt_failed_3;
+	}
+
+	i2c_set_clientdata(client, di);
+	di->dev = &client->dev;
+	bus->read = &bq27541_read_i2c;
+	di->bus = bus;
+	di->client = client;
+
+#ifdef CONFIG_BQ27541_TEST_ENABLE
+	platform_set_drvdata(&this_device, di);
+	retval = platform_device_register(&this_device);
+	if (!retval) {
+		retval = sysfs_create_group(&this_device.dev.kobj,
+			 &fs_attr_group);
+		if (retval)
+			goto batt_failed_4;
+	} else
+		goto batt_failed_4;
+#endif
+
+	if (retval) {
+		dev_err(&client->dev, "failed to setup bq27541\n");
+		goto batt_failed_4;
+	}
+
+	if (retval) {
+		dev_err(&client->dev, "failed to powerup bq27541\n");
+		goto batt_failed_4;
+	}
+
+	spin_lock_init(&lock);
+
+	bq27541_di = di;
+	INIT_WORK(&di->counter, bq27541_coulomb_counter_work);
+	INIT_DELAYED_WORK(&di->hw_config, bq27541_hw_config);
+	schedule_delayed_work(&di->hw_config, BQ27541_INIT_DELAY);
+	return 0;
+
+batt_failed_4:
+	kfree(bus);
+batt_failed_3:
+	kfree(di);
+batt_failed_2:
+	kfree(name);
+batt_failed_1:
+	mutex_lock(&battery_mutex);
+	idr_remove(&battery_id, num);
+	mutex_unlock(&battery_mutex);
+
+	return retval;
+}
+
+static int bq27541_battery_remove(struct i2c_client *client)
+{
+	struct bq27541_device_info *di = i2c_get_clientdata(client);
+
+	msm_battery_gauge_unregister(&bq27541_batt_gauge);
+	bq27541_cntl_cmd(di, BQ27541_SUBCMD_DISABLE_DLOG);
+	udelay(66);
+	bq27541_cntl_cmd(di, BQ27541_SUBCMD_DISABLE_IT);
+	cancel_delayed_work_sync(&di->hw_config);
+
+	kfree(di->bus);
+
+	mutex_lock(&battery_mutex);
+	idr_remove(&battery_id, di->id);
+	mutex_unlock(&battery_mutex);
+
+	kfree(di);
+	return 0;
+}
+
+static const struct i2c_device_id bq27541_id[] = {
+	{ "bq27541", 1 },
+	{},
+};
+MODULE_DEVICE_TABLE(i2c, BQ27541_id);
+
+static struct i2c_driver bq27541_battery_driver = {
+	.driver		= {
+			.name = "bq27541-battery",
+	},
+	.probe		= bq27541_battery_probe,
+	.remove		= bq27541_battery_remove,
+	.id_table	= bq27541_id,
+};
+
+static int __init bq27541_battery_init(void)
+{
+	int ret;
+
+	ret = i2c_add_driver(&bq27541_battery_driver);
+	if (ret)
+		printk(KERN_ERR "Unable to register BQ27541 driver\n");
+
+	return ret;
+}
+module_init(bq27541_battery_init);
+
+static void __exit bq27541_battery_exit(void)
+{
+	i2c_del_driver(&bq27541_battery_driver);
+}
+module_exit(bq27541_battery_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Qualcomm Innovation Center, Inc.");
+MODULE_DESCRIPTION("BQ27541 battery monitor driver");
diff --git a/drivers/power/isl9519q.c b/drivers/power/isl9519q.c
new file mode 100644
index 0000000..4954a45
--- /dev/null
+++ b/drivers/power/isl9519q.c
@@ -0,0 +1,517 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include <linux/workqueue.h>
+#include <linux/interrupt.h>
+#include <linux/msm-charger.h>
+#include <linux/slab.h>
+#include <linux/i2c/isl9519.h>
+#include <linux/msm_adc.h>
+
+#define CHG_CURRENT_REG		0x14
+#define MAX_SYS_VOLTAGE_REG	0x15
+#define CONTROL_REG		0x3D
+#define MIN_SYS_VOLTAGE_REG	0x3E
+#define INPUT_CURRENT_REG	0x3F
+#define MANUFACTURER_ID_REG	0xFE
+#define DEVICE_ID_REG		0xFF
+
+#define TRCKL_CHG_STATUS_BIT	0x80
+
+#define ISL9519_CHG_PERIOD	((HZ) * 150)
+
+struct isl9519q_struct {
+	struct i2c_client		*client;
+	struct delayed_work		charge_work;
+	int				present;
+	int				batt_present;
+	bool				charging;
+	int				chgcurrent;
+	int				term_current;
+	int				input_current;
+	int				max_system_voltage;
+	int				min_system_voltage;
+	int				valid_n_gpio;
+	struct dentry			*dent;
+	struct msm_hardware_charger	adapter_hw_chg;
+};
+
+static int isl9519q_read_reg(struct i2c_client *client, int reg,
+	u16 *val)
+{
+	int ret;
+	struct isl9519q_struct *isl_chg;
+
+	isl_chg = i2c_get_clientdata(client);
+	ret = i2c_smbus_read_word_data(isl_chg->client, reg);
+
+	if (ret < 0) {
+		dev_err(&isl_chg->client->dev,
+			"i2c read fail: can't read from %02x: %d\n", reg, ret);
+		return -EAGAIN;
+	} else
+		*val = ret;
+
+	return 0;
+}
+
+static int isl9519q_write_reg(struct i2c_client *client, int reg,
+	u16 val)
+{
+	int ret;
+	struct isl9519q_struct *isl_chg;
+
+	isl_chg = i2c_get_clientdata(client);
+	ret = i2c_smbus_write_word_data(isl_chg->client, reg, val);
+
+	if (ret < 0) {
+		dev_err(&isl_chg->client->dev,
+			"i2c write fail: can't write %02x to %02x: %d\n",
+			val, reg, ret);
+		return -EAGAIN;
+	}
+	return 0;
+}
+
+static int isl_read_adc(int channel, int *mv_reading)
+{
+	int ret;
+	void *h;
+	struct adc_chan_result adc_chan_result;
+	struct completion  conv_complete_evt;
+
+	pr_debug("%s: called for %d\n", __func__, channel);
+	ret = adc_channel_open(channel, &h);
+	if (ret) {
+		pr_err("%s: couldnt open channel %d ret=%d\n",
+					__func__, channel, ret);
+		goto out;
+	}
+	init_completion(&conv_complete_evt);
+	ret = adc_channel_request_conv(h, &conv_complete_evt);
+	if (ret) {
+		pr_err("%s: couldnt request conv channel %d ret=%d\n",
+						__func__, channel, ret);
+		goto out;
+	}
+	ret = wait_for_completion_interruptible(&conv_complete_evt);
+	if (ret) {
+		pr_err("%s: wait interrupted channel %d ret=%d\n",
+						__func__, channel, ret);
+		goto out;
+	}
+	ret = adc_channel_read_result(h, &adc_chan_result);
+	if (ret) {
+		pr_err("%s: couldnt read result channel %d ret=%d\n",
+						__func__, channel, ret);
+		goto out;
+	}
+	ret = adc_channel_close(h);
+	if (ret)
+		pr_err("%s: couldnt close channel %d ret=%d\n",
+					__func__, channel, ret);
+	if (mv_reading)
+		*mv_reading = (int)adc_chan_result.measurement;
+
+	pr_debug("%s: done for %d\n", __func__, channel);
+	return adc_chan_result.physical;
+out:
+	*mv_reading = 0;
+	pr_debug("%s: done with error for %d\n", __func__, channel);
+	return -EINVAL;
+
+}
+
+static void isl9519q_charge(struct work_struct *isl9519_work)
+{
+	u16 temp;
+	int ret;
+	struct isl9519q_struct *isl_chg;
+	int isl_charger_current;
+	int mv_reading;
+
+	isl_chg = container_of(isl9519_work, struct isl9519q_struct,
+			charge_work.work);
+
+	dev_dbg(&isl_chg->client->dev, "%s\n", __func__);
+
+	if (isl_chg->charging) {
+		isl_charger_current = isl_read_adc(CHANNEL_ADC_BATT_AMON,
+								&mv_reading);
+		dev_dbg(&isl_chg->client->dev, "%s mv_reading=%d\n",
+				__func__, mv_reading);
+		dev_dbg(&isl_chg->client->dev, "%s isl_charger_current=%d\n",
+				__func__, isl_charger_current);
+		if (isl_charger_current >= 0
+			&& isl_charger_current <= isl_chg->term_current) {
+			msm_charger_notify_event(
+					&isl_chg->adapter_hw_chg,
+					CHG_DONE_EVENT);
+		}
+		isl9519q_write_reg(isl_chg->client, CHG_CURRENT_REG,
+				isl_chg->chgcurrent);
+		ret = isl9519q_read_reg(isl_chg->client, CONTROL_REG, &temp);
+		if (!ret) {
+			if (!(temp & TRCKL_CHG_STATUS_BIT))
+				msm_charger_notify_event(
+						&isl_chg->adapter_hw_chg,
+						CHG_BATT_BEGIN_FAST_CHARGING);
+		} else {
+			dev_err(&isl_chg->client->dev,
+				"%s couldnt read cntrl reg\n", __func__);
+		}
+		schedule_delayed_work(&isl_chg->charge_work,
+						ISL9519_CHG_PERIOD);
+	}
+}
+
+static int isl9519q_start_charging(struct msm_hardware_charger *hw_chg,
+		int chg_voltage, int chg_current)
+{
+	struct isl9519q_struct *isl_chg;
+	int ret = 0;
+
+	isl_chg = container_of(hw_chg, struct isl9519q_struct, adapter_hw_chg);
+	if (isl_chg->charging)
+		/* we are already charging */
+		return 0;
+
+	dev_dbg(&isl_chg->client->dev, "%s\n", __func__);
+
+	ret = isl9519q_write_reg(isl_chg->client, CHG_CURRENT_REG,
+						isl_chg->chgcurrent);
+	if (ret) {
+		dev_err(&isl_chg->client->dev,
+			"%s coulnt write to current_reg\n", __func__);
+		goto out;
+	}
+
+	dev_dbg(&isl_chg->client->dev, "%s starting timed work\n",
+							__func__);
+	schedule_delayed_work(&isl_chg->charge_work,
+						ISL9519_CHG_PERIOD);
+	isl_chg->charging = true;
+
+out:
+	return ret;
+}
+
+static int isl9519q_stop_charging(struct msm_hardware_charger *hw_chg)
+{
+	struct isl9519q_struct *isl_chg;
+	int ret = 0;
+
+	isl_chg = container_of(hw_chg, struct isl9519q_struct, adapter_hw_chg);
+	if (!(isl_chg->charging))
+		/* we arent charging */
+		return 0;
+
+	dev_dbg(&isl_chg->client->dev, "%s\n", __func__);
+
+	ret = isl9519q_write_reg(isl_chg->client, CHG_CURRENT_REG, 0);
+	if (ret) {
+		dev_err(&isl_chg->client->dev,
+			"%s coulnt write to current_reg\n", __func__);
+		goto out;
+	}
+
+	isl_chg->charging = false;
+	cancel_delayed_work(&isl_chg->charge_work);
+out:
+	return ret;
+}
+
+static int isl9519q_charging_switched(struct msm_hardware_charger *hw_chg)
+{
+	struct isl9519q_struct *isl_chg;
+
+	isl_chg = container_of(hw_chg, struct isl9519q_struct, adapter_hw_chg);
+	dev_dbg(&isl_chg->client->dev, "%s\n", __func__);
+	return 0;
+}
+
+static irqreturn_t isl_valid_handler(int irq, void *dev_id)
+{
+	int val;
+	struct isl9519q_struct *isl_chg;
+	struct i2c_client *client = dev_id;
+
+	isl_chg = i2c_get_clientdata(client);
+	val = gpio_get_value_cansleep(isl_chg->valid_n_gpio);
+	if (val < 0) {
+		dev_err(&isl_chg->client->dev,
+			"%s gpio_get_value failed for %d ret=%d\n", __func__,
+			isl_chg->valid_n_gpio, val);
+		goto err;
+	}
+	dev_dbg(&isl_chg->client->dev, "%s val=%d\n", __func__, val);
+
+	if (val) {
+		if (isl_chg->present == 1) {
+			msm_charger_notify_event(&isl_chg->adapter_hw_chg,
+						 CHG_REMOVED_EVENT);
+			isl_chg->present = 0;
+		}
+	} else {
+		if (isl_chg->present == 0) {
+			msm_charger_notify_event(&isl_chg->adapter_hw_chg,
+						 CHG_INSERTED_EVENT);
+			isl_chg->present = 1;
+		}
+	}
+err:
+	return IRQ_HANDLED;
+}
+
+#define MAX_VOLTAGE_REG_MASK  0x3FF0
+#define MIN_VOLTAGE_REG_MASK  0x3F00
+#define DEFAULT_MAX_VOLTAGE_REG_VALUE	0x1070
+#define DEFAULT_MIN_VOLTAGE_REG_VALUE	0x0D00
+
+static int __devinit isl9519q_probe(struct i2c_client *client,
+				    const struct i2c_device_id *id)
+{
+	struct isl_platform_data *pdata;
+	struct isl9519q_struct *isl_chg;
+	int ret;
+
+	ret = 0;
+	pdata = client->dev.platform_data;
+
+	if (pdata == NULL) {
+		dev_err(&client->dev, "%s no platform data\n", __func__);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (!i2c_check_functionality(client->adapter,
+				I2C_FUNC_SMBUS_WORD_DATA)) {
+		ret = -EIO;
+		goto out;
+	}
+
+	isl_chg = kzalloc(sizeof(*isl_chg), GFP_KERNEL);
+	if (!isl_chg) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	INIT_DELAYED_WORK(&isl_chg->charge_work, isl9519q_charge);
+	isl_chg->client = client;
+	isl_chg->chgcurrent = pdata->chgcurrent;
+	isl_chg->term_current = pdata->term_current;
+	isl_chg->input_current = pdata->input_current;
+	isl_chg->max_system_voltage = pdata->max_system_voltage;
+	isl_chg->min_system_voltage = pdata->min_system_voltage;
+	isl_chg->valid_n_gpio = pdata->valid_n_gpio;
+
+	/* h/w ignores lower 7 bits of charging current and input current */
+	isl_chg->chgcurrent &= ~0x7F;
+	isl_chg->input_current &= ~0x7F;
+
+	isl_chg->adapter_hw_chg.type = CHG_TYPE_AC;
+	isl_chg->adapter_hw_chg.rating = 2;
+	isl_chg->adapter_hw_chg.name = "isl-adapter";
+	isl_chg->adapter_hw_chg.start_charging = isl9519q_start_charging;
+	isl_chg->adapter_hw_chg.stop_charging = isl9519q_stop_charging;
+	isl_chg->adapter_hw_chg.charging_switched = isl9519q_charging_switched;
+
+	if (pdata->chg_detection_config) {
+		ret = pdata->chg_detection_config();
+		if (ret) {
+			dev_err(&client->dev, "%s valid config failed ret=%d\n",
+				__func__, ret);
+			goto free_isl_chg;
+		}
+	}
+
+	ret = gpio_request(pdata->valid_n_gpio, "isl_charger_valid");
+	if (ret) {
+		dev_err(&client->dev, "%s gpio_request failed for %d ret=%d\n",
+			__func__, pdata->valid_n_gpio, ret);
+		goto free_isl_chg;
+	}
+
+	i2c_set_clientdata(client, isl_chg);
+
+	ret = msm_charger_register(&isl_chg->adapter_hw_chg);
+	if (ret) {
+		dev_err(&client->dev,
+			"%s msm_charger_register failed for ret =%d\n",
+			__func__, ret);
+		goto free_gpio;
+	}
+
+	ret = request_threaded_irq(client->irq, NULL,
+				   isl_valid_handler,
+				   IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
+				   "isl_charger_valid", client);
+	if (ret) {
+		dev_err(&client->dev,
+			"%s request_threaded_irq failed for %d ret =%d\n",
+			__func__, client->irq, ret);
+		goto unregister;
+	}
+	irq_set_irq_wake(client->irq, 1);
+
+	isl_chg->max_system_voltage &= MAX_VOLTAGE_REG_MASK;
+	isl_chg->min_system_voltage &= MIN_VOLTAGE_REG_MASK;
+	if (isl_chg->max_system_voltage == 0)
+		isl_chg->max_system_voltage = DEFAULT_MAX_VOLTAGE_REG_VALUE;
+	if (isl_chg->min_system_voltage == 0)
+		isl_chg->min_system_voltage = DEFAULT_MIN_VOLTAGE_REG_VALUE;
+
+	ret = isl9519q_write_reg(isl_chg->client, MAX_SYS_VOLTAGE_REG,
+			isl_chg->max_system_voltage);
+	if (ret) {
+		dev_err(&client->dev,
+			"%s couldnt write to MAX_SYS_VOLTAGE_REG ret=%d\n",
+			__func__, ret);
+		goto free_irq;
+	}
+
+	ret = isl9519q_write_reg(isl_chg->client, MIN_SYS_VOLTAGE_REG,
+			isl_chg->min_system_voltage);
+	if (ret) {
+		dev_err(&client->dev,
+			"%s couldnt write to MIN_SYS_VOLTAGE_REG ret=%d\n",
+			__func__, ret);
+		goto free_irq;
+	}
+
+	if (isl_chg->input_current) {
+		ret = isl9519q_write_reg(isl_chg->client,
+				INPUT_CURRENT_REG,
+				isl_chg->input_current);
+		if (ret) {
+			dev_err(&client->dev,
+				"%s couldnt write INPUT_CURRENT_REG ret=%d\n",
+				__func__, ret);
+			goto free_irq;
+		}
+	}
+
+	ret = gpio_get_value_cansleep(isl_chg->valid_n_gpio);
+	if (ret < 0) {
+		dev_err(&client->dev,
+			"%s gpio_get_value failed for %d ret=%d\n", __func__,
+			pdata->valid_n_gpio, ret);
+		/* assume absent */
+		ret = 1;
+	}
+	if (!ret) {
+		msm_charger_notify_event(&isl_chg->adapter_hw_chg,
+				CHG_INSERTED_EVENT);
+		isl_chg->present = 1;
+	}
+
+	pr_debug("%s OK chg_present=%d\n", __func__, isl_chg->present);
+	return 0;
+
+free_irq:
+	free_irq(client->irq, NULL);
+unregister:
+	msm_charger_register(&isl_chg->adapter_hw_chg);
+free_gpio:
+	gpio_free(pdata->valid_n_gpio);
+free_isl_chg:
+	kfree(isl_chg);
+out:
+	return ret;
+}
+
+static int __devexit isl9519q_remove(struct i2c_client *client)
+{
+	struct isl_platform_data *pdata;
+	struct isl9519q_struct *isl_chg = i2c_get_clientdata(client);
+
+	pdata = client->dev.platform_data;
+	gpio_free(pdata->valid_n_gpio);
+	free_irq(client->irq, client);
+	cancel_delayed_work_sync(&isl_chg->charge_work);
+	msm_charger_notify_event(&isl_chg->adapter_hw_chg, CHG_REMOVED_EVENT);
+	msm_charger_unregister(&isl_chg->adapter_hw_chg);
+	return 0;
+}
+
+static const struct i2c_device_id isl9519q_id[] = {
+	{"isl9519q", 0},
+	{},
+};
+
+#ifdef CONFIG_PM
+static int isl9519q_suspend(struct device *dev)
+{
+	struct isl9519q_struct *isl_chg = dev_get_drvdata(dev);
+
+	dev_dbg(&isl_chg->client->dev, "%s\n", __func__);
+	/*
+	 * do not suspend while we are charging
+	 * because we need to periodically update the register
+	 * for charging to proceed
+	 */
+	if (isl_chg->charging)
+		return -EBUSY;
+	return 0;
+}
+
+static int isl9519q_resume(struct device *dev)
+{
+	struct isl9519q_struct *isl_chg = dev_get_drvdata(dev);
+
+	dev_dbg(&isl_chg->client->dev, "%s\n", __func__);
+	return 0;
+}
+
+static const struct dev_pm_ops isl9519q_pm_ops = {
+	.suspend = isl9519q_suspend,
+	.resume = isl9519q_resume,
+};
+#endif
+
+static struct i2c_driver isl9519q_driver = {
+	.driver = {
+		   .name = "isl9519q",
+		   .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+		   .pm = &isl9519q_pm_ops,
+#endif
+	},
+	.probe = isl9519q_probe,
+	.remove = __devexit_p(isl9519q_remove),
+	.id_table = isl9519q_id,
+};
+
+static int __init isl9519q_init(void)
+{
+	return i2c_add_driver(&isl9519q_driver);
+}
+
+module_init(isl9519q_init);
+
+static void __exit isl9519q_exit(void)
+{
+	return i2c_del_driver(&isl9519q_driver);
+}
+
+module_exit(isl9519q_exit);
+
+MODULE_AUTHOR("Abhijeet Dharmapurikar <adharmap@codeaurora.org>");
+MODULE_DESCRIPTION("Driver for ISL9519Q Charger chip");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/power/msm_battery.c b/drivers/power/msm_battery.c
new file mode 100644
index 0000000..464a1b5
--- /dev/null
+++ b/drivers/power/msm_battery.c
@@ -0,0 +1,1592 @@
+/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/*
+ * this needs to be before <linux/kernel.h> is loaded,
+ * and <linux/sched.h> loads <linux/kernel.h>
+ */
+#define DEBUG  1
+
+#include <linux/slab.h>
+#include <linux/earlysuspend.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/uaccess.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+
+#include <asm/atomic.h>
+
+#include <mach/msm_rpcrouter.h>
+#include <mach/msm_battery.h>
+
+#define BATTERY_RPC_PROG	0x30000089
+#define BATTERY_RPC_VER_1_1	0x00010001
+#define BATTERY_RPC_VER_2_1	0x00020001
+#define BATTERY_RPC_VER_4_1     0x00040001
+#define BATTERY_RPC_VER_5_1     0x00050001
+
+#define BATTERY_RPC_CB_PROG	(BATTERY_RPC_PROG | 0x01000000)
+
+#define CHG_RPC_PROG		0x3000001a
+#define CHG_RPC_VER_1_1		0x00010001
+#define CHG_RPC_VER_1_3		0x00010003
+#define CHG_RPC_VER_2_2		0x00020002
+#define CHG_RPC_VER_3_1         0x00030001
+#define CHG_RPC_VER_4_1         0x00040001
+
+#define BATTERY_REGISTER_PROC				2
+#define BATTERY_MODIFY_CLIENT_PROC			4
+#define BATTERY_DEREGISTER_CLIENT_PROC			5
+#define BATTERY_READ_MV_PROC				12
+#define BATTERY_ENABLE_DISABLE_FILTER_PROC		14
+
+#define VBATT_FILTER			2
+
+#define BATTERY_CB_TYPE_PROC		1
+#define BATTERY_CB_ID_ALL_ACTIV		1
+#define BATTERY_CB_ID_LOW_VOL		2
+
+#define BATTERY_LOW		3200
+#define BATTERY_HIGH		4300
+
+#define ONCRPC_CHG_GET_GENERAL_STATUS_PROC	12
+#define ONCRPC_CHARGER_API_VERSIONS_PROC	0xffffffff
+
+#define BATT_RPC_TIMEOUT    5000	/* 5 sec */
+
+#define INVALID_BATT_HANDLE    -1
+
+#define RPC_TYPE_REQ     0
+#define RPC_TYPE_REPLY   1
+#define RPC_REQ_REPLY_COMMON_HEADER_SIZE   (3 * sizeof(uint32_t))
+
+
+#if DEBUG
+#define DBG_LIMIT(x...) do {if (printk_ratelimit()) pr_debug(x); } while (0)
+#else
+#define DBG_LIMIT(x...) do {} while (0)
+#endif
+
+enum {
+	BATTERY_REGISTRATION_SUCCESSFUL = 0,
+	BATTERY_DEREGISTRATION_SUCCESSFUL = BATTERY_REGISTRATION_SUCCESSFUL,
+	BATTERY_MODIFICATION_SUCCESSFUL = BATTERY_REGISTRATION_SUCCESSFUL,
+	BATTERY_INTERROGATION_SUCCESSFUL = BATTERY_REGISTRATION_SUCCESSFUL,
+	BATTERY_CLIENT_TABLE_FULL = 1,
+	BATTERY_REG_PARAMS_WRONG = 2,
+	BATTERY_DEREGISTRATION_FAILED = 4,
+	BATTERY_MODIFICATION_FAILED = 8,
+	BATTERY_INTERROGATION_FAILED = 16,
+	/* Client's filter could not be set because perhaps it does not exist */
+	BATTERY_SET_FILTER_FAILED         = 32,
+	/* Client's could not be found for enabling or disabling the individual
+	 * client */
+	BATTERY_ENABLE_DISABLE_INDIVIDUAL_CLIENT_FAILED  = 64,
+	BATTERY_LAST_ERROR = 128,
+};
+
+enum {
+	BATTERY_VOLTAGE_UP = 0,
+	BATTERY_VOLTAGE_DOWN,
+	BATTERY_VOLTAGE_ABOVE_THIS_LEVEL,
+	BATTERY_VOLTAGE_BELOW_THIS_LEVEL,
+	BATTERY_VOLTAGE_LEVEL,
+	BATTERY_ALL_ACTIVITY,
+	VBATT_CHG_EVENTS,
+	BATTERY_VOLTAGE_UNKNOWN,
+};
+
+/*
+ * This enum contains defintions of the charger hardware status
+ */
+enum chg_charger_status_type {
+	/* The charger is good      */
+	CHARGER_STATUS_GOOD,
+	/* The charger is bad       */
+	CHARGER_STATUS_BAD,
+	/* The charger is weak      */
+	CHARGER_STATUS_WEAK,
+	/* Invalid charger status.  */
+	CHARGER_STATUS_INVALID
+};
+
+/*
+ *This enum contains defintions of the charger hardware type
+ */
+enum chg_charger_hardware_type {
+	/* The charger is removed                 */
+	CHARGER_TYPE_NONE,
+	/* The charger is a regular wall charger   */
+	CHARGER_TYPE_WALL,
+	/* The charger is a PC USB                 */
+	CHARGER_TYPE_USB_PC,
+	/* The charger is a wall USB charger       */
+	CHARGER_TYPE_USB_WALL,
+	/* The charger is a USB carkit             */
+	CHARGER_TYPE_USB_CARKIT,
+	/* Invalid charger hardware status.        */
+	CHARGER_TYPE_INVALID
+};
+
+/*
+ *  This enum contains defintions of the battery status
+ */
+enum chg_battery_status_type {
+	/* The battery is good        */
+	BATTERY_STATUS_GOOD,
+	/* The battery is cold/hot    */
+	BATTERY_STATUS_BAD_TEMP,
+	/* The battery is bad         */
+	BATTERY_STATUS_BAD,
+	/* The battery is removed     */
+	BATTERY_STATUS_REMOVED,		/* on v2.2 only */
+	BATTERY_STATUS_INVALID_v1 = BATTERY_STATUS_REMOVED,
+	/* Invalid battery status.    */
+	BATTERY_STATUS_INVALID
+};
+
+/*
+ *This enum contains defintions of the battery voltage level
+ */
+enum chg_battery_level_type {
+	/* The battery voltage is dead/very low (less than 3.2V) */
+	BATTERY_LEVEL_DEAD,
+	/* The battery voltage is weak/low (between 3.2V and 3.4V) */
+	BATTERY_LEVEL_WEAK,
+	/* The battery voltage is good/normal(between 3.4V and 4.2V) */
+	BATTERY_LEVEL_GOOD,
+	/* The battery voltage is up to full (close to 4.2V) */
+	BATTERY_LEVEL_FULL,
+	/* Invalid battery voltage level. */
+	BATTERY_LEVEL_INVALID
+};
+
+#ifndef CONFIG_BATTERY_MSM_FAKE
+struct rpc_reply_batt_chg_v1 {
+	struct rpc_reply_hdr hdr;
+	u32 	more_data;
+
+	u32	charger_status;
+	u32	charger_type;
+	u32	battery_status;
+	u32	battery_level;
+	u32     battery_voltage;
+	u32	battery_temp;
+};
+
+struct rpc_reply_batt_chg_v2 {
+	struct rpc_reply_batt_chg_v1	v1;
+
+	u32	is_charger_valid;
+	u32	is_charging;
+	u32	is_battery_valid;
+	u32	ui_event;
+};
+
+union rpc_reply_batt_chg {
+	struct rpc_reply_batt_chg_v1	v1;
+	struct rpc_reply_batt_chg_v2	v2;
+};
+
+static union rpc_reply_batt_chg rep_batt_chg;
+#endif
+
+struct msm_battery_info {
+	u32 voltage_max_design;
+	u32 voltage_min_design;
+	u32 chg_api_version;
+	u32 batt_technology;
+	u32 batt_api_version;
+
+	u32 avail_chg_sources;
+	u32 current_chg_source;
+
+	u32 batt_status;
+	u32 batt_health;
+	u32 charger_valid;
+	u32 batt_valid;
+	u32 batt_capacity; /* in percentage */
+
+	u32 charger_status;
+	u32 charger_type;
+	u32 battery_status;
+	u32 battery_level;
+	u32 battery_voltage; /* in millie volts */
+	u32 battery_temp;  /* in celsius */
+
+	u32(*calculate_capacity) (u32 voltage);
+
+	s32 batt_handle;
+
+	struct power_supply *msm_psy_ac;
+	struct power_supply *msm_psy_usb;
+	struct power_supply *msm_psy_batt;
+	struct power_supply *current_ps;
+
+	struct msm_rpc_client *batt_client;
+	struct msm_rpc_endpoint *chg_ep;
+
+	wait_queue_head_t wait_q;
+
+	u32 vbatt_modify_reply_avail;
+
+	struct early_suspend early_suspend;
+};
+
+static struct msm_battery_info msm_batt_info = {
+	.batt_handle = INVALID_BATT_HANDLE,
+	.charger_status = CHARGER_STATUS_BAD,
+	.charger_type = CHARGER_TYPE_INVALID,
+	.battery_status = BATTERY_STATUS_GOOD,
+	.battery_level = BATTERY_LEVEL_FULL,
+	.battery_voltage = BATTERY_HIGH,
+	.batt_capacity = 100,
+	.batt_status = POWER_SUPPLY_STATUS_DISCHARGING,
+	.batt_health = POWER_SUPPLY_HEALTH_GOOD,
+	.batt_valid  = 1,
+	.battery_temp = 23,
+	.vbatt_modify_reply_avail = 0,
+};
+
+static enum power_supply_property msm_power_props[] = {
+	POWER_SUPPLY_PROP_ONLINE,
+};
+
+static char *msm_power_supplied_to[] = {
+	"battery",
+};
+
+static int msm_power_get_property(struct power_supply *psy,
+				  enum power_supply_property psp,
+				  union power_supply_propval *val)
+{
+	switch (psp) {
+	case POWER_SUPPLY_PROP_ONLINE:
+		if (psy->type == POWER_SUPPLY_TYPE_MAINS) {
+			val->intval = msm_batt_info.current_chg_source & AC_CHG
+			    ? 1 : 0;
+		}
+		if (psy->type == POWER_SUPPLY_TYPE_USB) {
+			val->intval = msm_batt_info.current_chg_source & USB_CHG
+			    ? 1 : 0;
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static struct power_supply msm_psy_ac = {
+	.name = "ac",
+	.type = POWER_SUPPLY_TYPE_MAINS,
+	.supplied_to = msm_power_supplied_to,
+	.num_supplicants = ARRAY_SIZE(msm_power_supplied_to),
+	.properties = msm_power_props,
+	.num_properties = ARRAY_SIZE(msm_power_props),
+	.get_property = msm_power_get_property,
+};
+
+static struct power_supply msm_psy_usb = {
+	.name = "usb",
+	.type = POWER_SUPPLY_TYPE_USB,
+	.supplied_to = msm_power_supplied_to,
+	.num_supplicants = ARRAY_SIZE(msm_power_supplied_to),
+	.properties = msm_power_props,
+	.num_properties = ARRAY_SIZE(msm_power_props),
+	.get_property = msm_power_get_property,
+};
+
+static enum power_supply_property msm_batt_power_props[] = {
+	POWER_SUPPLY_PROP_STATUS,
+	POWER_SUPPLY_PROP_HEALTH,
+	POWER_SUPPLY_PROP_PRESENT,
+	POWER_SUPPLY_PROP_TECHNOLOGY,
+	POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+	POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+	POWER_SUPPLY_PROP_VOLTAGE_NOW,
+	POWER_SUPPLY_PROP_CAPACITY,
+};
+
+static int msm_batt_power_get_property(struct power_supply *psy,
+				       enum power_supply_property psp,
+				       union power_supply_propval *val)
+{
+	switch (psp) {
+	case POWER_SUPPLY_PROP_STATUS:
+		val->intval = msm_batt_info.batt_status;
+		break;
+	case POWER_SUPPLY_PROP_HEALTH:
+		val->intval = msm_batt_info.batt_health;
+		break;
+	case POWER_SUPPLY_PROP_PRESENT:
+		val->intval = msm_batt_info.batt_valid;
+		break;
+	case POWER_SUPPLY_PROP_TECHNOLOGY:
+		val->intval = msm_batt_info.batt_technology;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+		val->intval = msm_batt_info.voltage_max_design;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
+		val->intval = msm_batt_info.voltage_min_design;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+		val->intval = msm_batt_info.battery_voltage;
+		break;
+	case POWER_SUPPLY_PROP_CAPACITY:
+		val->intval = msm_batt_info.batt_capacity;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static struct power_supply msm_psy_batt = {
+	.name = "battery",
+	.type = POWER_SUPPLY_TYPE_BATTERY,
+	.properties = msm_batt_power_props,
+	.num_properties = ARRAY_SIZE(msm_batt_power_props),
+	.get_property = msm_batt_power_get_property,
+};
+
+#ifndef CONFIG_BATTERY_MSM_FAKE
+struct msm_batt_get_volt_ret_data {
+	u32 battery_voltage;
+};
+
+static int msm_batt_get_volt_ret_func(struct msm_rpc_client *batt_client,
+				       void *buf, void *data)
+{
+	struct msm_batt_get_volt_ret_data *data_ptr, *buf_ptr;
+
+	data_ptr = (struct msm_batt_get_volt_ret_data *)data;
+	buf_ptr = (struct msm_batt_get_volt_ret_data *)buf;
+
+	data_ptr->battery_voltage = be32_to_cpu(buf_ptr->battery_voltage);
+
+	return 0;
+}
+
+static u32 msm_batt_get_vbatt_voltage(void)
+{
+	int rc;
+
+	struct msm_batt_get_volt_ret_data rep;
+
+	rc = msm_rpc_client_req(msm_batt_info.batt_client,
+			BATTERY_READ_MV_PROC,
+			NULL, NULL,
+			msm_batt_get_volt_ret_func, &rep,
+			msecs_to_jiffies(BATT_RPC_TIMEOUT));
+
+	if (rc < 0) {
+		pr_err("%s: FAIL: vbatt get volt. rc=%d\n", __func__, rc);
+		return 0;
+	}
+
+	return rep.battery_voltage;
+}
+
+#define	be32_to_cpu_self(v)	(v = be32_to_cpu(v))
+
+static int msm_batt_get_batt_chg_status(void)
+{
+	int rc;
+
+	struct rpc_req_batt_chg {
+		struct rpc_request_hdr hdr;
+		u32 more_data;
+	} req_batt_chg;
+	struct rpc_reply_batt_chg_v1 *v1p;
+
+	req_batt_chg.more_data = cpu_to_be32(1);
+
+	memset(&rep_batt_chg, 0, sizeof(rep_batt_chg));
+
+	v1p = &rep_batt_chg.v1;
+	rc = msm_rpc_call_reply(msm_batt_info.chg_ep,
+				ONCRPC_CHG_GET_GENERAL_STATUS_PROC,
+				&req_batt_chg, sizeof(req_batt_chg),
+				&rep_batt_chg, sizeof(rep_batt_chg),
+				msecs_to_jiffies(BATT_RPC_TIMEOUT));
+	if (rc < 0) {
+		pr_err("%s: ERROR. msm_rpc_call_reply failed! proc=%d rc=%d\n",
+		       __func__, ONCRPC_CHG_GET_GENERAL_STATUS_PROC, rc);
+		return rc;
+	} else if (be32_to_cpu(v1p->more_data)) {
+		be32_to_cpu_self(v1p->charger_status);
+		be32_to_cpu_self(v1p->charger_type);
+		be32_to_cpu_self(v1p->battery_status);
+		be32_to_cpu_self(v1p->battery_level);
+		be32_to_cpu_self(v1p->battery_voltage);
+		be32_to_cpu_self(v1p->battery_temp);
+	} else {
+		pr_err("%s: No battery/charger data in RPC reply\n", __func__);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static void msm_batt_update_psy_status(void)
+{
+	static u32 unnecessary_event_count;
+	u32	charger_status;
+	u32	charger_type;
+	u32	battery_status;
+	u32	battery_level;
+	u32     battery_voltage;
+	u32	battery_temp;
+	struct	power_supply	*supp;
+
+	if (msm_batt_get_batt_chg_status())
+		return;
+
+	charger_status = rep_batt_chg.v1.charger_status;
+	charger_type = rep_batt_chg.v1.charger_type;
+	battery_status = rep_batt_chg.v1.battery_status;
+	battery_level = rep_batt_chg.v1.battery_level;
+	battery_voltage = rep_batt_chg.v1.battery_voltage;
+	battery_temp = rep_batt_chg.v1.battery_temp;
+
+	/* Make correction for battery status */
+	if (battery_status == BATTERY_STATUS_INVALID_v1) {
+		if (msm_batt_info.chg_api_version < CHG_RPC_VER_3_1)
+			battery_status = BATTERY_STATUS_INVALID;
+	}
+
+	if (charger_status == msm_batt_info.charger_status &&
+	    charger_type == msm_batt_info.charger_type &&
+	    battery_status == msm_batt_info.battery_status &&
+	    battery_level == msm_batt_info.battery_level &&
+	    battery_voltage == msm_batt_info.battery_voltage &&
+	    battery_temp == msm_batt_info.battery_temp) {
+		/* Got unnecessary event from Modem PMIC VBATT driver.
+		 * Nothing changed in Battery or charger status.
+		 */
+		unnecessary_event_count++;
+		if ((unnecessary_event_count % 20) == 1)
+			DBG_LIMIT("BATT: same event count = %u\n",
+				 unnecessary_event_count);
+		return;
+	}
+
+	unnecessary_event_count = 0;
+
+	DBG_LIMIT("BATT: rcvd: %d, %d, %d, %d; %d, %d\n",
+		 charger_status, charger_type, battery_status,
+		 battery_level, battery_voltage, battery_temp);
+
+	if (battery_status == BATTERY_STATUS_INVALID &&
+	    battery_level != BATTERY_LEVEL_INVALID) {
+		DBG_LIMIT("BATT: change status(%d) to (%d) for level=%d\n",
+			 battery_status, BATTERY_STATUS_GOOD, battery_level);
+		battery_status = BATTERY_STATUS_GOOD;
+	}
+
+	if (msm_batt_info.charger_type != charger_type) {
+		if (charger_type == CHARGER_TYPE_USB_WALL ||
+		    charger_type == CHARGER_TYPE_USB_PC ||
+		    charger_type == CHARGER_TYPE_USB_CARKIT) {
+			DBG_LIMIT("BATT: USB charger plugged in\n");
+			msm_batt_info.current_chg_source = USB_CHG;
+			supp = &msm_psy_usb;
+		} else if (charger_type == CHARGER_TYPE_WALL) {
+			DBG_LIMIT("BATT: AC Wall changer plugged in\n");
+			msm_batt_info.current_chg_source = AC_CHG;
+			supp = &msm_psy_ac;
+		} else {
+			if (msm_batt_info.current_chg_source & AC_CHG)
+				DBG_LIMIT("BATT: AC Wall charger removed\n");
+			else if (msm_batt_info.current_chg_source & USB_CHG)
+				DBG_LIMIT("BATT: USB charger removed\n");
+			else
+				DBG_LIMIT("BATT: No charger present\n");
+			msm_batt_info.current_chg_source = 0;
+			supp = &msm_psy_batt;
+
+			/* Correct charger status */
+			if (charger_status != CHARGER_STATUS_INVALID) {
+				DBG_LIMIT("BATT: No charging!\n");
+				charger_status = CHARGER_STATUS_INVALID;
+				msm_batt_info.batt_status =
+					POWER_SUPPLY_STATUS_NOT_CHARGING;
+			}
+		}
+	} else
+		supp = NULL;
+
+	if (msm_batt_info.charger_status != charger_status) {
+		if (charger_status == CHARGER_STATUS_GOOD ||
+		    charger_status == CHARGER_STATUS_WEAK) {
+			if (msm_batt_info.current_chg_source) {
+				DBG_LIMIT("BATT: Charging.\n");
+				msm_batt_info.batt_status =
+					POWER_SUPPLY_STATUS_CHARGING;
+
+				/* Correct when supp==NULL */
+				if (msm_batt_info.current_chg_source & AC_CHG)
+					supp = &msm_psy_ac;
+				else
+					supp = &msm_psy_usb;
+			}
+		} else {
+			DBG_LIMIT("BATT: No charging.\n");
+			msm_batt_info.batt_status =
+				POWER_SUPPLY_STATUS_NOT_CHARGING;
+			supp = &msm_psy_batt;
+		}
+	} else {
+		/* Correct charger status */
+		if (charger_type != CHARGER_TYPE_INVALID &&
+		    charger_status == CHARGER_STATUS_GOOD) {
+			DBG_LIMIT("BATT: In charging\n");
+			msm_batt_info.batt_status =
+				POWER_SUPPLY_STATUS_CHARGING;
+		}
+	}
+
+	/* Correct battery voltage and status */
+	if (!battery_voltage) {
+		if (charger_status == CHARGER_STATUS_INVALID) {
+			DBG_LIMIT("BATT: Read VBATT\n");
+			battery_voltage = msm_batt_get_vbatt_voltage();
+		} else
+			/* Use previous */
+			battery_voltage = msm_batt_info.battery_voltage;
+	}
+	if (battery_status == BATTERY_STATUS_INVALID) {
+		if (battery_voltage >= msm_batt_info.voltage_min_design &&
+		    battery_voltage <= msm_batt_info.voltage_max_design) {
+			DBG_LIMIT("BATT: Battery valid\n");
+			msm_batt_info.batt_valid = 1;
+			battery_status = BATTERY_STATUS_GOOD;
+		}
+	}
+
+	if (msm_batt_info.battery_status != battery_status) {
+		if (battery_status != BATTERY_STATUS_INVALID) {
+			msm_batt_info.batt_valid = 1;
+
+			if (battery_status == BATTERY_STATUS_BAD) {
+				DBG_LIMIT("BATT: Battery bad.\n");
+				msm_batt_info.batt_health =
+					POWER_SUPPLY_HEALTH_DEAD;
+			} else if (battery_status == BATTERY_STATUS_BAD_TEMP) {
+				DBG_LIMIT("BATT: Battery overheat.\n");
+				msm_batt_info.batt_health =
+					POWER_SUPPLY_HEALTH_OVERHEAT;
+			} else {
+				DBG_LIMIT("BATT: Battery good.\n");
+				msm_batt_info.batt_health =
+					POWER_SUPPLY_HEALTH_GOOD;
+			}
+		} else {
+			msm_batt_info.batt_valid = 0;
+			DBG_LIMIT("BATT: Battery invalid.\n");
+			msm_batt_info.batt_health = POWER_SUPPLY_HEALTH_UNKNOWN;
+		}
+
+		if (msm_batt_info.batt_status != POWER_SUPPLY_STATUS_CHARGING) {
+			if (battery_status == BATTERY_STATUS_INVALID) {
+				DBG_LIMIT("BATT: Battery -> unknown\n");
+				msm_batt_info.batt_status =
+					POWER_SUPPLY_STATUS_UNKNOWN;
+			} else {
+				DBG_LIMIT("BATT: Battery -> discharging\n");
+				msm_batt_info.batt_status =
+					POWER_SUPPLY_STATUS_DISCHARGING;
+			}
+		}
+
+		if (!supp) {
+			if (msm_batt_info.current_chg_source) {
+				if (msm_batt_info.current_chg_source & AC_CHG)
+					supp = &msm_psy_ac;
+				else
+					supp = &msm_psy_usb;
+			} else
+				supp = &msm_psy_batt;
+		}
+	}
+
+	msm_batt_info.charger_status 	= charger_status;
+	msm_batt_info.charger_type 	= charger_type;
+	msm_batt_info.battery_status 	= battery_status;
+	msm_batt_info.battery_level 	= battery_level;
+	msm_batt_info.battery_temp 	= battery_temp;
+
+	if (msm_batt_info.battery_voltage != battery_voltage) {
+		msm_batt_info.battery_voltage  	= battery_voltage;
+		msm_batt_info.batt_capacity =
+			msm_batt_info.calculate_capacity(battery_voltage);
+		DBG_LIMIT("BATT: voltage = %u mV [capacity = %d%%]\n",
+			 battery_voltage, msm_batt_info.batt_capacity);
+
+		if (!supp)
+			supp = msm_batt_info.current_ps;
+	}
+
+	if (supp) {
+		msm_batt_info.current_ps = supp;
+		DBG_LIMIT("BATT: Supply = %s\n", supp->name);
+		power_supply_changed(supp);
+	}
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+struct batt_modify_client_req {
+
+	u32 client_handle;
+
+	/* The voltage at which callback (CB) should be called. */
+	u32 desired_batt_voltage;
+
+	/* The direction when the CB should be called. */
+	u32 voltage_direction;
+
+	/* The registered callback to be called when voltage and
+	 * direction specs are met. */
+	u32 batt_cb_id;
+
+	/* The call back data */
+	u32 cb_data;
+};
+
+struct batt_modify_client_rep {
+	u32 result;
+};
+
+static int msm_batt_modify_client_arg_func(struct msm_rpc_client *batt_client,
+				       void *buf, void *data)
+{
+	struct batt_modify_client_req *batt_modify_client_req =
+		(struct batt_modify_client_req *)data;
+	u32 *req = (u32 *)buf;
+	int size = 0;
+
+	*req = cpu_to_be32(batt_modify_client_req->client_handle);
+	size += sizeof(u32);
+	req++;
+
+	*req = cpu_to_be32(batt_modify_client_req->desired_batt_voltage);
+	size += sizeof(u32);
+	req++;
+
+	*req = cpu_to_be32(batt_modify_client_req->voltage_direction);
+	size += sizeof(u32);
+	req++;
+
+	*req = cpu_to_be32(batt_modify_client_req->batt_cb_id);
+	size += sizeof(u32);
+	req++;
+
+	*req = cpu_to_be32(batt_modify_client_req->cb_data);
+	size += sizeof(u32);
+
+	return size;
+}
+
+static int msm_batt_modify_client_ret_func(struct msm_rpc_client *batt_client,
+				       void *buf, void *data)
+{
+	struct  batt_modify_client_rep *data_ptr, *buf_ptr;
+
+	data_ptr = (struct batt_modify_client_rep *)data;
+	buf_ptr = (struct batt_modify_client_rep *)buf;
+
+	data_ptr->result = be32_to_cpu(buf_ptr->result);
+
+	return 0;
+}
+
+static int msm_batt_modify_client(u32 client_handle, u32 desired_batt_voltage,
+	     u32 voltage_direction, u32 batt_cb_id, u32 cb_data)
+{
+	int rc;
+
+	struct batt_modify_client_req  req;
+	struct batt_modify_client_rep rep;
+
+	req.client_handle = client_handle;
+	req.desired_batt_voltage = desired_batt_voltage;
+	req.voltage_direction = voltage_direction;
+	req.batt_cb_id = batt_cb_id;
+	req.cb_data = cb_data;
+
+	rc = msm_rpc_client_req(msm_batt_info.batt_client,
+			BATTERY_MODIFY_CLIENT_PROC,
+			msm_batt_modify_client_arg_func, &req,
+			msm_batt_modify_client_ret_func, &rep,
+			msecs_to_jiffies(BATT_RPC_TIMEOUT));
+
+	if (rc < 0) {
+		pr_err("%s: ERROR. failed to modify  Vbatt client\n",
+		       __func__);
+		return rc;
+	}
+
+	if (rep.result != BATTERY_MODIFICATION_SUCCESSFUL) {
+		pr_err("%s: ERROR. modify client failed. result = %u\n",
+		       __func__, rep.result);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+void msm_batt_early_suspend(struct early_suspend *h)
+{
+	int rc;
+
+	pr_debug("%s: enter\n", __func__);
+
+	if (msm_batt_info.batt_handle != INVALID_BATT_HANDLE) {
+		rc = msm_batt_modify_client(msm_batt_info.batt_handle,
+				BATTERY_LOW, BATTERY_VOLTAGE_BELOW_THIS_LEVEL,
+				BATTERY_CB_ID_LOW_VOL, BATTERY_LOW);
+
+		if (rc < 0) {
+			pr_err("%s: msm_batt_modify_client. rc=%d\n",
+			       __func__, rc);
+			return;
+		}
+	} else {
+		pr_err("%s: ERROR. invalid batt_handle\n", __func__);
+		return;
+	}
+
+	pr_debug("%s: exit\n", __func__);
+}
+
+void msm_batt_late_resume(struct early_suspend *h)
+{
+	int rc;
+
+	pr_debug("%s: enter\n", __func__);
+
+	if (msm_batt_info.batt_handle != INVALID_BATT_HANDLE) {
+		rc = msm_batt_modify_client(msm_batt_info.batt_handle,
+				BATTERY_LOW, BATTERY_ALL_ACTIVITY,
+			       BATTERY_CB_ID_ALL_ACTIV, BATTERY_ALL_ACTIVITY);
+		if (rc < 0) {
+			pr_err("%s: msm_batt_modify_client FAIL rc=%d\n",
+			       __func__, rc);
+			return;
+		}
+	} else {
+		pr_err("%s: ERROR. invalid batt_handle\n", __func__);
+		return;
+	}
+
+	msm_batt_update_psy_status();
+	pr_debug("%s: exit\n", __func__);
+}
+#endif
+
+struct msm_batt_vbatt_filter_req {
+	u32 batt_handle;
+	u32 enable_filter;
+	u32 vbatt_filter;
+};
+
+struct msm_batt_vbatt_filter_rep {
+	u32 result;
+};
+
+static int msm_batt_filter_arg_func(struct msm_rpc_client *batt_client,
+
+		void *buf, void *data)
+{
+	struct msm_batt_vbatt_filter_req *vbatt_filter_req =
+		(struct msm_batt_vbatt_filter_req *)data;
+	u32 *req = (u32 *)buf;
+	int size = 0;
+
+	*req = cpu_to_be32(vbatt_filter_req->batt_handle);
+	size += sizeof(u32);
+	req++;
+
+	*req = cpu_to_be32(vbatt_filter_req->enable_filter);
+	size += sizeof(u32);
+	req++;
+
+	*req = cpu_to_be32(vbatt_filter_req->vbatt_filter);
+	size += sizeof(u32);
+	return size;
+}
+
+static int msm_batt_filter_ret_func(struct msm_rpc_client *batt_client,
+				       void *buf, void *data)
+{
+
+	struct msm_batt_vbatt_filter_rep *data_ptr, *buf_ptr;
+
+	data_ptr = (struct msm_batt_vbatt_filter_rep *)data;
+	buf_ptr = (struct msm_batt_vbatt_filter_rep *)buf;
+
+	data_ptr->result = be32_to_cpu(buf_ptr->result);
+	return 0;
+}
+
+static int msm_batt_enable_filter(u32 vbatt_filter)
+{
+	int rc;
+	struct  msm_batt_vbatt_filter_req  vbatt_filter_req;
+	struct  msm_batt_vbatt_filter_rep  vbatt_filter_rep;
+
+	vbatt_filter_req.batt_handle = msm_batt_info.batt_handle;
+	vbatt_filter_req.enable_filter = 1;
+	vbatt_filter_req.vbatt_filter = vbatt_filter;
+
+	rc = msm_rpc_client_req(msm_batt_info.batt_client,
+			BATTERY_ENABLE_DISABLE_FILTER_PROC,
+			msm_batt_filter_arg_func, &vbatt_filter_req,
+			msm_batt_filter_ret_func, &vbatt_filter_rep,
+			msecs_to_jiffies(BATT_RPC_TIMEOUT));
+
+	if (rc < 0) {
+		pr_err("%s: FAIL: enable vbatt filter. rc=%d\n",
+		       __func__, rc);
+		return rc;
+	}
+
+	if (vbatt_filter_rep.result != BATTERY_DEREGISTRATION_SUCCESSFUL) {
+		pr_err("%s: FAIL: enable vbatt filter: result=%d\n",
+		       __func__, vbatt_filter_rep.result);
+		return -EIO;
+	}
+
+	pr_debug("%s: enable vbatt filter: OK\n", __func__);
+	return rc;
+}
+
+struct batt_client_registration_req {
+	/* The voltage at which callback (CB) should be called. */
+	u32 desired_batt_voltage;
+
+	/* The direction when the CB should be called. */
+	u32 voltage_direction;
+
+	/* The registered callback to be called when voltage and
+	 * direction specs are met. */
+	u32 batt_cb_id;
+
+	/* The call back data */
+	u32 cb_data;
+	u32 more_data;
+	u32 batt_error;
+};
+
+struct batt_client_registration_req_4_1 {
+	/* The voltage at which callback (CB) should be called. */
+	u32 desired_batt_voltage;
+
+	/* The direction when the CB should be called. */
+	u32 voltage_direction;
+
+	/* The registered callback to be called when voltage and
+	 * direction specs are met. */
+	u32 batt_cb_id;
+
+	/* The call back data */
+	u32 cb_data;
+	u32 batt_error;
+};
+
+struct batt_client_registration_rep {
+	u32 batt_handle;
+};
+
+struct batt_client_registration_rep_4_1 {
+	u32 batt_handle;
+	u32 more_data;
+	u32 err;
+};
+
+static int msm_batt_register_arg_func(struct msm_rpc_client *batt_client,
+				       void *buf, void *data)
+{
+	struct batt_client_registration_req *batt_reg_req =
+		(struct batt_client_registration_req *)data;
+
+	u32 *req = (u32 *)buf;
+	int size = 0;
+
+
+	if (msm_batt_info.batt_api_version == BATTERY_RPC_VER_4_1) {
+		*req = cpu_to_be32(batt_reg_req->desired_batt_voltage);
+		size += sizeof(u32);
+		req++;
+
+		*req = cpu_to_be32(batt_reg_req->voltage_direction);
+		size += sizeof(u32);
+		req++;
+
+		*req = cpu_to_be32(batt_reg_req->batt_cb_id);
+		size += sizeof(u32);
+		req++;
+
+		*req = cpu_to_be32(batt_reg_req->cb_data);
+		size += sizeof(u32);
+		req++;
+
+		*req = cpu_to_be32(batt_reg_req->batt_error);
+		size += sizeof(u32);
+
+		return size;
+	} else {
+		*req = cpu_to_be32(batt_reg_req->desired_batt_voltage);
+		size += sizeof(u32);
+		req++;
+
+		*req = cpu_to_be32(batt_reg_req->voltage_direction);
+		size += sizeof(u32);
+		req++;
+
+		*req = cpu_to_be32(batt_reg_req->batt_cb_id);
+		size += sizeof(u32);
+		req++;
+
+		*req = cpu_to_be32(batt_reg_req->cb_data);
+		size += sizeof(u32);
+		req++;
+
+		*req = cpu_to_be32(batt_reg_req->more_data);
+		size += sizeof(u32);
+		req++;
+
+		*req = cpu_to_be32(batt_reg_req->batt_error);
+		size += sizeof(u32);
+
+		return size;
+	}
+
+}
+
+static int msm_batt_register_ret_func(struct msm_rpc_client *batt_client,
+				       void *buf, void *data)
+{
+	struct batt_client_registration_rep *data_ptr, *buf_ptr;
+	struct batt_client_registration_rep_4_1 *data_ptr_4_1, *buf_ptr_4_1;
+
+	if (msm_batt_info.batt_api_version == BATTERY_RPC_VER_4_1) {
+		data_ptr_4_1 = (struct batt_client_registration_rep_4_1 *)data;
+		buf_ptr_4_1 = (struct batt_client_registration_rep_4_1 *)buf;
+
+		data_ptr_4_1->batt_handle
+			= be32_to_cpu(buf_ptr_4_1->batt_handle);
+		data_ptr_4_1->more_data
+			= be32_to_cpu(buf_ptr_4_1->more_data);
+		data_ptr_4_1->err = be32_to_cpu(buf_ptr_4_1->err);
+		return 0;
+	} else {
+		data_ptr = (struct batt_client_registration_rep *)data;
+		buf_ptr = (struct batt_client_registration_rep *)buf;
+
+		data_ptr->batt_handle = be32_to_cpu(buf_ptr->batt_handle);
+		return 0;
+	}
+}
+
+static int msm_batt_register(u32 desired_batt_voltage,
+			     u32 voltage_direction, u32 batt_cb_id, u32 cb_data)
+{
+	struct batt_client_registration_req batt_reg_req;
+	struct batt_client_registration_req_4_1 batt_reg_req_4_1;
+	struct batt_client_registration_rep batt_reg_rep;
+	struct batt_client_registration_rep_4_1 batt_reg_rep_4_1;
+	void *request;
+	void *reply;
+	int rc;
+
+	if (msm_batt_info.batt_api_version == BATTERY_RPC_VER_4_1) {
+		batt_reg_req_4_1.desired_batt_voltage = desired_batt_voltage;
+		batt_reg_req_4_1.voltage_direction = voltage_direction;
+		batt_reg_req_4_1.batt_cb_id = batt_cb_id;
+		batt_reg_req_4_1.cb_data = cb_data;
+		batt_reg_req_4_1.batt_error = 1;
+		request = &batt_reg_req_4_1;
+	} else {
+		batt_reg_req.desired_batt_voltage = desired_batt_voltage;
+		batt_reg_req.voltage_direction = voltage_direction;
+		batt_reg_req.batt_cb_id = batt_cb_id;
+		batt_reg_req.cb_data = cb_data;
+		batt_reg_req.more_data = 1;
+		batt_reg_req.batt_error = 0;
+		request = &batt_reg_req;
+	}
+
+	if (msm_batt_info.batt_api_version == BATTERY_RPC_VER_4_1)
+		reply = &batt_reg_rep_4_1;
+	else
+		reply = &batt_reg_rep;
+
+	rc = msm_rpc_client_req(msm_batt_info.batt_client,
+			BATTERY_REGISTER_PROC,
+			msm_batt_register_arg_func, request,
+			msm_batt_register_ret_func, reply,
+			msecs_to_jiffies(BATT_RPC_TIMEOUT));
+
+	if (rc < 0) {
+		pr_err("%s: FAIL: vbatt register. rc=%d\n", __func__, rc);
+		return rc;
+	}
+
+	if (msm_batt_info.batt_api_version == BATTERY_RPC_VER_4_1) {
+		if (batt_reg_rep_4_1.more_data != 0
+			&& batt_reg_rep_4_1.err
+				!= BATTERY_REGISTRATION_SUCCESSFUL) {
+			pr_err("%s: vBatt Registration Failed proc_num=%d\n"
+					, __func__, BATTERY_REGISTER_PROC);
+			return -EIO;
+		}
+		msm_batt_info.batt_handle = batt_reg_rep_4_1.batt_handle;
+	} else
+		msm_batt_info.batt_handle = batt_reg_rep.batt_handle;
+
+	return 0;
+}
+
+struct batt_client_deregister_req {
+	u32 batt_handle;
+};
+
+struct batt_client_deregister_rep {
+	u32 batt_error;
+};
+
+static int msm_batt_deregister_arg_func(struct msm_rpc_client *batt_client,
+				       void *buf, void *data)
+{
+	struct batt_client_deregister_req *deregister_req =
+		(struct  batt_client_deregister_req *)data;
+	u32 *req = (u32 *)buf;
+	int size = 0;
+
+	*req = cpu_to_be32(deregister_req->batt_handle);
+	size += sizeof(u32);
+
+	return size;
+}
+
+static int msm_batt_deregister_ret_func(struct msm_rpc_client *batt_client,
+				       void *buf, void *data)
+{
+	struct batt_client_deregister_rep *data_ptr, *buf_ptr;
+
+	data_ptr = (struct batt_client_deregister_rep *)data;
+	buf_ptr = (struct batt_client_deregister_rep *)buf;
+
+	data_ptr->batt_error = be32_to_cpu(buf_ptr->batt_error);
+
+	return 0;
+}
+
+static int msm_batt_deregister(u32 batt_handle)
+{
+	int rc;
+	struct batt_client_deregister_req req;
+	struct batt_client_deregister_rep rep;
+
+	req.batt_handle = batt_handle;
+
+	rc = msm_rpc_client_req(msm_batt_info.batt_client,
+			BATTERY_DEREGISTER_CLIENT_PROC,
+			msm_batt_deregister_arg_func, &req,
+			msm_batt_deregister_ret_func, &rep,
+			msecs_to_jiffies(BATT_RPC_TIMEOUT));
+
+	if (rc < 0) {
+		pr_err("%s: FAIL: vbatt deregister. rc=%d\n", __func__, rc);
+		return rc;
+	}
+
+	if (rep.batt_error != BATTERY_DEREGISTRATION_SUCCESSFUL) {
+		pr_err("%s: vbatt deregistration FAIL. error=%d, handle=%d\n",
+		       __func__, rep.batt_error, batt_handle);
+		return -EIO;
+	}
+
+	return 0;
+}
+#endif  /* CONFIG_BATTERY_MSM_FAKE */
+
+static int msm_batt_cleanup(void)
+{
+	int rc = 0;
+
+#ifndef CONFIG_BATTERY_MSM_FAKE
+	if (msm_batt_info.batt_handle != INVALID_BATT_HANDLE) {
+
+		rc = msm_batt_deregister(msm_batt_info.batt_handle);
+		if (rc < 0)
+			pr_err("%s: FAIL: msm_batt_deregister. rc=%d\n",
+			       __func__, rc);
+	}
+
+	msm_batt_info.batt_handle = INVALID_BATT_HANDLE;
+
+	if (msm_batt_info.batt_client)
+		msm_rpc_unregister_client(msm_batt_info.batt_client);
+#endif  /* CONFIG_BATTERY_MSM_FAKE */
+
+	if (msm_batt_info.msm_psy_ac)
+		power_supply_unregister(msm_batt_info.msm_psy_ac);
+
+	if (msm_batt_info.msm_psy_usb)
+		power_supply_unregister(msm_batt_info.msm_psy_usb);
+	if (msm_batt_info.msm_psy_batt)
+		power_supply_unregister(msm_batt_info.msm_psy_batt);
+
+#ifndef CONFIG_BATTERY_MSM_FAKE
+	if (msm_batt_info.chg_ep) {
+		rc = msm_rpc_close(msm_batt_info.chg_ep);
+		if (rc < 0) {
+			pr_err("%s: FAIL. msm_rpc_close(chg_ep). rc=%d\n",
+			       __func__, rc);
+		}
+	}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	if (msm_batt_info.early_suspend.suspend == msm_batt_early_suspend)
+		unregister_early_suspend(&msm_batt_info.early_suspend);
+#endif
+#endif
+	return rc;
+}
+
+static u32 msm_batt_capacity(u32 current_voltage)
+{
+	u32 low_voltage = msm_batt_info.voltage_min_design;
+	u32 high_voltage = msm_batt_info.voltage_max_design;
+
+	if (current_voltage <= low_voltage)
+		return 0;
+	else if (current_voltage >= high_voltage)
+		return 100;
+	else
+		return (current_voltage - low_voltage) * 100
+			/ (high_voltage - low_voltage);
+}
+
+#ifndef CONFIG_BATTERY_MSM_FAKE
+int msm_batt_get_charger_api_version(void)
+{
+	int rc ;
+	struct rpc_reply_hdr *reply;
+
+	struct rpc_req_chg_api_ver {
+		struct rpc_request_hdr hdr;
+		u32 more_data;
+	} req_chg_api_ver;
+
+	struct rpc_rep_chg_api_ver {
+		struct rpc_reply_hdr hdr;
+		u32 num_of_chg_api_versions;
+		u32 *chg_api_versions;
+	};
+
+	u32 num_of_versions;
+
+	struct rpc_rep_chg_api_ver *rep_chg_api_ver;
+
+
+	req_chg_api_ver.more_data = cpu_to_be32(1);
+
+	msm_rpc_setup_req(&req_chg_api_ver.hdr, CHG_RPC_PROG, CHG_RPC_VER_1_1,
+			  ONCRPC_CHARGER_API_VERSIONS_PROC);
+
+	rc = msm_rpc_write(msm_batt_info.chg_ep, &req_chg_api_ver,
+			sizeof(req_chg_api_ver));
+	if (rc < 0) {
+		pr_err("%s: FAIL: msm_rpc_write. proc=0x%08x, rc=%d\n",
+		       __func__, ONCRPC_CHARGER_API_VERSIONS_PROC, rc);
+		return rc;
+	}
+
+	for (;;) {
+		rc = msm_rpc_read(msm_batt_info.chg_ep, (void *) &reply, -1,
+				BATT_RPC_TIMEOUT);
+		if (rc < 0)
+			return rc;
+		if (rc < RPC_REQ_REPLY_COMMON_HEADER_SIZE) {
+			pr_err("%s: LENGTH ERR: msm_rpc_read. rc=%d (<%d)\n",
+			       __func__, rc, RPC_REQ_REPLY_COMMON_HEADER_SIZE);
+
+			rc = -EIO;
+			break;
+		}
+		/* we should not get RPC REQ or call packets -- ignore them */
+		if (reply->type == RPC_TYPE_REQ) {
+			pr_err("%s: TYPE ERR: type=%d (!=%d)\n",
+			       __func__, reply->type, RPC_TYPE_REQ);
+			kfree(reply);
+			continue;
+		}
+
+		/* If an earlier call timed out, we could get the (no
+		 * longer wanted) reply for it.	 Ignore replies that
+		 * we don't expect
+		 */
+		if (reply->xid != req_chg_api_ver.hdr.xid) {
+			pr_err("%s: XID ERR: xid=%d (!=%d)\n", __func__,
+			       reply->xid, req_chg_api_ver.hdr.xid);
+			kfree(reply);
+			continue;
+		}
+		if (reply->reply_stat != RPCMSG_REPLYSTAT_ACCEPTED) {
+			rc = -EPERM;
+			break;
+		}
+		if (reply->data.acc_hdr.accept_stat !=
+				RPC_ACCEPTSTAT_SUCCESS) {
+			rc = -EINVAL;
+			break;
+		}
+
+		rep_chg_api_ver = (struct rpc_rep_chg_api_ver *)reply;
+
+		num_of_versions =
+			be32_to_cpu(rep_chg_api_ver->num_of_chg_api_versions);
+
+		rep_chg_api_ver->chg_api_versions =  (u32 *)
+			((u8 *) reply + sizeof(struct rpc_reply_hdr) +
+			sizeof(rep_chg_api_ver->num_of_chg_api_versions));
+
+		rc = be32_to_cpu(
+			rep_chg_api_ver->chg_api_versions[num_of_versions - 1]);
+
+		pr_debug("%s: num_of_chg_api_versions = %u. "
+			"The chg api version = 0x%08x\n", __func__,
+			num_of_versions, rc);
+		break;
+	}
+	kfree(reply);
+	return rc;
+}
+
+static int msm_batt_cb_func(struct msm_rpc_client *client,
+			    void *buffer, int in_size)
+{
+	int rc = 0;
+	struct rpc_request_hdr *req;
+	u32 procedure;
+	u32 accept_status;
+
+	req = (struct rpc_request_hdr *)buffer;
+	procedure = be32_to_cpu(req->procedure);
+
+	switch (procedure) {
+	case BATTERY_CB_TYPE_PROC:
+		accept_status = RPC_ACCEPTSTAT_SUCCESS;
+		break;
+
+	default:
+		accept_status = RPC_ACCEPTSTAT_PROC_UNAVAIL;
+		pr_err("%s: ERROR. procedure (%d) not supported\n",
+		       __func__, procedure);
+		break;
+	}
+
+	msm_rpc_start_accepted_reply(msm_batt_info.batt_client,
+			be32_to_cpu(req->xid), accept_status);
+
+	rc = msm_rpc_send_accepted_reply(msm_batt_info.batt_client, 0);
+	if (rc)
+		pr_err("%s: FAIL: sending reply. rc=%d\n", __func__, rc);
+
+	if (accept_status == RPC_ACCEPTSTAT_SUCCESS)
+		msm_batt_update_psy_status();
+
+	return rc;
+}
+#endif  /* CONFIG_BATTERY_MSM_FAKE */
+
+static int __devinit msm_batt_probe(struct platform_device *pdev)
+{
+	int rc;
+	struct msm_psy_batt_pdata *pdata = pdev->dev.platform_data;
+
+	if (pdev->id != -1) {
+		dev_err(&pdev->dev,
+			"%s: MSM chipsets Can only support one"
+			" battery ", __func__);
+		return -EINVAL;
+	}
+
+#ifndef CONFIG_BATTERY_MSM_FAKE
+	if (pdata->avail_chg_sources & AC_CHG) {
+#else
+	{
+#endif
+		rc = power_supply_register(&pdev->dev, &msm_psy_ac);
+		if (rc < 0) {
+			dev_err(&pdev->dev,
+				"%s: power_supply_register failed"
+				" rc = %d\n", __func__, rc);
+			msm_batt_cleanup();
+			return rc;
+		}
+		msm_batt_info.msm_psy_ac = &msm_psy_ac;
+		msm_batt_info.avail_chg_sources |= AC_CHG;
+	}
+
+	if (pdata->avail_chg_sources & USB_CHG) {
+		rc = power_supply_register(&pdev->dev, &msm_psy_usb);
+		if (rc < 0) {
+			dev_err(&pdev->dev,
+				"%s: power_supply_register failed"
+				" rc = %d\n", __func__, rc);
+			msm_batt_cleanup();
+			return rc;
+		}
+		msm_batt_info.msm_psy_usb = &msm_psy_usb;
+		msm_batt_info.avail_chg_sources |= USB_CHG;
+	}
+
+	if (!msm_batt_info.msm_psy_ac && !msm_batt_info.msm_psy_usb) {
+
+		dev_err(&pdev->dev,
+			"%s: No external Power supply(AC or USB)"
+			"is avilable\n", __func__);
+		msm_batt_cleanup();
+		return -ENODEV;
+	}
+
+	msm_batt_info.voltage_max_design = pdata->voltage_max_design;
+	msm_batt_info.voltage_min_design = pdata->voltage_min_design;
+	msm_batt_info.batt_technology = pdata->batt_technology;
+	msm_batt_info.calculate_capacity = pdata->calculate_capacity;
+
+	if (!msm_batt_info.voltage_min_design)
+		msm_batt_info.voltage_min_design = BATTERY_LOW;
+	if (!msm_batt_info.voltage_max_design)
+		msm_batt_info.voltage_max_design = BATTERY_HIGH;
+
+	if (msm_batt_info.batt_technology == POWER_SUPPLY_TECHNOLOGY_UNKNOWN)
+		msm_batt_info.batt_technology = POWER_SUPPLY_TECHNOLOGY_LION;
+
+	if (!msm_batt_info.calculate_capacity)
+		msm_batt_info.calculate_capacity = msm_batt_capacity;
+
+	rc = power_supply_register(&pdev->dev, &msm_psy_batt);
+	if (rc < 0) {
+		dev_err(&pdev->dev, "%s: power_supply_register failed"
+			" rc=%d\n", __func__, rc);
+		msm_batt_cleanup();
+		return rc;
+	}
+	msm_batt_info.msm_psy_batt = &msm_psy_batt;
+
+#ifndef CONFIG_BATTERY_MSM_FAKE
+	rc = msm_batt_register(BATTERY_LOW, BATTERY_ALL_ACTIVITY,
+			       BATTERY_CB_ID_ALL_ACTIV, BATTERY_ALL_ACTIVITY);
+	if (rc < 0) {
+		dev_err(&pdev->dev,
+			"%s: msm_batt_register failed rc = %d\n", __func__, rc);
+		msm_batt_cleanup();
+		return rc;
+	}
+
+	rc =  msm_batt_enable_filter(VBATT_FILTER);
+
+	if (rc < 0) {
+		dev_err(&pdev->dev,
+			"%s: msm_batt_enable_filter failed rc = %d\n",
+			__func__, rc);
+		msm_batt_cleanup();
+		return rc;
+	}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	msm_batt_info.early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN;
+	msm_batt_info.early_suspend.suspend = msm_batt_early_suspend;
+	msm_batt_info.early_suspend.resume = msm_batt_late_resume;
+	register_early_suspend(&msm_batt_info.early_suspend);
+#endif
+	msm_batt_update_psy_status();
+
+#else
+	power_supply_changed(&msm_psy_ac);
+#endif  /* CONFIG_BATTERY_MSM_FAKE */
+
+	return 0;
+}
+
+static int __devexit msm_batt_remove(struct platform_device *pdev)
+{
+	int rc;
+	rc = msm_batt_cleanup();
+
+	if (rc < 0) {
+		dev_err(&pdev->dev,
+			"%s: msm_batt_cleanup  failed rc=%d\n", __func__, rc);
+		return rc;
+	}
+	return 0;
+}
+
+static struct platform_driver msm_batt_driver = {
+	.probe = msm_batt_probe,
+	.remove = __devexit_p(msm_batt_remove),
+	.driver = {
+		   .name = "msm-battery",
+		   .owner = THIS_MODULE,
+		   },
+};
+
+static int __devinit msm_batt_init_rpc(void)
+{
+	int rc;
+
+#ifdef CONFIG_BATTERY_MSM_FAKE
+	pr_info("Faking MSM battery\n");
+#else
+
+	msm_batt_info.chg_ep =
+		msm_rpc_connect_compatible(CHG_RPC_PROG, CHG_RPC_VER_4_1, 0);
+	msm_batt_info.chg_api_version =  CHG_RPC_VER_4_1;
+	if (msm_batt_info.chg_ep == NULL) {
+		pr_err("%s: rpc connect CHG_RPC_PROG = NULL\n", __func__);
+		return -ENODEV;
+	}
+
+	if (IS_ERR(msm_batt_info.chg_ep)) {
+		msm_batt_info.chg_ep = msm_rpc_connect_compatible(
+				CHG_RPC_PROG, CHG_RPC_VER_3_1, 0);
+		msm_batt_info.chg_api_version =  CHG_RPC_VER_3_1;
+	}
+	if (IS_ERR(msm_batt_info.chg_ep)) {
+		msm_batt_info.chg_ep = msm_rpc_connect_compatible(
+				CHG_RPC_PROG, CHG_RPC_VER_1_1, 0);
+		msm_batt_info.chg_api_version =  CHG_RPC_VER_1_1;
+	}
+	if (IS_ERR(msm_batt_info.chg_ep)) {
+		msm_batt_info.chg_ep = msm_rpc_connect_compatible(
+				CHG_RPC_PROG, CHG_RPC_VER_1_3, 0);
+		msm_batt_info.chg_api_version =  CHG_RPC_VER_1_3;
+	}
+	if (IS_ERR(msm_batt_info.chg_ep)) {
+		msm_batt_info.chg_ep = msm_rpc_connect_compatible(
+				CHG_RPC_PROG, CHG_RPC_VER_2_2, 0);
+		msm_batt_info.chg_api_version =  CHG_RPC_VER_2_2;
+	}
+	if (IS_ERR(msm_batt_info.chg_ep)) {
+		rc = PTR_ERR(msm_batt_info.chg_ep);
+		pr_err("%s: FAIL: rpc connect for CHG_RPC_PROG. rc=%d\n",
+		       __func__, rc);
+		msm_batt_info.chg_ep = NULL;
+		return rc;
+	}
+
+	/* Get the real 1.x version */
+	if (msm_batt_info.chg_api_version == CHG_RPC_VER_1_1)
+		msm_batt_info.chg_api_version =
+			msm_batt_get_charger_api_version();
+
+	/* Fall back to 1.1 for default */
+	if (msm_batt_info.chg_api_version < 0)
+		msm_batt_info.chg_api_version = CHG_RPC_VER_1_1;
+	msm_batt_info.batt_api_version =  BATTERY_RPC_VER_4_1;
+
+	msm_batt_info.batt_client =
+		msm_rpc_register_client("battery", BATTERY_RPC_PROG,
+					BATTERY_RPC_VER_4_1,
+					1, msm_batt_cb_func);
+
+	if (msm_batt_info.batt_client == NULL) {
+		pr_err("%s: FAIL: rpc_register_client. batt_client=NULL\n",
+		       __func__);
+		return -ENODEV;
+	}
+	if (IS_ERR(msm_batt_info.batt_client)) {
+		msm_batt_info.batt_client =
+			msm_rpc_register_client("battery", BATTERY_RPC_PROG,
+						BATTERY_RPC_VER_1_1,
+						1, msm_batt_cb_func);
+		msm_batt_info.batt_api_version =  BATTERY_RPC_VER_1_1;
+	}
+	if (IS_ERR(msm_batt_info.batt_client)) {
+		msm_batt_info.batt_client =
+			msm_rpc_register_client("battery", BATTERY_RPC_PROG,
+						BATTERY_RPC_VER_2_1,
+						1, msm_batt_cb_func);
+		msm_batt_info.batt_api_version =  BATTERY_RPC_VER_2_1;
+	}
+	if (IS_ERR(msm_batt_info.batt_client)) {
+		msm_batt_info.batt_client =
+			msm_rpc_register_client("battery", BATTERY_RPC_PROG,
+						BATTERY_RPC_VER_5_1,
+						1, msm_batt_cb_func);
+		msm_batt_info.batt_api_version =  BATTERY_RPC_VER_5_1;
+	}
+	if (IS_ERR(msm_batt_info.batt_client)) {
+		rc = PTR_ERR(msm_batt_info.batt_client);
+		pr_err("%s: ERROR: rpc_register_client: rc = %d\n ",
+		       __func__, rc);
+		msm_batt_info.batt_client = NULL;
+		return rc;
+	}
+#endif  /* CONFIG_BATTERY_MSM_FAKE */
+
+	rc = platform_driver_register(&msm_batt_driver);
+
+	if (rc < 0)
+		pr_err("%s: FAIL: platform_driver_register. rc = %d\n",
+		       __func__, rc);
+
+	return rc;
+}
+
+static int __init msm_batt_init(void)
+{
+	int rc;
+
+	pr_debug("%s: enter\n", __func__);
+
+	rc = msm_batt_init_rpc();
+
+	if (rc < 0) {
+		pr_err("%s: FAIL: msm_batt_init_rpc.  rc=%d\n", __func__, rc);
+		msm_batt_cleanup();
+		return rc;
+	}
+
+	pr_info("%s: Charger/Battery = 0x%08x/0x%08x (RPC version)\n",
+		__func__, msm_batt_info.chg_api_version,
+		msm_batt_info.batt_api_version);
+
+	return 0;
+}
+
+static void __exit msm_batt_exit(void)
+{
+	platform_driver_unregister(&msm_batt_driver);
+}
+
+module_init(msm_batt_init);
+module_exit(msm_batt_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Kiran Kandi, Qualcomm Innovation Center, Inc.");
+MODULE_DESCRIPTION("Battery driver for Qualcomm MSM chipsets.");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:msm_battery");
diff --git a/drivers/power/msm_charger.c b/drivers/power/msm_charger.c
new file mode 100644
index 0000000..f40477a
--- /dev/null
+++ b/drivers/power/msm_charger.c
@@ -0,0 +1,1250 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/mfd/pmic8058.h>
+#include <linux/interrupt.h>
+#include <linux/power_supply.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/debugfs.h>
+#include <linux/msm-charger.h>
+#include <linux/time.h>
+#include <linux/slab.h>
+#include <linux/wakelock.h>
+
+#include <asm/atomic.h>
+
+#include <mach/msm_hsusb.h>
+
+#define MSM_CHG_MAX_EVENTS		16
+#define CHARGING_TEOC_MS		9000000
+#define UPDATE_TIME_MS			60000
+#define RESUME_CHECK_PERIOD_MS		60000
+
+#define DEFAULT_BATT_MAX_V		4200
+#define DEFAULT_BATT_MIN_V		3200
+
+#define MSM_CHARGER_GAUGE_MISSING_VOLTS 3500
+#define MSM_CHARGER_GAUGE_MISSING_TEMP  35
+/**
+ * enum msm_battery_status
+ * @BATT_STATUS_ABSENT: battery not present
+ * @BATT_STATUS_ID_INVALID: battery present but the id is invalid
+ * @BATT_STATUS_DISCHARGING: battery is present and is discharging
+ * @BATT_STATUS_TRKL_CHARGING: battery is being trickle charged
+ * @BATT_STATUS_FAST_CHARGING: battery is being fast charged
+ * @BATT_STATUS_JUST_FINISHED_CHARGING: just finished charging,
+ *		battery is fully charged. Do not begin charging untill the
+ *		voltage falls below a threshold to avoid overcharging
+ * @BATT_STATUS_TEMPERATURE_OUT_OF_RANGE: battery present,
+					no charging, temp is hot/cold
+ */
+enum msm_battery_status {
+	BATT_STATUS_ABSENT,
+	BATT_STATUS_ID_INVALID,
+	BATT_STATUS_DISCHARGING,
+	BATT_STATUS_TRKL_CHARGING,
+	BATT_STATUS_FAST_CHARGING,
+	BATT_STATUS_JUST_FINISHED_CHARGING,
+	BATT_STATUS_TEMPERATURE_OUT_OF_RANGE,
+};
+
+struct msm_hardware_charger_priv {
+	struct list_head list;
+	struct msm_hardware_charger *hw_chg;
+	enum msm_hardware_charger_state hw_chg_state;
+	unsigned int max_source_current;
+	struct power_supply psy;
+};
+
+struct msm_charger_event {
+	enum msm_hardware_charger_event event;
+	struct msm_hardware_charger *hw_chg;
+};
+
+struct msm_charger_mux {
+	int inited;
+	struct list_head msm_hardware_chargers;
+	int count_chargers;
+	struct mutex msm_hardware_chargers_lock;
+
+	struct device *dev;
+
+	unsigned int max_voltage;
+	unsigned int min_voltage;
+
+	unsigned int safety_time;
+	struct delayed_work teoc_work;
+
+	unsigned int update_time;
+	int stop_update;
+	struct delayed_work update_heartbeat_work;
+
+	struct mutex status_lock;
+	enum msm_battery_status batt_status;
+	struct msm_hardware_charger_priv *current_chg_priv;
+	struct msm_hardware_charger_priv *current_mon_priv;
+
+	unsigned int (*get_batt_capacity_percent) (void);
+
+	struct msm_charger_event *queue;
+	int tail;
+	int head;
+	spinlock_t queue_lock;
+	int queue_count;
+	struct work_struct queue_work;
+	struct workqueue_struct *event_wq_thread;
+	struct wake_lock wl;
+};
+
+static struct msm_charger_mux msm_chg;
+
+static struct msm_battery_gauge *msm_batt_gauge;
+
+static int is_chg_capable_of_charging(struct msm_hardware_charger_priv *priv)
+{
+	if (priv->hw_chg_state == CHG_READY_STATE
+	    || priv->hw_chg_state == CHG_CHARGING_STATE)
+		return 1;
+
+	return 0;
+}
+
+static int is_batt_status_capable_of_charging(void)
+{
+	if (msm_chg.batt_status == BATT_STATUS_ABSENT
+	    || msm_chg.batt_status == BATT_STATUS_TEMPERATURE_OUT_OF_RANGE
+	    || msm_chg.batt_status == BATT_STATUS_ID_INVALID
+	    || msm_chg.batt_status == BATT_STATUS_JUST_FINISHED_CHARGING)
+		return 0;
+	return 1;
+}
+
+static int is_batt_status_charging(void)
+{
+	if (msm_chg.batt_status == BATT_STATUS_TRKL_CHARGING
+	    || msm_chg.batt_status == BATT_STATUS_FAST_CHARGING)
+		return 1;
+	return 0;
+}
+
+static int is_battery_present(void)
+{
+	if (msm_batt_gauge && msm_batt_gauge->is_battery_present)
+		return msm_batt_gauge->is_battery_present();
+	else {
+		pr_err("msm-charger: no batt gauge batt=absent\n");
+		return 0;
+	}
+}
+
+static int is_battery_temp_within_range(void)
+{
+	if (msm_batt_gauge && msm_batt_gauge->is_battery_temp_within_range)
+		return msm_batt_gauge->is_battery_temp_within_range();
+	else {
+		pr_err("msm-charger no batt gauge batt=out_of_temperatur\n");
+		return 0;
+	}
+}
+
+static int is_battery_id_valid(void)
+{
+	if (msm_batt_gauge && msm_batt_gauge->is_battery_id_valid)
+		return msm_batt_gauge->is_battery_id_valid();
+	else {
+		pr_err("msm-charger no batt gauge batt=id_invalid\n");
+		return 0;
+	}
+}
+
+static int get_prop_battery_mvolts(void)
+{
+	if (msm_batt_gauge && msm_batt_gauge->get_battery_mvolts)
+		return msm_batt_gauge->get_battery_mvolts();
+	else {
+		pr_err("msm-charger no batt gauge assuming 3.5V\n");
+		return MSM_CHARGER_GAUGE_MISSING_VOLTS;
+	}
+}
+
+static int get_battery_temperature(void)
+{
+	if (msm_batt_gauge && msm_batt_gauge->get_battery_temperature)
+		return msm_batt_gauge->get_battery_temperature();
+	else {
+		pr_err("msm-charger no batt gauge assuming 35 deg G\n");
+		return MSM_CHARGER_GAUGE_MISSING_TEMP;
+	}
+}
+
+static int get_prop_batt_capacity(void)
+{
+	if (msm_batt_gauge && msm_batt_gauge->get_batt_remaining_capacity)
+		return msm_batt_gauge->get_batt_remaining_capacity();
+
+	return msm_chg.get_batt_capacity_percent();
+}
+
+static int get_prop_batt_health(void)
+{
+	int status = 0;
+
+	if (msm_chg.batt_status == BATT_STATUS_TEMPERATURE_OUT_OF_RANGE)
+		status = POWER_SUPPLY_HEALTH_OVERHEAT;
+	else
+		status = POWER_SUPPLY_HEALTH_GOOD;
+
+	return status;
+}
+
+static int get_prop_charge_type(void)
+{
+	int status = 0;
+
+	if (msm_chg.batt_status == BATT_STATUS_TRKL_CHARGING)
+		status = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
+	else if (msm_chg.batt_status == BATT_STATUS_FAST_CHARGING)
+		status = POWER_SUPPLY_CHARGE_TYPE_FAST;
+	else
+		status = POWER_SUPPLY_CHARGE_TYPE_NONE;
+
+	return status;
+}
+
+static int get_prop_batt_status(void)
+{
+	int status = 0;
+
+	if (msm_batt_gauge && msm_batt_gauge->get_battery_status) {
+		status = msm_batt_gauge->get_battery_status();
+		if (status == POWER_SUPPLY_STATUS_CHARGING ||
+			status == POWER_SUPPLY_STATUS_FULL ||
+			status == POWER_SUPPLY_STATUS_DISCHARGING)
+			return status;
+	}
+
+	if (is_batt_status_charging())
+		status = POWER_SUPPLY_STATUS_CHARGING;
+	else if (msm_chg.batt_status ==
+		 BATT_STATUS_JUST_FINISHED_CHARGING
+			 && msm_chg.current_chg_priv != NULL)
+		status = POWER_SUPPLY_STATUS_FULL;
+	else
+		status = POWER_SUPPLY_STATUS_DISCHARGING;
+
+	return status;
+}
+
+ /* This function should only be called within handle_event or resume */
+static void update_batt_status(void)
+{
+	if (is_battery_present()) {
+		if (is_battery_id_valid()) {
+			if (msm_chg.batt_status == BATT_STATUS_ABSENT
+				|| msm_chg.batt_status
+					== BATT_STATUS_ID_INVALID) {
+				msm_chg.batt_status = BATT_STATUS_DISCHARGING;
+			}
+		} else
+			msm_chg.batt_status = BATT_STATUS_ID_INVALID;
+	 } else
+		msm_chg.batt_status = BATT_STATUS_ABSENT;
+}
+
+static enum power_supply_property msm_power_props[] = {
+	POWER_SUPPLY_PROP_PRESENT,
+	POWER_SUPPLY_PROP_ONLINE,
+};
+
+static char *msm_power_supplied_to[] = {
+	"battery",
+};
+
+static int msm_power_get_property(struct power_supply *psy,
+				  enum power_supply_property psp,
+				  union power_supply_propval *val)
+{
+	struct msm_hardware_charger_priv *priv;
+
+	priv = container_of(psy, struct msm_hardware_charger_priv, psy);
+	switch (psp) {
+	case POWER_SUPPLY_PROP_PRESENT:
+		val->intval = !(priv->hw_chg_state == CHG_ABSENT_STATE);
+		break;
+	case POWER_SUPPLY_PROP_ONLINE:
+		val->intval = (priv->hw_chg_state == CHG_READY_STATE)
+			|| (priv->hw_chg_state == CHG_CHARGING_STATE);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static enum power_supply_property msm_batt_power_props[] = {
+	POWER_SUPPLY_PROP_STATUS,
+	POWER_SUPPLY_PROP_CHARGE_TYPE,
+	POWER_SUPPLY_PROP_HEALTH,
+	POWER_SUPPLY_PROP_PRESENT,
+	POWER_SUPPLY_PROP_TECHNOLOGY,
+	POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+	POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+	POWER_SUPPLY_PROP_VOLTAGE_NOW,
+	POWER_SUPPLY_PROP_CAPACITY,
+};
+
+static int msm_batt_power_get_property(struct power_supply *psy,
+				       enum power_supply_property psp,
+				       union power_supply_propval *val)
+{
+	switch (psp) {
+	case POWER_SUPPLY_PROP_STATUS:
+		val->intval = get_prop_batt_status();
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_TYPE:
+		val->intval = get_prop_charge_type();
+		break;
+	case POWER_SUPPLY_PROP_HEALTH:
+		val->intval = get_prop_batt_health();
+		break;
+	case POWER_SUPPLY_PROP_PRESENT:
+		val->intval = !(msm_chg.batt_status == BATT_STATUS_ABSENT);
+		break;
+	case POWER_SUPPLY_PROP_TECHNOLOGY:
+		val->intval = POWER_SUPPLY_TECHNOLOGY_NiMH;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+		val->intval = msm_chg.max_voltage * 1000;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
+		val->intval = msm_chg.min_voltage * 1000;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+		val->intval = get_prop_battery_mvolts();
+		break;
+	case POWER_SUPPLY_PROP_CAPACITY:
+		val->intval = get_prop_batt_capacity();
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static struct power_supply msm_psy_batt = {
+	.name = "battery",
+	.type = POWER_SUPPLY_TYPE_BATTERY,
+	.properties = msm_batt_power_props,
+	.num_properties = ARRAY_SIZE(msm_batt_power_props),
+	.get_property = msm_batt_power_get_property,
+};
+
+static int usb_chg_current;
+static struct msm_hardware_charger_priv *usb_hw_chg_priv;
+static void (*notify_vbus_state_func_ptr)(int);
+static int usb_notified_of_insertion;
+
+/* this is passed to the hsusb via platform_data msm_otg_pdata */
+int msm_charger_register_vbus_sn(void (*callback)(int))
+{
+	pr_debug(KERN_INFO "%s\n", __func__);
+	notify_vbus_state_func_ptr = callback;
+	return 0;
+}
+
+/* this is passed to the hsusb via platform_data msm_otg_pdata */
+void msm_charger_unregister_vbus_sn(void (*callback)(int))
+{
+	pr_debug(KERN_INFO "%s\n", __func__);
+	notify_vbus_state_func_ptr = NULL;
+}
+
+static void notify_usb_of_the_plugin_event(struct msm_hardware_charger_priv
+					   *hw_chg, int plugin)
+{
+	plugin = !!plugin;
+	if (plugin == 1 && usb_notified_of_insertion == 0) {
+		usb_notified_of_insertion = 1;
+		if (notify_vbus_state_func_ptr) {
+			dev_dbg(msm_chg.dev, "%s notifying plugin\n", __func__);
+			(*notify_vbus_state_func_ptr) (plugin);
+		} else
+			dev_dbg(msm_chg.dev, "%s unable to notify plugin\n",
+				__func__);
+		usb_hw_chg_priv = hw_chg;
+	}
+	if (plugin == 0 && usb_notified_of_insertion == 1) {
+		if (notify_vbus_state_func_ptr) {
+			dev_dbg(msm_chg.dev, "%s notifying unplugin\n",
+				__func__);
+			(*notify_vbus_state_func_ptr) (plugin);
+		} else
+			dev_dbg(msm_chg.dev, "%s unable to notify unplugin\n",
+				__func__);
+		usb_notified_of_insertion = 0;
+		usb_hw_chg_priv = NULL;
+	}
+}
+
+static unsigned int msm_chg_get_batt_capacity_percent(void)
+{
+	unsigned int current_voltage = get_prop_battery_mvolts();
+	unsigned int low_voltage = msm_chg.min_voltage;
+	unsigned int high_voltage = msm_chg.max_voltage;
+
+	if (current_voltage <= low_voltage)
+		return 0;
+	else if (current_voltage >= high_voltage)
+		return 100;
+	else
+		return (current_voltage - low_voltage) * 100
+		    / (high_voltage - low_voltage);
+}
+
+#ifdef DEBUG
+static inline void debug_print(const char *func,
+			       struct msm_hardware_charger_priv *hw_chg_priv)
+{
+	dev_dbg(msm_chg.dev,
+		"%s current=(%s)(s=%d)(r=%d) new=(%s)(s=%d)(r=%d) batt=%d En\n",
+		func,
+		msm_chg.current_chg_priv ? msm_chg.current_chg_priv->
+		hw_chg->name : "none",
+		msm_chg.current_chg_priv ? msm_chg.
+		current_chg_priv->hw_chg_state : -1,
+		msm_chg.current_chg_priv ? msm_chg.current_chg_priv->
+		hw_chg->rating : -1,
+		hw_chg_priv ? hw_chg_priv->hw_chg->name : "none",
+		hw_chg_priv ? hw_chg_priv->hw_chg_state : -1,
+		hw_chg_priv ? hw_chg_priv->hw_chg->rating : -1,
+		msm_chg.batt_status);
+}
+#else
+static inline void debug_print(const char *func,
+			       struct msm_hardware_charger_priv *hw_chg_priv)
+{
+}
+#endif
+
+static struct msm_hardware_charger_priv *find_best_charger(void)
+{
+	struct msm_hardware_charger_priv *hw_chg_priv;
+	struct msm_hardware_charger_priv *better;
+	int rating;
+
+	better = NULL;
+	rating = 0;
+
+	list_for_each_entry(hw_chg_priv, &msm_chg.msm_hardware_chargers, list) {
+		if (is_chg_capable_of_charging(hw_chg_priv)) {
+			if (hw_chg_priv->hw_chg->rating > rating) {
+				rating = hw_chg_priv->hw_chg->rating;
+				better = hw_chg_priv;
+			}
+		}
+	}
+
+	return better;
+}
+
+static int msm_charging_switched(struct msm_hardware_charger_priv *priv)
+{
+	int ret = 0;
+
+	if (priv->hw_chg->charging_switched)
+		ret = priv->hw_chg->charging_switched(priv->hw_chg);
+	return ret;
+}
+
+static int msm_stop_charging(struct msm_hardware_charger_priv *priv)
+{
+	int ret;
+
+	ret = priv->hw_chg->stop_charging(priv->hw_chg);
+	if (!ret)
+		wake_unlock(&msm_chg.wl);
+	return ret;
+}
+
+/* the best charger has been selected -start charging from current_chg_priv */
+static int msm_start_charging(void)
+{
+	int ret;
+	struct msm_hardware_charger_priv *priv;
+
+	priv = msm_chg.current_chg_priv;
+	wake_lock(&msm_chg.wl);
+	ret = priv->hw_chg->start_charging(priv->hw_chg, msm_chg.max_voltage,
+					 priv->max_source_current);
+	if (ret) {
+		wake_unlock(&msm_chg.wl);
+		dev_err(msm_chg.dev, "%s couldnt start chg error = %d\n",
+			priv->hw_chg->name, ret);
+	} else
+		priv->hw_chg_state = CHG_CHARGING_STATE;
+
+	return ret;
+}
+
+static void handle_charging_done(struct msm_hardware_charger_priv *priv)
+{
+	if (msm_chg.current_chg_priv == priv) {
+		if (msm_chg.current_chg_priv->hw_chg_state ==
+		    CHG_CHARGING_STATE)
+			if (msm_stop_charging(msm_chg.current_chg_priv)) {
+				dev_err(msm_chg.dev, "%s couldnt stop chg\n",
+					msm_chg.current_chg_priv->hw_chg->name);
+			}
+		msm_chg.current_chg_priv->hw_chg_state = CHG_READY_STATE;
+
+		msm_chg.batt_status = BATT_STATUS_JUST_FINISHED_CHARGING;
+		dev_info(msm_chg.dev, "%s: stopping safety timer work\n",
+				__func__);
+		cancel_delayed_work(&msm_chg.teoc_work);
+
+		if (msm_batt_gauge && msm_batt_gauge->monitor_for_recharging)
+			msm_batt_gauge->monitor_for_recharging();
+		else
+			dev_err(msm_chg.dev,
+			      "%s: no batt gauge recharge monitor\n", __func__);
+	}
+}
+
+static void teoc(struct work_struct *work)
+{
+	/* we have been charging too long - stop charging */
+	dev_info(msm_chg.dev, "%s: safety timer work expired\n", __func__);
+
+	mutex_lock(&msm_chg.status_lock);
+	if (msm_chg.current_chg_priv != NULL
+	    && msm_chg.current_chg_priv->hw_chg_state == CHG_CHARGING_STATE) {
+		handle_charging_done(msm_chg.current_chg_priv);
+	}
+	mutex_unlock(&msm_chg.status_lock);
+}
+
+static void handle_battery_inserted(void)
+{
+	/* if a charger is already present start charging */
+	if (msm_chg.current_chg_priv != NULL &&
+	    is_batt_status_capable_of_charging() &&
+	    !is_batt_status_charging()) {
+		if (msm_start_charging()) {
+			dev_err(msm_chg.dev, "%s couldnt start chg\n",
+				msm_chg.current_chg_priv->hw_chg->name);
+			return;
+		}
+		msm_chg.batt_status = BATT_STATUS_TRKL_CHARGING;
+
+		dev_info(msm_chg.dev, "%s: starting safety timer work\n",
+				__func__);
+		queue_delayed_work(msm_chg.event_wq_thread,
+					&msm_chg.teoc_work,
+				      round_jiffies_relative(msecs_to_jiffies
+							     (msm_chg.
+							      safety_time)));
+	}
+}
+
+static void handle_battery_removed(void)
+{
+	/* if a charger is charging the battery stop it */
+	if (msm_chg.current_chg_priv != NULL
+	    && msm_chg.current_chg_priv->hw_chg_state == CHG_CHARGING_STATE) {
+		if (msm_stop_charging(msm_chg.current_chg_priv)) {
+			dev_err(msm_chg.dev, "%s couldnt stop chg\n",
+				msm_chg.current_chg_priv->hw_chg->name);
+		}
+		msm_chg.current_chg_priv->hw_chg_state = CHG_READY_STATE;
+
+		dev_info(msm_chg.dev, "%s: stopping safety timer work\n",
+				__func__);
+		cancel_delayed_work(&msm_chg.teoc_work);
+	}
+}
+
+static void update_heartbeat(struct work_struct *work)
+{
+	int temperature;
+
+	if (msm_chg.batt_status == BATT_STATUS_ABSENT
+		|| msm_chg.batt_status == BATT_STATUS_ID_INVALID) {
+		if (is_battery_present())
+			if (is_battery_id_valid()) {
+				msm_chg.batt_status = BATT_STATUS_DISCHARGING;
+				handle_battery_inserted();
+			}
+	} else {
+		if (!is_battery_present()) {
+			msm_chg.batt_status = BATT_STATUS_ABSENT;
+			handle_battery_removed();
+		}
+		/*
+		 * check battery id because a good battery could be removed
+		 * and replaced with a invalid battery.
+		 */
+		if (!is_battery_id_valid()) {
+			msm_chg.batt_status = BATT_STATUS_ID_INVALID;
+			handle_battery_removed();
+		}
+	}
+	pr_debug("msm-charger %s batt_status= %d\n",
+				__func__, msm_chg.batt_status);
+
+	if (msm_chg.current_chg_priv
+		&& msm_chg.current_chg_priv->hw_chg_state
+			== CHG_CHARGING_STATE) {
+		temperature = get_battery_temperature();
+		/* TODO implement JEITA SPEC*/
+	}
+
+	/* notify that the voltage has changed
+	 * the read of the capacity will trigger a
+	 * voltage read*/
+	power_supply_changed(&msm_psy_batt);
+
+	if (msm_chg.stop_update) {
+		msm_chg.stop_update = 0;
+		return;
+	}
+	queue_delayed_work(msm_chg.event_wq_thread,
+				&msm_chg.update_heartbeat_work,
+			      round_jiffies_relative(msecs_to_jiffies
+						     (msm_chg.update_time)));
+}
+
+/* set the charger state to READY before calling this */
+static void handle_charger_ready(struct msm_hardware_charger_priv *hw_chg_priv)
+{
+	debug_print(__func__, hw_chg_priv);
+
+	if (msm_chg.current_chg_priv != NULL
+	    && hw_chg_priv->hw_chg->rating >
+	    msm_chg.current_chg_priv->hw_chg->rating) {
+		if (msm_chg.current_chg_priv->hw_chg_state ==
+		    CHG_CHARGING_STATE) {
+			if (msm_stop_charging(msm_chg.current_chg_priv)) {
+				dev_err(msm_chg.dev, "%s couldnt stop chg\n",
+					msm_chg.current_chg_priv->hw_chg->name);
+				return;
+			}
+			if (msm_charging_switched(msm_chg.current_chg_priv)) {
+				dev_err(msm_chg.dev, "%s couldnt stop chg\n",
+					msm_chg.current_chg_priv->hw_chg->name);
+				return;
+			}
+		}
+		msm_chg.current_chg_priv->hw_chg_state = CHG_READY_STATE;
+		msm_chg.current_chg_priv = NULL;
+	}
+
+	if (msm_chg.current_chg_priv == NULL) {
+		msm_chg.current_chg_priv = hw_chg_priv;
+		dev_info(msm_chg.dev,
+			 "%s: best charger = %s\n", __func__,
+			 msm_chg.current_chg_priv->hw_chg->name);
+
+		if (!is_batt_status_capable_of_charging())
+			return;
+
+		/* start charging from the new charger */
+		if (!msm_start_charging()) {
+			/* if we simply switched chg continue with teoc timer
+			 * else we update the batt state and set the teoc
+			 * timer */
+			if (!is_batt_status_charging()) {
+				dev_info(msm_chg.dev,
+				       "%s: starting safety timer\n", __func__);
+				queue_delayed_work(msm_chg.event_wq_thread,
+							&msm_chg.teoc_work,
+						      round_jiffies_relative
+						      (msecs_to_jiffies
+						       (msm_chg.safety_time)));
+				msm_chg.batt_status = BATT_STATUS_TRKL_CHARGING;
+			}
+		} else {
+			/* we couldnt start charging from the new readied
+			 * charger */
+			if (is_batt_status_charging())
+				msm_chg.batt_status = BATT_STATUS_DISCHARGING;
+		}
+	}
+}
+
+static void handle_charger_removed(struct msm_hardware_charger_priv
+				   *hw_chg_removed, int new_state)
+{
+	struct msm_hardware_charger_priv *hw_chg_priv;
+
+	debug_print(__func__, hw_chg_removed);
+
+	if (msm_chg.current_chg_priv == hw_chg_removed) {
+		if (msm_chg.current_chg_priv->hw_chg_state
+						== CHG_CHARGING_STATE) {
+			if (msm_stop_charging(hw_chg_removed)) {
+				dev_err(msm_chg.dev, "%s couldnt stop chg\n",
+					msm_chg.current_chg_priv->hw_chg->name);
+			}
+		}
+		msm_chg.current_chg_priv = NULL;
+	}
+
+	hw_chg_removed->hw_chg_state = new_state;
+
+	if (msm_chg.current_chg_priv == NULL) {
+		hw_chg_priv = find_best_charger();
+		if (hw_chg_priv == NULL) {
+			dev_info(msm_chg.dev, "%s: no chargers\n", __func__);
+			/* if the battery was Just finished charging
+			 * we keep that state as is so that we dont rush
+			 * in to charging the battery when a charger is
+			 * plugged in shortly. */
+			if (is_batt_status_charging())
+				msm_chg.batt_status = BATT_STATUS_DISCHARGING;
+		} else {
+			msm_chg.current_chg_priv = hw_chg_priv;
+			dev_info(msm_chg.dev,
+				 "%s: best charger = %s\n", __func__,
+				 msm_chg.current_chg_priv->hw_chg->name);
+
+			if (!is_batt_status_capable_of_charging())
+				return;
+
+			if (msm_start_charging()) {
+				/* we couldnt start charging for some reason */
+				msm_chg.batt_status = BATT_STATUS_DISCHARGING;
+			}
+		}
+	}
+
+	/* if we arent charging stop the safety timer */
+	if (!is_batt_status_charging()) {
+		dev_info(msm_chg.dev, "%s: stopping safety timer work\n",
+				__func__);
+		cancel_delayed_work(&msm_chg.teoc_work);
+	}
+}
+
+static void handle_event(struct msm_hardware_charger *hw_chg, int event)
+{
+	struct msm_hardware_charger_priv *priv = NULL;
+
+	/*
+	 * if hw_chg is NULL then this event comes from non-charger
+	 * parties like battery gauge
+	 */
+	if (hw_chg)
+		priv = hw_chg->charger_private;
+
+	mutex_lock(&msm_chg.status_lock);
+
+	switch (event) {
+	case CHG_INSERTED_EVENT:
+		if (priv->hw_chg_state != CHG_ABSENT_STATE) {
+			dev_info(msm_chg.dev,
+				 "%s insertion detected when cbl present",
+				 hw_chg->name);
+			break;
+		}
+		update_batt_status();
+		if (hw_chg->type == CHG_TYPE_USB) {
+			priv->hw_chg_state = CHG_PRESENT_STATE;
+			notify_usb_of_the_plugin_event(priv, 1);
+			if (usb_chg_current) {
+				priv->max_source_current = usb_chg_current;
+				usb_chg_current = 0;
+				/* usb has already indicated us to charge */
+				priv->hw_chg_state = CHG_READY_STATE;
+				handle_charger_ready(priv);
+			}
+		} else {
+			priv->hw_chg_state = CHG_READY_STATE;
+			handle_charger_ready(priv);
+		}
+		break;
+	case CHG_ENUMERATED_EVENT:	/* only in USB types */
+		if (priv->hw_chg_state == CHG_ABSENT_STATE) {
+			dev_info(msm_chg.dev, "%s enum withuot presence\n",
+				 hw_chg->name);
+			break;
+		}
+		update_batt_status();
+		dev_dbg(msm_chg.dev, "%s enum with %dmA to draw\n",
+			 hw_chg->name, priv->max_source_current);
+		if (priv->max_source_current == 0) {
+			/* usb subsystem doesnt want us to draw
+			 * charging current */
+			/* act as if the charge is removed */
+			if (priv->hw_chg_state != CHG_PRESENT_STATE)
+				handle_charger_removed(priv, CHG_PRESENT_STATE);
+		} else {
+			if (priv->hw_chg_state != CHG_READY_STATE) {
+				priv->hw_chg_state = CHG_READY_STATE;
+				handle_charger_ready(priv);
+			}
+		}
+		break;
+	case CHG_REMOVED_EVENT:
+		if (priv->hw_chg_state == CHG_ABSENT_STATE) {
+			dev_info(msm_chg.dev, "%s cable already removed\n",
+				 hw_chg->name);
+			break;
+		}
+		update_batt_status();
+		if (hw_chg->type == CHG_TYPE_USB) {
+			usb_chg_current = 0;
+			notify_usb_of_the_plugin_event(priv, 0);
+		}
+		handle_charger_removed(priv, CHG_ABSENT_STATE);
+		break;
+	case CHG_DONE_EVENT:
+		if (priv->hw_chg_state == CHG_CHARGING_STATE)
+			handle_charging_done(priv);
+		break;
+	case CHG_BATT_BEGIN_FAST_CHARGING:
+		/* only update if we are TRKL charging */
+		if (msm_chg.batt_status == BATT_STATUS_TRKL_CHARGING)
+			msm_chg.batt_status = BATT_STATUS_FAST_CHARGING;
+		break;
+	case CHG_BATT_NEEDS_RECHARGING:
+		msm_chg.batt_status = BATT_STATUS_DISCHARGING;
+		handle_battery_inserted();
+		priv = msm_chg.current_chg_priv;
+		break;
+	case CHG_BATT_TEMP_OUTOFRANGE:
+		/* the batt_temp out of range can trigger
+		 * when the battery is absent */
+		if (!is_battery_present()
+		    && msm_chg.batt_status != BATT_STATUS_ABSENT) {
+			msm_chg.batt_status = BATT_STATUS_ABSENT;
+			handle_battery_removed();
+			break;
+		}
+		if (msm_chg.batt_status == BATT_STATUS_TEMPERATURE_OUT_OF_RANGE)
+			break;
+		msm_chg.batt_status = BATT_STATUS_TEMPERATURE_OUT_OF_RANGE;
+		handle_battery_removed();
+		break;
+	case CHG_BATT_TEMP_INRANGE:
+		if (msm_chg.batt_status != BATT_STATUS_TEMPERATURE_OUT_OF_RANGE)
+			break;
+		msm_chg.batt_status = BATT_STATUS_ID_INVALID;
+		/* check id */
+		if (!is_battery_id_valid())
+			break;
+		/* assume that we are discharging from the battery
+		 * and act as if the battery was inserted
+		 * if a charger is present charging will be resumed */
+		msm_chg.batt_status = BATT_STATUS_DISCHARGING;
+		handle_battery_inserted();
+		break;
+	case CHG_BATT_INSERTED:
+		if (msm_chg.batt_status != BATT_STATUS_ABSENT)
+			break;
+		/* debounce */
+		if (!is_battery_present())
+			break;
+		msm_chg.batt_status = BATT_STATUS_ID_INVALID;
+		if (!is_battery_id_valid())
+			break;
+		/* assume that we are discharging from the battery */
+		msm_chg.batt_status = BATT_STATUS_DISCHARGING;
+		/* check if a charger is present */
+		handle_battery_inserted();
+		break;
+	case CHG_BATT_REMOVED:
+		if (msm_chg.batt_status == BATT_STATUS_ABSENT)
+			break;
+		/* debounce */
+		if (is_battery_present())
+			break;
+		msm_chg.batt_status = BATT_STATUS_ABSENT;
+		handle_battery_removed();
+		break;
+	case CHG_BATT_STATUS_CHANGE:
+		/* TODO  battery SOC like battery-alarm/charging-full features
+		can be added here for future improvement */
+		break;
+	}
+	dev_dbg(msm_chg.dev, "%s %d done batt_status=%d\n", __func__,
+		event, msm_chg.batt_status);
+
+	/* update userspace */
+	if (msm_batt_gauge)
+		power_supply_changed(&msm_psy_batt);
+	if (priv)
+		power_supply_changed(&priv->psy);
+
+	mutex_unlock(&msm_chg.status_lock);
+}
+
+static int msm_chg_dequeue_event(struct msm_charger_event **event)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&msm_chg.queue_lock, flags);
+	if (msm_chg.queue_count == 0) {
+		spin_unlock_irqrestore(&msm_chg.queue_lock, flags);
+		return -EINVAL;
+	}
+	*event = &msm_chg.queue[msm_chg.head];
+	msm_chg.head = (msm_chg.head + 1) % MSM_CHG_MAX_EVENTS;
+	pr_debug("%s dequeueing %d\n", __func__, (*event)->event);
+	msm_chg.queue_count--;
+	spin_unlock_irqrestore(&msm_chg.queue_lock, flags);
+	return 0;
+}
+
+static int msm_chg_enqueue_event(struct msm_hardware_charger *hw_chg,
+			enum msm_hardware_charger_event event)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&msm_chg.queue_lock, flags);
+	if (msm_chg.queue_count == MSM_CHG_MAX_EVENTS) {
+		spin_unlock_irqrestore(&msm_chg.queue_lock, flags);
+		pr_err("%s: queue full cannot enqueue %d\n",
+				__func__, event);
+		return -EAGAIN;
+	}
+	pr_debug("%s queueing %d\n", __func__, event);
+	msm_chg.queue[msm_chg.tail].event = event;
+	msm_chg.queue[msm_chg.tail].hw_chg = hw_chg;
+	msm_chg.tail = (msm_chg.tail + 1)%MSM_CHG_MAX_EVENTS;
+	msm_chg.queue_count++;
+	spin_unlock_irqrestore(&msm_chg.queue_lock, flags);
+	return 0;
+}
+
+static void process_events(struct work_struct *work)
+{
+	struct msm_charger_event *event;
+	int rc;
+
+	do {
+		rc = msm_chg_dequeue_event(&event);
+		if (!rc)
+			handle_event(event->hw_chg, event->event);
+	} while (!rc);
+}
+
+/* USB calls these to tell us how much charging current we should draw */
+void msm_charger_vbus_draw(unsigned int mA)
+{
+	if (usb_hw_chg_priv) {
+		usb_hw_chg_priv->max_source_current = mA;
+		msm_charger_notify_event(usb_hw_chg_priv->hw_chg,
+						CHG_ENUMERATED_EVENT);
+	} else
+		/* remember the current, to be used when charger is ready */
+		usb_chg_current = mA;
+}
+
+static int __init determine_initial_batt_status(void)
+{
+	int rc;
+
+	if (is_battery_present())
+		if (is_battery_id_valid())
+			if (is_battery_temp_within_range())
+				msm_chg.batt_status = BATT_STATUS_DISCHARGING;
+			else
+				msm_chg.batt_status
+				    = BATT_STATUS_TEMPERATURE_OUT_OF_RANGE;
+		else
+			msm_chg.batt_status = BATT_STATUS_ID_INVALID;
+	else
+		msm_chg.batt_status = BATT_STATUS_ABSENT;
+
+	if (is_batt_status_capable_of_charging())
+		handle_battery_inserted();
+
+	rc = power_supply_register(msm_chg.dev, &msm_psy_batt);
+	if (rc < 0) {
+		dev_err(msm_chg.dev, "%s: power_supply_register failed"
+			" rc=%d\n", __func__, rc);
+		return rc;
+	}
+
+	/* start updaing the battery powersupply every msm_chg.update_time
+	 * milliseconds */
+	queue_delayed_work(msm_chg.event_wq_thread,
+				&msm_chg.update_heartbeat_work,
+			      round_jiffies_relative(msecs_to_jiffies
+						     (msm_chg.update_time)));
+
+	pr_debug("%s:OK batt_status=%d\n", __func__, msm_chg.batt_status);
+	return 0;
+}
+
+static int __devinit msm_charger_probe(struct platform_device *pdev)
+{
+	msm_chg.dev = &pdev->dev;
+	if (pdev->dev.platform_data) {
+		unsigned int milli_secs;
+
+		struct msm_charger_platform_data *pdata
+		    =
+		    (struct msm_charger_platform_data *)pdev->dev.platform_data;
+
+		milli_secs = pdata->safety_time * 60 * MSEC_PER_SEC;
+		if (milli_secs > jiffies_to_msecs(MAX_JIFFY_OFFSET)) {
+			dev_warn(&pdev->dev, "%s: safety time too large"
+				 "%dms\n", __func__, milli_secs);
+			milli_secs = jiffies_to_msecs(MAX_JIFFY_OFFSET);
+		}
+		msm_chg.safety_time = milli_secs;
+
+		milli_secs = pdata->update_time * 60 * MSEC_PER_SEC;
+		if (milli_secs > jiffies_to_msecs(MAX_JIFFY_OFFSET)) {
+			dev_warn(&pdev->dev, "%s: safety time too large"
+				 "%dms\n", __func__, milli_secs);
+			milli_secs = jiffies_to_msecs(MAX_JIFFY_OFFSET);
+		}
+		msm_chg.update_time = milli_secs;
+
+		msm_chg.max_voltage = pdata->max_voltage;
+		msm_chg.min_voltage = pdata->min_voltage;
+		msm_chg.get_batt_capacity_percent =
+		    pdata->get_batt_capacity_percent;
+	}
+	if (msm_chg.safety_time == 0)
+		msm_chg.safety_time = CHARGING_TEOC_MS;
+	if (msm_chg.update_time == 0)
+		msm_chg.update_time = UPDATE_TIME_MS;
+	if (msm_chg.max_voltage == 0)
+		msm_chg.max_voltage = DEFAULT_BATT_MAX_V;
+	if (msm_chg.min_voltage == 0)
+		msm_chg.min_voltage = DEFAULT_BATT_MIN_V;
+	if (msm_chg.get_batt_capacity_percent == NULL)
+		msm_chg.get_batt_capacity_percent =
+		    msm_chg_get_batt_capacity_percent;
+
+	mutex_init(&msm_chg.status_lock);
+	INIT_DELAYED_WORK(&msm_chg.teoc_work, teoc);
+	INIT_DELAYED_WORK(&msm_chg.update_heartbeat_work, update_heartbeat);
+
+	wake_lock_init(&msm_chg.wl, WAKE_LOCK_SUSPEND, "msm_charger");
+	return 0;
+}
+
+static int __devexit msm_charger_remove(struct platform_device *pdev)
+{
+	mutex_destroy(&msm_chg.status_lock);
+	power_supply_unregister(&msm_psy_batt);
+	return 0;
+}
+
+int msm_charger_notify_event(struct msm_hardware_charger *hw_chg,
+			     enum msm_hardware_charger_event event)
+{
+	msm_chg_enqueue_event(hw_chg, event);
+	queue_work(msm_chg.event_wq_thread, &msm_chg.queue_work);
+	return 0;
+}
+EXPORT_SYMBOL(msm_charger_notify_event);
+
+int msm_charger_register(struct msm_hardware_charger *hw_chg)
+{
+	struct msm_hardware_charger_priv *priv;
+	int rc = 0;
+
+	if (!msm_chg.inited) {
+		pr_err("%s: msm_chg is NULL,Too early to register\n", __func__);
+		return -EAGAIN;
+	}
+
+	if (hw_chg->start_charging == NULL
+		|| hw_chg->stop_charging == NULL
+		|| hw_chg->name == NULL
+		|| hw_chg->rating == 0) {
+		pr_err("%s: invalid hw_chg\n", __func__);
+		return -EINVAL;
+	}
+
+	priv = kzalloc(sizeof *priv, GFP_KERNEL);
+	if (priv == NULL) {
+		dev_err(msm_chg.dev, "%s kzalloc failed\n", __func__);
+		return -ENOMEM;
+	}
+
+	priv->psy.name = hw_chg->name;
+	if (hw_chg->type == CHG_TYPE_USB)
+		priv->psy.type = POWER_SUPPLY_TYPE_USB;
+	else
+		priv->psy.type = POWER_SUPPLY_TYPE_MAINS;
+
+	priv->psy.supplied_to = msm_power_supplied_to;
+	priv->psy.num_supplicants = ARRAY_SIZE(msm_power_supplied_to);
+	priv->psy.properties = msm_power_props;
+	priv->psy.num_properties = ARRAY_SIZE(msm_power_props);
+	priv->psy.get_property = msm_power_get_property;
+
+	rc = power_supply_register(NULL, &priv->psy);
+	if (rc) {
+		dev_err(msm_chg.dev, "%s power_supply_register failed\n",
+			__func__);
+		goto out;
+	}
+
+	priv->hw_chg = hw_chg;
+	priv->hw_chg_state = CHG_ABSENT_STATE;
+	INIT_LIST_HEAD(&priv->list);
+	mutex_lock(&msm_chg.msm_hardware_chargers_lock);
+	list_add_tail(&priv->list, &msm_chg.msm_hardware_chargers);
+	mutex_unlock(&msm_chg.msm_hardware_chargers_lock);
+	hw_chg->charger_private = (void *)priv;
+	return 0;
+
+out:
+	wake_lock_destroy(&msm_chg.wl);
+	kfree(priv);
+	return rc;
+}
+EXPORT_SYMBOL(msm_charger_register);
+
+void msm_battery_gauge_register(struct msm_battery_gauge *batt_gauge)
+{
+	if (msm_batt_gauge) {
+		msm_batt_gauge = batt_gauge;
+		pr_err("msm-charger %s multiple battery gauge called\n",
+								__func__);
+	} else {
+		msm_batt_gauge = batt_gauge;
+		determine_initial_batt_status();
+	}
+}
+EXPORT_SYMBOL(msm_battery_gauge_register);
+
+void msm_battery_gauge_unregister(struct msm_battery_gauge *batt_gauge)
+{
+	msm_batt_gauge = NULL;
+}
+EXPORT_SYMBOL(msm_battery_gauge_unregister);
+
+int msm_charger_unregister(struct msm_hardware_charger *hw_chg)
+{
+	struct msm_hardware_charger_priv *priv;
+
+	priv = (struct msm_hardware_charger_priv *)(hw_chg->charger_private);
+	mutex_lock(&msm_chg.msm_hardware_chargers_lock);
+	list_del(&priv->list);
+	mutex_unlock(&msm_chg.msm_hardware_chargers_lock);
+	wake_lock_destroy(&msm_chg.wl);
+	power_supply_unregister(&priv->psy);
+	kfree(priv);
+	return 0;
+}
+EXPORT_SYMBOL(msm_charger_unregister);
+
+static int msm_charger_suspend(struct device *dev)
+{
+	dev_dbg(msm_chg.dev, "%s suspended\n", __func__);
+	msm_chg.stop_update = 1;
+	cancel_delayed_work(&msm_chg.update_heartbeat_work);
+	mutex_lock(&msm_chg.status_lock);
+	handle_battery_removed();
+	mutex_unlock(&msm_chg.status_lock);
+	return 0;
+}
+
+static int msm_charger_resume(struct device *dev)
+{
+	dev_dbg(msm_chg.dev, "%s resumed\n", __func__);
+	msm_chg.stop_update = 0;
+	/* start updaing the battery powersupply every msm_chg.update_time
+	 * milliseconds */
+	queue_delayed_work(msm_chg.event_wq_thread,
+				&msm_chg.update_heartbeat_work,
+			      round_jiffies_relative(msecs_to_jiffies
+						     (msm_chg.update_time)));
+	mutex_lock(&msm_chg.status_lock);
+	handle_battery_inserted();
+	mutex_unlock(&msm_chg.status_lock);
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(msm_charger_pm_ops,
+		msm_charger_suspend, msm_charger_resume);
+
+static struct platform_driver msm_charger_driver = {
+	.probe = msm_charger_probe,
+	.remove = __devexit_p(msm_charger_remove),
+	.driver = {
+		   .name = "msm-charger",
+		   .owner = THIS_MODULE,
+		   .pm = &msm_charger_pm_ops,
+	},
+};
+
+static int __init msm_charger_init(void)
+{
+	int rc;
+
+	INIT_LIST_HEAD(&msm_chg.msm_hardware_chargers);
+	msm_chg.count_chargers = 0;
+	mutex_init(&msm_chg.msm_hardware_chargers_lock);
+
+	msm_chg.queue = kzalloc(sizeof(struct msm_charger_event)
+				* MSM_CHG_MAX_EVENTS,
+				GFP_KERNEL);
+	if (!msm_chg.queue) {
+		rc = -ENOMEM;
+		goto out;
+	}
+	msm_chg.tail = 0;
+	msm_chg.head = 0;
+	spin_lock_init(&msm_chg.queue_lock);
+	msm_chg.queue_count = 0;
+	INIT_WORK(&msm_chg.queue_work, process_events);
+	msm_chg.event_wq_thread = create_workqueue("msm_charger_eventd");
+	if (!msm_chg.event_wq_thread) {
+		rc = -ENOMEM;
+		goto free_queue;
+	}
+	rc = platform_driver_register(&msm_charger_driver);
+	if (rc < 0) {
+		pr_err("%s: FAIL: platform_driver_register. rc = %d\n",
+		       __func__, rc);
+		goto destroy_wq_thread;
+	}
+	msm_chg.inited = 1;
+	return 0;
+
+destroy_wq_thread:
+	destroy_workqueue(msm_chg.event_wq_thread);
+free_queue:
+	kfree(msm_chg.queue);
+out:
+	return rc;
+}
+
+static void __exit msm_charger_exit(void)
+{
+	flush_workqueue(msm_chg.event_wq_thread);
+	destroy_workqueue(msm_chg.event_wq_thread);
+	kfree(msm_chg.queue);
+	platform_driver_unregister(&msm_charger_driver);
+}
+
+module_init(msm_charger_init);
+module_exit(msm_charger_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Abhijeet Dharmapurikar <adharmap@codeaurora.org>");
+MODULE_DESCRIPTION("Battery driver for Qualcomm MSM chipsets.");
+MODULE_VERSION("1.0");
diff --git a/drivers/power/pm8058_usb_fix.c b/drivers/power/pm8058_usb_fix.c
new file mode 100644
index 0000000..80b1f87
--- /dev/null
+++ b/drivers/power/pm8058_usb_fix.c
@@ -0,0 +1,357 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/errno.h>
+#include <linux/mfd/pmic8058.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/workqueue.h>
+#include <linux/debugfs.h>
+#include <linux/slab.h>
+
+#include <mach/msm_xo.h>
+#include <mach/msm_hsusb.h>
+
+/* Config Regs  and their bits*/
+#define PM8058_CHG_TEST			0x75
+#define IGNORE_LL                       2
+
+#define PM8058_CHG_TEST_2		0xEA
+#define PM8058_CHG_TEST_3		0xEB
+#define PM8058_OVP_TEST_REG		0xF6
+#define FORCE_OVP_OFF			3
+
+#define PM8058_CHG_CNTRL		0x1E
+#define CHG_TRICKLE_EN			7
+#define CHG_USB_SUSPEND			6
+#define CHG_IMON_CAL			5
+#define CHG_IMON_GAIN			4
+#define CHG_VBUS_FROM_BOOST_OVRD	2
+#define CHG_CHARGE_DIS			1
+#define CHG_VCP_EN			0
+
+#define PM8058_CHG_CNTRL_2		0xD8
+#define ATC_DIS				7	/* coincell backed */
+#define CHARGE_AUTO_DIS			6
+#define DUMB_CHG_OVRD			5	/* coincell backed */
+#define ENUM_DONE			4
+#define CHG_TEMP_MODE			3
+#define CHG_BATT_TEMP_DIS		1	/* coincell backed */
+#define CHG_FAILED_CLEAR		0
+
+#define PM8058_CHG_VMAX_SEL		0x21
+#define PM8058_CHG_VBAT_DET		0xD9
+#define PM8058_CHG_IMAX			0x1F
+#define PM8058_CHG_TRICKLE		0xDB
+#define PM8058_CHG_ITERM		0xDC
+#define PM8058_CHG_TTRKL_MAX		0xE1
+#define PM8058_CHG_TCHG_MAX		0xE4
+#define PM8058_CHG_TEMP_THRESH		0xE2
+#define PM8058_CHG_TEMP_REG		0xE3
+#define PM8058_CHG_PULSE		0x22
+
+/* IRQ STATUS and CLEAR */
+#define PM8058_CHG_STATUS_CLEAR_IRQ_1	0x31
+#define PM8058_CHG_STATUS_CLEAR_IRQ_3	0x33
+#define PM8058_CHG_STATUS_CLEAR_IRQ_10	0xB3
+#define PM8058_CHG_STATUS_CLEAR_IRQ_11	0xB4
+
+/* IRQ MASKS */
+#define PM8058_CHG_MASK_IRQ_1		0x38
+
+#define PM8058_CHG_MASK_IRQ_3		0x3A
+#define PM8058_CHG_MASK_IRQ_10		0xBA
+#define PM8058_CHG_MASK_IRQ_11		0xBB
+
+/* IRQ Real time status regs */
+#define PM8058_CHG_STATUS_RT_1		0x3F
+#define STATUS_RTCHGVAL			7
+#define STATUS_RTCHGINVAL		6
+#define STATUS_RTBATT_REPLACE		5
+#define STATUS_RTVBATDET_LOW		4
+#define STATUS_RTCHGILIM		3
+#define STATUS_RTPCTDONE		1
+#define STATUS_RTVCP			0
+#define PM8058_CHG_STATUS_RT_3		0x41
+#define PM8058_CHG_STATUS_RT_10		0xC1
+#define PM8058_CHG_STATUS_RT_11		0xC2
+
+/* VTRIM */
+#define PM8058_CHG_VTRIM		0x1D
+#define PM8058_CHG_VBATDET_TRIM		0x1E
+#define PM8058_CHG_ITRIM		0x1F
+#define PM8058_CHG_TTRIM		0x20
+
+#define AUTO_CHARGING_VMAXSEL				4200
+#define AUTO_CHARGING_FAST_TIME_MAX_MINUTES		512
+#define AUTO_CHARGING_TRICKLE_TIME_MINUTES		30
+#define AUTO_CHARGING_VEOC_ITERM			100
+#define AUTO_CHARGING_IEOC_ITERM			160
+
+#define AUTO_CHARGING_VBATDET				4150
+#define AUTO_CHARGING_VEOC_VBATDET			4100
+#define AUTO_CHARGING_VEOC_TCHG				16
+#define AUTO_CHARGING_VEOC_TCHG_FINAL_CYCLE		32
+#define AUTO_CHARGING_VEOC_BEGIN_TIME_MS		5400000
+
+#define AUTO_CHARGING_VEOC_VBAT_LOW_CHECK_TIME_MS	60000
+#define AUTO_CHARGING_RESUME_CHARGE_DETECTION_COUNTER	5
+
+#define PM8058_CHG_I_STEP_MA 50
+#define PM8058_CHG_I_MIN_MA 50
+#define PM8058_CHG_T_TCHG_SHIFT 2
+#define PM8058_CHG_I_TERM_STEP_MA 10
+#define PM8058_CHG_V_STEP_MV 25
+#define PM8058_CHG_V_MIN_MV  2400
+/*
+ * enum pmic_chg_interrupts: pmic interrupts
+ * @CHGVAL_IRQ: charger V between 3.3 and 7.9
+ * @CHGINVAL_IRQ: charger V outside 3.3 and 7.9
+ * @VBATDET_LOW_IRQ: VBAT < VBATDET
+ * @VCP_IRQ: VDD went below VBAT: BAT_FET is turned on
+ * @CHGILIM_IRQ: mA consumed>IMAXSEL: chgloop draws less mA
+ * @ATC_DONE_IRQ: Auto Trickle done
+ * @ATCFAIL_IRQ: Auto Trickle fail
+ * @AUTO_CHGDONE_IRQ: Auto chg done
+ * @AUTO_CHGFAIL_IRQ: time exceeded w/o reaching term current
+ * @CHGSTATE_IRQ: something happend causing a state change
+ * @FASTCHG_IRQ: trkl charging completed: moving to fastchg
+ * @CHG_END_IRQ: mA has dropped to termination current
+ * @BATTTEMP_IRQ: batt temp is out of range
+ * @CHGHOT_IRQ: the pass device is too hot
+ * @CHGTLIMIT_IRQ: unused
+ * @CHG_GONE_IRQ: charger was removed
+ * @VCPMAJOR_IRQ: vcp major
+ * @VBATDET_IRQ: VBAT >= VBATDET
+ * @BATFET_IRQ: BATFET closed
+ * @BATT_REPLACE_IRQ:
+ * @BATTCONNECT_IRQ:
+ */
+enum pmic_chg_interrupts {
+	CHGVAL_IRQ,
+	CHGINVAL_IRQ,
+	VBATDET_LOW_IRQ,
+	VCP_IRQ,
+	CHGILIM_IRQ,
+	ATC_DONE_IRQ,
+	ATCFAIL_IRQ,
+	AUTO_CHGDONE_IRQ,
+	AUTO_CHGFAIL_IRQ,
+	CHGSTATE_IRQ,
+	FASTCHG_IRQ,
+	CHG_END_IRQ,
+	BATTTEMP_IRQ,
+	CHGHOT_IRQ,
+	CHGTLIMIT_IRQ,
+	CHG_GONE_IRQ,
+	VCPMAJOR_IRQ,
+	VBATDET_IRQ,
+	BATFET_IRQ,
+	BATT_REPLACE_IRQ,
+	BATTCONNECT_IRQ,
+	PMIC_CHG_MAX_INTS
+};
+
+struct pm8058_charger {
+	struct pmic_charger_pdata *pdata;
+	struct pm8058_chip *pm_chip;
+	struct device *dev;
+
+	int pmic_chg_irq[PMIC_CHG_MAX_INTS];
+	DECLARE_BITMAP(enabled_irqs, PMIC_CHG_MAX_INTS);
+
+	struct delayed_work check_vbat_low_work;
+	struct delayed_work veoc_begin_work;
+	int waiting_for_topoff;
+	int waiting_for_veoc;
+	int current_charger_current;
+
+	struct msm_xo_voter *voter;
+	struct dentry *dent;
+};
+
+static struct pm8058_charger pm8058_chg;
+
+static int pm_chg_get_rt_status(int irq)
+{
+	int count = 3;
+	int ret;
+
+	while ((ret =
+		pm8058_irq_get_rt_status(pm8058_chg.pm_chip, irq)) == -EAGAIN
+	       && count--) {
+		dev_info(pm8058_chg.dev, "%s trycount=%d\n", __func__, count);
+		cpu_relax();
+	}
+	if (ret == -EAGAIN)
+		return 0;
+	else
+		return ret;
+}
+
+static int is_chg_plugged_in(void)
+{
+	return pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[CHGVAL_IRQ]);
+}
+
+static irqreturn_t pm8058_chg_chgval_handler(int irq, void *dev_id)
+{
+	u8 old, temp;
+	int ret;
+
+	if (!is_chg_plugged_in()) {	/*this debounces it */
+		ret = pm8058_read(pm8058_chg.pm_chip, PM8058_OVP_TEST_REG,
+					&old, 1);
+		temp = old | BIT(FORCE_OVP_OFF);
+		ret = pm8058_write(pm8058_chg.pm_chip, PM8058_OVP_TEST_REG,
+					&temp, 1);
+		temp = 0xFC;
+		ret = pm8058_write(pm8058_chg.pm_chip, PM8058_CHG_TEST,
+					&temp, 1);
+		pr_debug("%s forced wrote 0xFC to test ret=%d\n",
+							__func__, ret);
+		/* 20 ms sleep is for the VCHG to discharge */
+		msleep(20);
+		temp = 0xF0;
+		ret = pm8058_write(pm8058_chg.pm_chip, PM8058_CHG_TEST,
+					&temp, 1);
+		ret = pm8058_write(pm8058_chg.pm_chip, PM8058_OVP_TEST_REG,
+					&old, 1);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static void free_irqs(void)
+{
+	int i;
+
+	for (i = 0; i < PMIC_CHG_MAX_INTS; i++)
+		if (pm8058_chg.pmic_chg_irq[i]) {
+			free_irq(pm8058_chg.pmic_chg_irq[i], NULL);
+			pm8058_chg.pmic_chg_irq[i] = 0;
+		}
+}
+
+static int __devinit request_irqs(struct platform_device *pdev)
+{
+	struct resource *res;
+	int ret;
+
+	ret = 0;
+	bitmap_fill(pm8058_chg.enabled_irqs, PMIC_CHG_MAX_INTS);
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "CHGVAL");
+	if (res == NULL) {
+		dev_err(pm8058_chg.dev,
+			"%s:couldnt find resource CHGVAL\n", __func__);
+		goto err_out;
+	} else {
+		ret = request_any_context_irq(res->start,
+				  pm8058_chg_chgval_handler,
+				  IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+				  res->name, NULL);
+		if (ret < 0) {
+			dev_err(pm8058_chg.dev, "%s:couldnt request %d %d\n",
+				__func__, res->start, ret);
+			goto err_out;
+		} else {
+			pm8058_chg.pmic_chg_irq[CHGVAL_IRQ] = res->start;
+		}
+	}
+
+	return 0;
+
+err_out:
+	free_irqs();
+	return -EINVAL;
+}
+
+static int pm8058_usb_voltage_lower_limit(void)
+{
+	u8 temp, old;
+	int ret = 0;
+
+	temp = 0x10;
+	ret |= pm8058_write(pm8058_chg.pm_chip, PM8058_CHG_TEST, &temp, 1);
+	ret |= pm8058_read(pm8058_chg.pm_chip, PM8058_CHG_TEST, &old, 1);
+	old = old & ~BIT(IGNORE_LL);
+	temp = 0x90  | (0xF & old);
+	pr_debug("%s writing 0x%x to test\n", __func__, temp);
+	ret |= pm8058_write(pm8058_chg.pm_chip, PM8058_CHG_TEST, &temp, 1);
+
+	return ret;
+}
+
+static int __devinit pm8058_charger_probe(struct platform_device *pdev)
+{
+	struct pm8058_chip *pm_chip;
+
+	pm_chip = dev_get_drvdata(pdev->dev.parent);
+	if (pm_chip == NULL) {
+		pr_err("%s:no parent data passed in.\n", __func__);
+		return -EFAULT;
+	}
+
+	pm8058_chg.pm_chip = pm_chip;
+	pm8058_chg.pdata = pdev->dev.platform_data;
+	pm8058_chg.dev = &pdev->dev;
+
+	if (request_irqs(pdev)) {
+		pr_err("%s: couldnt register interrupts\n", __func__);
+		return -EINVAL;
+	}
+
+	if (pm8058_usb_voltage_lower_limit()) {
+		pr_err("%s: couldnt write to IGNORE_LL\n", __func__);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int __devexit pm8058_charger_remove(struct platform_device *pdev)
+{
+	free_irqs();
+	return 0;
+}
+
+static struct platform_driver pm8058_charger_driver = {
+	.probe = pm8058_charger_probe,
+	.remove = __devexit_p(pm8058_charger_remove),
+	.driver = {
+		   .name = "pm-usb-fix",
+		   .owner = THIS_MODULE,
+	},
+};
+
+static int __init pm8058_charger_init(void)
+{
+	return platform_driver_register(&pm8058_charger_driver);
+}
+
+static void __exit pm8058_charger_exit(void)
+{
+	platform_driver_unregister(&pm8058_charger_driver);
+}
+
+late_initcall(pm8058_charger_init);
+module_exit(pm8058_charger_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PMIC8058 BATTERY driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:pm8058_charger");
diff --git a/drivers/power/pm8921-bms.c b/drivers/power/pm8921-bms.c
new file mode 100644
index 0000000..6ad6a18
--- /dev/null
+++ b/drivers/power/pm8921-bms.c
@@ -0,0 +1,1178 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#define pr_fmt(fmt)	"%s: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/errno.h>
+#include <linux/mfd/pm8xxx/pm8921-bms.h>
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/interrupt.h>
+#include <linux/bitops.h>
+#include <linux/debugfs.h>
+#include <linux/slab.h>
+
+#define BMS_CONTROL		0x224
+#define BMS_OUTPUT0		0x230
+#define BMS_OUTPUT1		0x231
+#define BMS_TEST1		0x237
+#define CCADC_ANA_PARAM		0x240
+#define CCADC_DIG_PARAM		0x241
+#define CCADC_RSV		0x242
+#define CCADC_DATA0		0x244
+#define CCADC_DATA1		0x245
+#define CCADC_OFFSET_TRIM1	0x34A
+#define CCADC_OFFSET_TRIM0	0x34B
+
+#define ADC_ARB_SECP_CNTRL 0x190
+#define ADC_ARB_SECP_AMUX_CNTRL 0x191
+#define ADC_ARB_SECP_ANA_PARAM 0x192
+#define ADC_ARB_SECP_RSV 0x194
+#define ADC_ARB_SECP_DATA1 0x195
+#define ADC_ARB_SECP_DATA0 0x196
+
+enum pmic_bms_interrupts {
+	PM8921_BMS_SBI_WRITE_OK,
+	PM8921_BMS_CC_THR,
+	PM8921_BMS_VSENSE_THR,
+	PM8921_BMS_VSENSE_FOR_R,
+	PM8921_BMS_OCV_FOR_R,
+	PM8921_BMS_GOOD_OCV,
+	PM8921_BMS_VSENSE_AVG,
+	PM_BMS_MAX_INTS,
+};
+
+/**
+ * struct pm8921_bms_chip -device information
+ * @dev:	device pointer to access the parent
+ * @dent:	debugfs directory
+ * @r_sense:	batt sense resistance value
+ * @i_test:	peak current
+ * @v_failure:	battery dead voltage
+ * @fcc:	battery capacity
+ *
+ */
+struct pm8921_bms_chip {
+	struct device		*dev;
+	struct dentry		*dent;
+	unsigned int		r_sense;
+	unsigned int		i_test;
+	unsigned int		v_failure;
+	unsigned int		fcc;
+	struct single_row_lut	*fcc_temp_lut;
+	struct single_row_lut	*fcc_sf_lut;
+	struct pc_temp_ocv_lut	*pc_temp_ocv_lut;
+	struct pc_sf_lut	*pc_sf_lut;
+	struct delayed_work	calib_work;
+	unsigned int		calib_delay_ms;
+	unsigned int		pmic_bms_irq[PM_BMS_MAX_INTS];
+	DECLARE_BITMAP(enabled_irqs, PM_BMS_MAX_INTS);
+};
+
+static struct pm8921_bms_chip *the_chip;
+
+#define DEFAULT_RBATT_MOHMS		128
+#define DEFAULT_UNUSABLE_CHARGE_MAH	10
+#define DEFAULT_OCV_MICROVOLTS		3900000
+#define DEFAULT_REMAINING_CHARGE_MAH	990
+#define DEFAULT_COULUMB_COUNTER		0
+#define DEFAULT_CHARGE_CYCLES		0
+
+static int last_rbatt = -EINVAL;
+static int last_fcc = -EINVAL;
+static int last_unusable_charge = -EINVAL;
+static int last_ocv_uv = -EINVAL;
+static int last_remaining_charge = -EINVAL;
+static int last_coulumb_counter = -EINVAL;
+static int last_soc = -EINVAL;
+
+static int last_chargecycles = DEFAULT_CHARGE_CYCLES;
+static int last_charge_increase;
+
+module_param(last_rbatt, int, 0644);
+module_param(last_fcc, int, 0644);
+module_param(last_unusable_charge, int, 0644);
+module_param(last_ocv_uv, int, 0644);
+module_param(last_remaining_charge, int, 0644);
+module_param(last_coulumb_counter, int, 0644);
+module_param(last_chargecycles, int, 0644);
+module_param(last_charge_increase, int, 0644);
+
+static int pm_bms_get_rt_status(struct pm8921_bms_chip *chip, int irq_id)
+{
+	return pm8xxx_read_irq_stat(chip->dev->parent,
+					chip->pmic_bms_irq[irq_id]);
+}
+
+static void pm8921_bms_disable_irq(struct pm8921_bms_chip *chip, int interrupt)
+{
+	if (__test_and_clear_bit(interrupt, chip->enabled_irqs)) {
+		dev_dbg(chip->dev, "%d\n", chip->pmic_bms_irq[interrupt]);
+		disable_irq_nosync(chip->pmic_bms_irq[interrupt]);
+	}
+}
+
+static int pm_bms_masked_write(struct pm8921_bms_chip *chip, u16 addr,
+							u8 mask, u8 val)
+{
+	int rc;
+	u8 reg;
+
+	rc = pm8xxx_readb(chip->dev->parent, addr, &reg);
+	if (rc) {
+		pr_err("read failed addr = %03X, rc = %d\n", addr, rc);
+		return rc;
+	}
+	reg &= ~mask;
+	reg |= val & mask;
+	rc = pm8xxx_writeb(chip->dev->parent, addr, reg);
+	if (rc) {
+		pr_err("write failed addr = %03X, rc = %d\n", addr, rc);
+		return rc;
+	}
+	return 0;
+}
+
+#define SELECT_OUTPUT_DATA	0x1C
+#define SELECT_OUTPUT_TYPE_SHIFT	2
+#define OCV_FOR_RBATT		0x0
+#define VSENSE_FOR_RBATT	0x1
+#define VBATT_FOR_RBATT		0x2
+#define CC_MSB			0x3
+#define CC_LSB			0x4
+#define LAST_GOOD_OCV_VALUE	0x5
+#define VSENSE_AVG		0x6
+#define VBATT_AVG		0x7
+
+static int pm_bms_read_output_data(struct pm8921_bms_chip *chip, int type,
+						int16_t *result)
+{
+	int rc;
+	u8 reg;
+
+	if (!result) {
+		pr_err("result pointer null\n");
+		return -EINVAL;
+	}
+	*result = 0;
+	if (type < OCV_FOR_RBATT || type > VBATT_AVG) {
+		pr_err("invalid type %d asked to read\n", type);
+		return -EINVAL;
+	}
+	rc = pm_bms_masked_write(chip, BMS_CONTROL, SELECT_OUTPUT_DATA,
+					type << SELECT_OUTPUT_TYPE_SHIFT);
+	if (rc) {
+		pr_err("fail to select %d type in BMS_CONTROL rc = %d\n",
+						type, rc);
+		return rc;
+	}
+
+	rc = pm8xxx_readb(chip->dev->parent, BMS_OUTPUT0, &reg);
+	if (rc) {
+		pr_err("fail to read BMS_OUTPUT0 for type %d rc = %d\n",
+			type, rc);
+		return rc;
+	}
+	*result = reg;
+	rc = pm8xxx_readb(chip->dev->parent, BMS_OUTPUT1, &reg);
+	if (rc) {
+		pr_err("fail to read BMS_OUTPUT1 for type %d rc = %d\n",
+			type, rc);
+		return rc;
+	}
+	*result |= reg << 8;
+	pr_debug("type %d result %x", type, *result);
+	return 0;
+}
+
+#define V_PER_BIT_MUL_FACTOR	977
+#define V_PER_BIT_DIV_FACTOR	10
+#define CONV_READING(a)		(((a) * (int)V_PER_BIT_MUL_FACTOR)\
+				/V_PER_BIT_DIV_FACTOR)
+
+/* returns the signed value read from the hardware */
+static int read_cc(struct pm8921_bms_chip *chip, int *result)
+{
+	int rc;
+	uint16_t msw, lsw;
+
+	rc = pm_bms_read_output_data(chip, CC_LSB, &lsw);
+	if (rc) {
+		pr_err("fail to read CC_LSB rc = %d\n", rc);
+		return rc;
+	}
+	rc = pm_bms_read_output_data(chip, CC_MSB, &msw);
+	if (rc) {
+		pr_err("fail to read CC_MSB rc = %d\n", rc);
+		return rc;
+	}
+	*result = msw << 16 | lsw;
+	pr_debug("msw = %04x lsw = %04x cc = %d\n", msw, lsw, *result);
+	return 0;
+}
+
+static int read_last_good_ocv(struct pm8921_bms_chip *chip, uint *result)
+{
+	int rc;
+	uint16_t reading;
+
+	rc = pm_bms_read_output_data(chip, LAST_GOOD_OCV_VALUE, &reading);
+	if (rc) {
+		pr_err("fail to read LAST_GOOD_OCV_VALUE rc = %d\n", rc);
+		return rc;
+	}
+	*result = CONV_READING(reading);
+	pr_debug("raw = %04x ocv_microV = %u\n", reading, *result);
+	return 0;
+}
+
+static int read_vbatt_for_rbatt(struct pm8921_bms_chip *chip, uint *result)
+{
+	int rc;
+	uint16_t reading;
+
+	rc = pm_bms_read_output_data(chip, VBATT_FOR_RBATT, &reading);
+	if (rc) {
+		pr_err("fail to read VBATT_FOR_RBATT rc = %d\n", rc);
+		return rc;
+	}
+	*result = CONV_READING(reading);
+	pr_debug("raw = %04x vbatt_for_r_microV = %u\n", reading, *result);
+	return 0;
+}
+
+static int read_vsense_for_rbatt(struct pm8921_bms_chip *chip, uint *result)
+{
+	int rc;
+	uint16_t reading;
+
+	rc = pm_bms_read_output_data(chip, VSENSE_FOR_RBATT, &reading);
+	if (rc) {
+		pr_err("fail to read VSENSE_FOR_RBATT rc = %d\n", rc);
+		return rc;
+	}
+	*result = CONV_READING(reading);
+	pr_debug("raw = %04x vsense_for_r_microV = %u\n", reading, *result);
+	return 0;
+}
+
+static int read_ocv_for_rbatt(struct pm8921_bms_chip *chip, uint *result)
+{
+	int rc;
+	uint16_t reading;
+
+	rc = pm_bms_read_output_data(chip, OCV_FOR_RBATT, &reading);
+	if (rc) {
+		pr_err("fail to read OCV_FOR_RBATT rc = %d\n", rc);
+		return rc;
+	}
+	*result = CONV_READING(reading);
+	pr_debug("read = %04x ocv_for_r_microV = %u\n", reading, *result);
+	return 0;
+}
+
+static int linear_interpolate(int y0, int x0, int y1, int x1, int x)
+{
+	if (y0 == y1 || x == x0)
+		return y0;
+	if (x1 == x0 || x == x1)
+		return y1;
+
+	return y0 + ((y1 - y0) * (x - x0) / (x1 - x0));
+}
+
+static int interpolate_single_lut(struct single_row_lut *lut, int x)
+{
+	int i, result;
+
+	if (x < lut->x[0]) {
+		pr_err("x %d less than known range returning y = %d\n",
+							x, lut->y[0]);
+		return lut->y[0];
+	}
+	if (x > lut->x[lut->cols - 1]) {
+		pr_err("x %d more than known range returning y = %d\n",
+						x, lut->y[lut->cols - 1]);
+		return lut->y[lut->cols - 1];
+	}
+
+	for (i = 0; i < lut->cols; i++)
+		if (x <= lut->x[i])
+			break;
+	if (x == lut->x[i]) {
+		result = lut->y[i];
+	} else {
+		result = linear_interpolate(
+			lut->y[i - 1],
+			lut->x[i - 1],
+			lut->y[i],
+			lut->x[i],
+			x);
+	}
+	return result;
+}
+
+static int interpolate_fcc(struct pm8921_bms_chip *chip, int batt_temp)
+{
+	return interpolate_single_lut(chip->fcc_temp_lut, batt_temp);
+}
+
+static int interpolate_scalingfactor_fcc(struct pm8921_bms_chip *chip,
+								int cycles)
+{
+	return interpolate_single_lut(chip->fcc_sf_lut, cycles);
+}
+
+static int interpolate_scalingfactor_pc(struct pm8921_bms_chip *chip,
+				int cycles, int pc)
+{
+	int i, scalefactorrow1, scalefactorrow2, scalefactor, row1, row2;
+	int rows = chip->pc_sf_lut->rows;
+	int cols = chip->pc_sf_lut->cols;
+
+	if (pc > chip->pc_sf_lut->percent[0]) {
+		pr_err("pc %d greater than known pc ranges for sfd\n", pc);
+		row1 = 0;
+		row2 = 0;
+	}
+	if (pc < chip->pc_sf_lut->percent[rows - 1]) {
+		pr_err("pc %d less than known pc ranges for sf", pc);
+		row1 = rows - 1;
+		row2 = rows - 1;
+	}
+	for (i = 0; i < rows; i++) {
+		if (pc == chip->pc_sf_lut->percent[i]) {
+			row1 = i;
+			row2 = i;
+			break;
+		}
+		if (pc > chip->pc_sf_lut->percent[i]) {
+			row1 = i - 1;
+			row2 = i;
+			break;
+		}
+	}
+
+	if (cycles < chip->pc_sf_lut->cycles[0])
+		cycles = chip->pc_sf_lut->cycles[0];
+	if (cycles > chip->pc_sf_lut->cycles[cols - 1])
+		cycles = chip->pc_sf_lut->cycles[cols - 1];
+
+	for (i = 0; i < cols; i++)
+		if (cycles <= chip->pc_sf_lut->cycles[i])
+			break;
+	if (cycles == chip->pc_sf_lut->cycles[i]) {
+		scalefactor = linear_interpolate(
+				chip->pc_sf_lut->sf[row1][i],
+				chip->pc_sf_lut->percent[row1],
+				chip->pc_sf_lut->sf[row2][i],
+				chip->pc_sf_lut->percent[row2],
+				pc);
+		return scalefactor;
+	}
+
+	scalefactorrow1 = linear_interpolate(
+				chip->pc_sf_lut->sf[row1][i - 1],
+				chip->pc_sf_lut->cycles[i - 1],
+				chip->pc_sf_lut->sf[row1][i],
+				chip->pc_sf_lut->cycles[i],
+				cycles);
+
+	scalefactorrow2 = linear_interpolate(
+				chip->pc_sf_lut->sf[row2][i - 1],
+				chip->pc_sf_lut->cycles[i - 1],
+				chip->pc_sf_lut->sf[row2][i],
+				chip->pc_sf_lut->cycles[i],
+				cycles);
+
+	scalefactor = linear_interpolate(
+				scalefactorrow1,
+				chip->pc_sf_lut->percent[row1],
+				scalefactorrow2,
+				chip->pc_sf_lut->percent[row2],
+				pc);
+
+	return scalefactor;
+}
+
+static int interpolate_pc(struct pm8921_bms_chip *chip,
+				int batt_temp, int ocv)
+{
+	int i, j, ocvi, ocviplusone, pc = 0;
+	int rows = chip->pc_temp_ocv_lut->rows;
+	int cols = chip->pc_temp_ocv_lut->cols;
+
+	if (batt_temp < chip->pc_temp_ocv_lut->temp[0]) {
+		pr_err("batt_temp %d < known temp range for pc\n", batt_temp);
+		batt_temp = chip->pc_temp_ocv_lut->temp[0];
+	}
+	if (batt_temp > chip->pc_temp_ocv_lut->temp[cols - 1]) {
+		pr_err("batt_temp %d > known temp range for pc\n", batt_temp);
+		batt_temp = chip->pc_temp_ocv_lut->temp[cols - 1];
+	}
+
+	for (j = 0; j < cols; j++)
+		if (batt_temp <= chip->pc_temp_ocv_lut->temp[j])
+			break;
+	if (batt_temp == chip->pc_temp_ocv_lut->temp[j]) {
+		/* found an exact match for temp in the table */
+		if (ocv >= chip->pc_temp_ocv_lut->ocv[0][j])
+			return chip->pc_temp_ocv_lut->percent[0];
+		if (ocv <= chip->pc_temp_ocv_lut->ocv[rows - 1][j])
+			return chip->pc_temp_ocv_lut->percent[rows - 1];
+		for (i = 0; i < rows; i++) {
+			if (ocv >= chip->pc_temp_ocv_lut->ocv[i][j]) {
+				if (ocv == chip->pc_temp_ocv_lut->ocv[i][j])
+					return
+					chip->pc_temp_ocv_lut->percent[i];
+				pc = linear_interpolate(
+					chip->pc_temp_ocv_lut->percent[i],
+					chip->pc_temp_ocv_lut->ocv[i][j],
+					chip->pc_temp_ocv_lut->percent[i - 1],
+					chip->pc_temp_ocv_lut->ocv[i - 1][j],
+					ocv);
+				return pc;
+			}
+		}
+	}
+
+	if (ocv >= chip->pc_temp_ocv_lut->ocv[0][j])
+		return chip->pc_temp_ocv_lut->percent[0];
+	if (ocv <= chip->pc_temp_ocv_lut->ocv[rows - 1][j - 1])
+		return chip->pc_temp_ocv_lut->percent[rows - 1];
+	for (i = 0; i < rows; i++) {
+		if (ocv >= chip->pc_temp_ocv_lut->ocv[i][j - 1]
+			&& ocv <= chip->pc_temp_ocv_lut->ocv[i][j]) {
+			pc = chip->pc_temp_ocv_lut->percent[i];
+
+			if (i < rows - 1
+				&& ocv >=
+					chip->pc_temp_ocv_lut->ocv[i + 1][j - 1]
+				&& ocv <=
+					chip->pc_temp_ocv_lut->ocv[i + 1][j]) {
+				ocvi = linear_interpolate(
+					chip->pc_temp_ocv_lut->ocv[i][j - 1],
+					chip->pc_temp_ocv_lut->temp[j - 1],
+					chip->pc_temp_ocv_lut->ocv[i][j],
+					chip->pc_temp_ocv_lut->temp[j],
+					batt_temp);
+
+				ocviplusone = linear_interpolate(
+					chip->pc_temp_ocv_lut
+							->ocv[i + 1][j - 1],
+					chip->pc_temp_ocv_lut->temp[j - 1],
+					chip->pc_temp_ocv_lut->ocv[i + 1][j],
+					chip->pc_temp_ocv_lut->temp[j],
+					batt_temp);
+
+				pc = linear_interpolate(
+					chip->pc_temp_ocv_lut->percent[i],
+					ocvi,
+					chip->pc_temp_ocv_lut->percent[i + 1],
+					ocviplusone,
+					ocv);
+			}
+			return pc;
+		}
+	}
+
+	pr_err("%d ocv wasn't found for temp %d in the LUT returning pc = %d",
+							ocv, batt_temp, pc);
+	return pc;
+}
+
+static int calculate_rbatt(struct pm8921_bms_chip *chip)
+{
+	int rc;
+	unsigned int ocv, vsense, vbatt, r_batt;
+
+	rc = read_ocv_for_rbatt(chip, &ocv);
+	if (rc) {
+		pr_err("fail to read ocv_for_rbatt rc = %d\n", rc);
+		ocv = 0;
+	}
+	rc = read_vbatt_for_rbatt(chip, &vbatt);
+	if (rc) {
+		pr_err("fail to read vbatt_for_rbatt rc = %d\n", rc);
+		ocv = 0;
+	}
+	rc = read_vsense_for_rbatt(chip, &vsense);
+	if (rc) {
+		pr_err("fail to read vsense_for_rbatt rc = %d\n", rc);
+		ocv = 0;
+	}
+	if (ocv == 0
+		|| ocv == vbatt
+		|| vsense == 0) {
+		pr_warning("incorret reading ocv = %d, vbatt = %d, vsen = %d\n",
+					ocv, vbatt, vsense);
+		return -EINVAL;
+	}
+	r_batt = ((ocv - vbatt) * chip->r_sense) / vsense;
+	pr_debug("r_batt = %umilliOhms", r_batt);
+	return r_batt;
+}
+
+static int calculate_fcc(struct pm8921_bms_chip *chip, int batt_temp,
+							int chargecycles)
+{
+	int initfcc, result, scalefactor = 0;
+
+	initfcc = interpolate_fcc(chip, batt_temp);
+	pr_debug("intfcc = %umAh batt_temp = %d\n", initfcc, batt_temp);
+
+	scalefactor = interpolate_scalingfactor_fcc(chip, chargecycles);
+	pr_debug("scalefactor = %d batt_temp = %d\n", scalefactor, batt_temp);
+
+	/* Multiply the initial FCC value by the scale factor. */
+	result = (initfcc * scalefactor) / 100;
+	pr_debug("fcc mAh = %d\n", result);
+	return result;
+}
+
+static int calculate_pc(struct pm8921_bms_chip *chip, int ocv_uv, int batt_temp,
+							int chargecycles)
+{
+	int pc, scalefactor;
+
+	pc = interpolate_pc(chip, batt_temp, ocv_uv / 1000);
+	pr_debug("pc = %u for ocv = %dmicroVolts batt_temp = %d\n",
+					pc, ocv_uv, batt_temp);
+
+	scalefactor = interpolate_scalingfactor_pc(chip, chargecycles, pc);
+	pr_debug("scalefactor = %u batt_temp = %d\n", scalefactor, batt_temp);
+
+	/* Multiply the initial FCC value by the scale factor. */
+	pc = (pc * scalefactor) / 100;
+	return pc;
+}
+
+#define CC_TO_MICROVOLT(cc)		div_s64(cc * 1085069, 100000);
+#define CCMICROVOLT_TO_UVH(cc_uv)	div_s64(cc_uv * 55, 32768 * 3600)
+
+static void calculate_cc_mvh(struct pm8921_bms_chip *chip, int64_t *val,
+			int *coulumb_counter, int *update_userspace)
+{
+	int rc;
+	int64_t cc_voltage_uv, cc_uvh, cc_mah;
+
+	rc = read_cc(the_chip, coulumb_counter);
+	if (rc) {
+		*coulumb_counter = (last_coulumb_counter < 0) ?
+			DEFAULT_COULUMB_COUNTER : last_coulumb_counter;
+		pr_err("couldn't read coulumb counter err = %d assuming %d\n",
+							rc, *coulumb_counter);
+		*update_userspace = 0;
+	}
+	cc_voltage_uv = (int64_t)*coulumb_counter;
+	cc_voltage_uv = CC_TO_MICROVOLT(cc_voltage_uv);
+	pr_debug("cc_voltage_uv = %lld microvolts\n", cc_voltage_uv);
+	cc_uvh = CCMICROVOLT_TO_UVH(cc_voltage_uv);
+	pr_debug("cc_uvh = %lld micro_volt_hour\n", cc_uvh);
+	cc_mah = div_s64(cc_uvh, chip->r_sense);
+	*val = cc_mah;
+}
+
+static int calculate_state_of_charge(struct pm8921_bms_chip *chip,
+						int batt_temp, int chargecycles)
+{
+	int remaining_usable_charge, fcc, unusable_charge;
+	int remaining_charge, soc, rc, ocv, pc, coulumb_counter;
+	int rbatt, voltage_unusable_uv, pc_unusable;
+	int update_userspace = 1;
+	int64_t cc_mah;
+
+	rbatt = calculate_rbatt(chip);
+	if (rbatt < 0) {
+		rbatt = (last_rbatt < 0) ? DEFAULT_RBATT_MOHMS : last_rbatt;
+		pr_err("failed to read rbatt assuming %d\n",
+						rbatt);
+		update_userspace = 0;
+	}
+	pr_debug("rbatt = %umilliOhms", rbatt);
+
+	fcc = calculate_fcc(chip, batt_temp, chargecycles);
+	if (fcc < -EINVAL) {
+		fcc = (last_fcc < 0) ? chip->fcc : last_fcc;
+		pr_err("failed to read fcc assuming %d\n", fcc);
+		update_userspace = 0;
+	}
+	pr_debug("fcc = %umAh", fcc);
+
+	/* calculate unusable charge */
+	voltage_unusable_uv = (rbatt * chip->i_test)
+						+ (chip->v_failure * 1000);
+	pc_unusable = calculate_pc(chip, voltage_unusable_uv,
+						batt_temp, chargecycles);
+	if (pc_unusable < 0) {
+		unusable_charge = (last_unusable_charge < 0) ?
+			DEFAULT_UNUSABLE_CHARGE_MAH : last_unusable_charge;
+		pr_err("unusable_charge failed assuming %d\n", unusable_charge);
+	} else {
+		unusable_charge = (fcc * pc_unusable) / 100;
+	}
+	pr_debug("unusable_charge = %umAh at temp = %d, fcc = %umAh"
+			"unusable_voltage = %umicroVolts pc_unusable = %d\n",
+			unusable_charge, batt_temp, fcc,
+			voltage_unusable_uv, pc_unusable);
+
+	/* calculate remainging charge */
+	rc = read_last_good_ocv(chip, &ocv);
+	if (rc || ocv == 0) {
+		ocv = (last_ocv_uv < 0) ? DEFAULT_OCV_MICROVOLTS : last_ocv_uv;
+		pr_err("ocv failed assuming %d rc = %d\n", ocv, rc);
+		update_userspace = 0;
+	}
+	pc = calculate_pc(chip, ocv, batt_temp, chargecycles);
+	if (pc < 0) {
+		remaining_charge = (last_remaining_charge < 0) ?
+			DEFAULT_REMAINING_CHARGE_MAH : last_remaining_charge;
+		pr_err("calculate remaining charge failed assuming %d\n",
+				remaining_charge);
+		update_userspace = 0;
+	} else {
+		remaining_charge = (fcc * pc) / 100;
+	}
+	pr_debug("remaining_charge = %umAh ocv = %d pc = %d\n",
+			remaining_charge, ocv, pc);
+
+	/* calculate cc milli_volt_hour */
+	calculate_cc_mvh(chip, &cc_mah, &coulumb_counter, &update_userspace);
+	pr_debug("cc_mah = %lldmAh cc = %d\n", cc_mah, coulumb_counter);
+
+	/* calculate remaining usable charge */
+	remaining_usable_charge = remaining_charge - cc_mah - unusable_charge;
+	pr_debug("remaining_usable_charge = %dmAh\n", remaining_usable_charge);
+	if (remaining_usable_charge < 0) {
+		pr_err("bad rem_usb_chg cc_mah %lld, rem_chg %d unusb_chg %d\n",
+				cc_mah, remaining_charge, unusable_charge);
+		update_userspace = 0;
+	}
+
+	soc = (remaining_usable_charge * 100) / (fcc - unusable_charge);
+	if (soc > 100 || soc < 0) {
+		pr_err("bad soc rem_usb_chg %d fcc %d unusb_chg %d\n",
+				remaining_usable_charge, fcc, unusable_charge);
+		update_userspace = 0;
+	}
+	pr_debug("soc = %u%%\n", soc);
+
+	if (update_userspace) {
+		last_rbatt = rbatt;
+		last_fcc = fcc;
+		last_unusable_charge = unusable_charge;
+		last_ocv_uv = ocv;
+		last_remaining_charge = remaining_charge;
+		last_coulumb_counter = coulumb_counter;
+		last_soc = soc;
+	}
+	return soc;
+}
+
+int pm8921_bms_get_percent_charge(void)
+{
+	/* TODO get batt_temp from ADC */
+	int batt_temp = 73;
+
+	return calculate_state_of_charge(the_chip,
+					batt_temp, last_chargecycles);
+}
+EXPORT_SYMBOL_GPL(pm8921_bms_get_percent_charge);
+
+static int start_percent;
+static int end_percent;
+void pm8921_bms_charging_began(void)
+{
+	/* TODO get batt_temp from ADC */
+	int batt_temp = 73;
+
+	start_percent = calculate_state_of_charge(the_chip,
+					batt_temp, last_chargecycles);
+	pr_debug("start_percent = %u%%\n", start_percent);
+}
+EXPORT_SYMBOL_GPL(pm8921_bms_charging_began);
+
+void pm8921_bms_charging_end(void)
+{
+	/* TODO get batt_temp from ADC */
+	int batt_temp = 73;
+
+	end_percent = calculate_state_of_charge(the_chip,
+					batt_temp, last_chargecycles);
+	if (end_percent > start_percent) {
+		last_charge_increase = end_percent - start_percent;
+		if (last_charge_increase > 100) {
+			last_chargecycles++;
+			last_charge_increase = last_charge_increase % 100;
+		}
+	}
+	pr_debug("end_percent = %u%% last_charge_increase = %d"
+			"last_chargecycles = %d\n",
+			end_percent,
+			last_charge_increase,
+			last_chargecycles);
+}
+EXPORT_SYMBOL_GPL(pm8921_bms_charging_end);
+
+static irqreturn_t pm8921_bms_sbi_write_ok_handler(int irq, void *data)
+{
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t pm8921_bms_cc_thr_handler(int irq, void *data)
+{
+	return IRQ_HANDLED;
+}
+static irqreturn_t pm8921_bms_vsense_thr_handler(int irq, void *data)
+{
+	return IRQ_HANDLED;
+}
+static irqreturn_t pm8921_bms_vsense_for_r_handler(int irq, void *data)
+{
+	return IRQ_HANDLED;
+}
+static irqreturn_t pm8921_bms_ocv_for_r_handler(int irq, void *data)
+{
+	return IRQ_HANDLED;
+}
+static irqreturn_t pm8921_bms_good_ocv_handler(int irq, void *data)
+{
+	return IRQ_HANDLED;
+}
+static irqreturn_t pm8921_bms_vsense_avg_handler(int irq, void *data)
+{
+	return IRQ_HANDLED;
+}
+
+struct pm_bms_irq_init_data {
+	unsigned int	irq_id;
+	char		*name;
+	unsigned long	flags;
+	irqreturn_t	(*handler)(int, void *);
+};
+
+#define BMS_IRQ(_id, _flags, _handler) \
+{ \
+	.irq_id		= _id, \
+	.name		= #_id, \
+	.flags		= _flags, \
+	.handler	= _handler, \
+}
+
+struct pm_bms_irq_init_data bms_irq_data[] = {
+	BMS_IRQ(PM8921_BMS_SBI_WRITE_OK, IRQF_TRIGGER_RISING,
+				pm8921_bms_sbi_write_ok_handler),
+	BMS_IRQ(PM8921_BMS_CC_THR, IRQF_TRIGGER_RISING,
+				pm8921_bms_cc_thr_handler),
+	BMS_IRQ(PM8921_BMS_VSENSE_THR, IRQF_TRIGGER_RISING,
+				pm8921_bms_vsense_thr_handler),
+	BMS_IRQ(PM8921_BMS_VSENSE_FOR_R, IRQF_TRIGGER_RISING,
+				pm8921_bms_vsense_for_r_handler),
+	BMS_IRQ(PM8921_BMS_OCV_FOR_R, IRQF_TRIGGER_RISING,
+				pm8921_bms_ocv_for_r_handler),
+	BMS_IRQ(PM8921_BMS_GOOD_OCV, IRQF_TRIGGER_RISING,
+				pm8921_bms_good_ocv_handler),
+	BMS_IRQ(PM8921_BMS_VSENSE_AVG, IRQF_TRIGGER_RISING,
+				pm8921_bms_vsense_avg_handler),
+};
+
+static void free_irqs(struct pm8921_bms_chip *chip)
+{
+	int i;
+
+	for (i = 0; i < PM_BMS_MAX_INTS; i++)
+		if (chip->pmic_bms_irq[i]) {
+			free_irq(chip->pmic_bms_irq[i], NULL);
+			chip->pmic_bms_irq[i] = 0;
+		}
+}
+
+static int __devinit request_irqs(struct pm8921_bms_chip *chip,
+					struct platform_device *pdev)
+{
+	struct resource *res;
+	int ret, i;
+
+	ret = 0;
+	bitmap_fill(chip->enabled_irqs, PM_BMS_MAX_INTS);
+
+	for (i = 0; i < ARRAY_SIZE(bms_irq_data); i++) {
+		res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+				bms_irq_data[i].name);
+		if (res == NULL) {
+			pr_err("couldn't find %s\n", bms_irq_data[i].name);
+			goto err_out;
+		}
+		ret = request_irq(res->start, bms_irq_data[i].handler,
+			bms_irq_data[i].flags,
+			bms_irq_data[i].name, chip);
+		if (ret < 0) {
+			pr_err("couldn't request %d (%s) %d\n", res->start,
+					bms_irq_data[i].name, ret);
+			goto err_out;
+		}
+		chip->pmic_bms_irq[bms_irq_data[i].irq_id] = res->start;
+		pm8921_bms_disable_irq(chip, bms_irq_data[i].irq_id);
+	}
+	return 0;
+
+err_out:
+	free_irqs(chip);
+	return -EINVAL;
+}
+
+#define EN_BMS_BIT	BIT(7)
+#define EN_PON_HS_BIT	BIT(0)
+static int __devinit pm8921_bms_hw_init(struct pm8921_bms_chip *chip)
+{
+	int rc;
+
+	rc = pm_bms_masked_write(chip, BMS_CONTROL,
+			EN_BMS_BIT | EN_PON_HS_BIT, EN_BMS_BIT | EN_PON_HS_BIT);
+	if (rc) {
+		pr_err("failed to enable pon and bms addr = %d %d",
+				BMS_CONTROL, rc);
+	}
+
+	return 0;
+}
+
+enum {
+	CALC_RBATT,
+	CALC_FCC,
+	CALC_PC,
+	CALC_SOC,
+};
+
+static int test_batt_temp = 5;
+static int test_chargecycle = 150;
+static int test_ocv = 3900000;
+enum {
+	TEST_BATT_TEMP,
+	TEST_CHARGE_CYCLE,
+	TEST_OCV,
+};
+static int get_test_param(void *data, u64 * val)
+{
+	switch ((int)data) {
+	case TEST_BATT_TEMP:
+		*val = test_batt_temp;
+		break;
+	case TEST_CHARGE_CYCLE:
+		*val = test_chargecycle;
+		break;
+	case TEST_OCV:
+		*val = test_ocv;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+static int set_test_param(void *data, u64  val)
+{
+	switch ((int)data) {
+	case TEST_BATT_TEMP:
+		test_batt_temp = (int)val;
+		break;
+	case TEST_CHARGE_CYCLE:
+		test_chargecycle = (int)val;
+		break;
+	case TEST_OCV:
+		test_ocv = (int)val;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(temp_fops, get_test_param, set_test_param, "%llu\n");
+
+static int get_calc(void *data, u64 * val)
+{
+	int param = (int)data;
+	int ret = 0;
+
+	*val = 0;
+
+	/* global irq number passed in via data */
+	switch (param) {
+	case CALC_RBATT:
+		*val = calculate_rbatt(the_chip);
+		break;
+	case CALC_FCC:
+		*val = calculate_fcc(the_chip, test_batt_temp,
+							test_chargecycle);
+		break;
+	case CALC_PC:
+		*val = calculate_pc(the_chip, test_ocv, test_batt_temp,
+							test_chargecycle);
+		break;
+	case CALC_SOC:
+		*val = calculate_state_of_charge(the_chip,
+					test_batt_temp, test_chargecycle);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+	return ret;
+}
+DEFINE_SIMPLE_ATTRIBUTE(calc_fops, get_calc, NULL, "%llu\n");
+
+static int get_reading(void *data, u64 * val)
+{
+	int param = (int)data;
+	int ret = 0;
+
+	*val = 0;
+
+	/* global irq number passed in via data */
+	switch (param) {
+	case CC_MSB:
+	case CC_LSB:
+		read_cc(the_chip, (int *)val);
+		break;
+	case LAST_GOOD_OCV_VALUE:
+		read_last_good_ocv(the_chip, (uint *)val);
+		break;
+	case VBATT_FOR_RBATT:
+		read_vbatt_for_rbatt(the_chip, (uint *)val);
+		break;
+	case VSENSE_FOR_RBATT:
+		read_vsense_for_rbatt(the_chip, (uint *)val);
+		break;
+	case OCV_FOR_RBATT:
+		read_ocv_for_rbatt(the_chip, (uint *)val);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+	return ret;
+}
+DEFINE_SIMPLE_ATTRIBUTE(reading_fops, get_reading, NULL, "%llu\n");
+
+static int get_rt_status(void *data, u64 * val)
+{
+	int i = (int)data;
+	int ret;
+
+	/* global irq number passed in via data */
+	ret = pm_bms_get_rt_status(the_chip, i);
+	*val = ret;
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(rt_fops, get_rt_status, NULL, "%llu\n");
+
+static int get_reg(void *data, u64 * val)
+{
+	int addr = (int)data;
+	int ret;
+	u8 temp;
+
+	ret = pm8xxx_readb(the_chip->dev->parent, addr, &temp);
+	if (ret) {
+		pr_err("pm8xxx_readb to %x value = %d errored = %d\n",
+			addr, temp, ret);
+		return -EAGAIN;
+	}
+	*val = temp;
+	return 0;
+}
+
+static int set_reg(void *data, u64 val)
+{
+	int addr = (int)data;
+	int ret;
+	u8 temp;
+
+	temp = (u8) val;
+	ret = pm8xxx_writeb(the_chip->dev->parent, addr, temp);
+	if (ret) {
+		pr_err("pm8xxx_writeb to %x value = %d errored = %d\n",
+			addr, temp, ret);
+		return -EAGAIN;
+	}
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(reg_fops, get_reg, set_reg, "0x%02llx\n");
+
+static void create_debugfs_entries(struct pm8921_bms_chip *chip)
+{
+	int i;
+
+	chip->dent = debugfs_create_dir("pm8921_bms", NULL);
+
+	if (IS_ERR(chip->dent)) {
+		pr_err("pmic bms couldnt create debugfs dir\n");
+		return;
+	}
+
+	debugfs_create_file("BMS_CONTROL", 0644, chip->dent,
+			(void *)BMS_CONTROL, &reg_fops);
+	debugfs_create_file("BMS_OUTPUT0", 0644, chip->dent,
+			(void *)BMS_OUTPUT0, &reg_fops);
+	debugfs_create_file("BMS_OUTPUT1", 0644, chip->dent,
+			(void *)BMS_OUTPUT1, &reg_fops);
+	debugfs_create_file("BMS_TEST1", 0644, chip->dent,
+			(void *)BMS_TEST1, &reg_fops);
+	debugfs_create_file("CCADC_ANA_PARAM", 0644, chip->dent,
+			(void *)CCADC_ANA_PARAM, &reg_fops);
+	debugfs_create_file("CCADC_DIG_PARAM", 0644, chip->dent,
+			(void *)CCADC_DIG_PARAM, &reg_fops);
+	debugfs_create_file("CCADC_RSV", 0644, chip->dent,
+			(void *)CCADC_RSV, &reg_fops);
+	debugfs_create_file("CCADC_DATA0", 0644, chip->dent,
+			(void *)CCADC_DATA0, &reg_fops);
+	debugfs_create_file("CCADC_DATA1", 0644, chip->dent,
+			(void *)CCADC_DATA1, &reg_fops);
+	debugfs_create_file("CCADC_OFFSET_TRIM1", 0644, chip->dent,
+			(void *)CCADC_OFFSET_TRIM1, &reg_fops);
+	debugfs_create_file("CCADC_OFFSET_TRIM0", 0644, chip->dent,
+			(void *)CCADC_OFFSET_TRIM0, &reg_fops);
+
+	debugfs_create_file("test_batt_temp", 0644, chip->dent,
+				(void *)TEST_BATT_TEMP, &temp_fops);
+	debugfs_create_file("test_chargecycle", 0644, chip->dent,
+				(void *)TEST_CHARGE_CYCLE, &temp_fops);
+	debugfs_create_file("test_ocv", 0644, chip->dent,
+				(void *)TEST_OCV, &temp_fops);
+
+	debugfs_create_file("read_cc", 0644, chip->dent,
+				(void *)CC_MSB, &reading_fops);
+	debugfs_create_file("read_last_good_ocv", 0644, chip->dent,
+				(void *)LAST_GOOD_OCV_VALUE, &reading_fops);
+	debugfs_create_file("read_vbatt_for_rbatt", 0644, chip->dent,
+				(void *)VBATT_FOR_RBATT, &reading_fops);
+	debugfs_create_file("read_vsense_for_rbatt", 0644, chip->dent,
+				(void *)VSENSE_FOR_RBATT, &reading_fops);
+	debugfs_create_file("read_ocv_for_rbatt", 0644, chip->dent,
+				(void *)OCV_FOR_RBATT, &reading_fops);
+
+	debugfs_create_file("show_rbatt", 0644, chip->dent,
+				(void *)CALC_RBATT, &calc_fops);
+	debugfs_create_file("show_fcc", 0644, chip->dent,
+				(void *)CALC_FCC, &calc_fops);
+	debugfs_create_file("show_pc", 0644, chip->dent,
+				(void *)CALC_PC, &calc_fops);
+	debugfs_create_file("show_soc", 0644, chip->dent,
+				(void *)CALC_SOC, &calc_fops);
+
+	for (i = 0; i < ARRAY_SIZE(bms_irq_data); i++) {
+		if (chip->pmic_bms_irq[bms_irq_data[i].irq_id])
+			debugfs_create_file(bms_irq_data[i].name, 0444,
+				chip->dent,
+				(void *)bms_irq_data[i].irq_id,
+				&rt_fops);
+	}
+}
+
+static void calibrate_work(struct work_struct *work)
+{
+	/* TODO */
+}
+
+static int __devinit pm8921_bms_probe(struct platform_device *pdev)
+{
+	int rc = 0;
+	struct pm8921_bms_chip *chip;
+	const struct pm8921_bms_platform_data *pdata
+				= pdev->dev.platform_data;
+	if (!pdata) {
+		pr_err("missing platform data\n");
+		return -EINVAL;
+	}
+
+	chip = kzalloc(sizeof(struct pm8921_bms_chip),
+					GFP_KERNEL);
+	if (!chip) {
+		pr_err("Cannot allocate pm_bms_chip\n");
+		return -ENOMEM;
+	}
+
+	chip->dev = &pdev->dev;
+	chip->r_sense = pdata->r_sense;
+	chip->i_test = pdata->i_test;
+	chip->v_failure = pdata->v_failure;
+	chip->calib_delay_ms = pdata->calib_delay_ms;
+	chip->fcc = pdata->batt_data->fcc;
+
+	chip->fcc_temp_lut = pdata->batt_data->fcc_temp_lut;
+	chip->fcc_sf_lut = pdata->batt_data->fcc_sf_lut;
+	chip->pc_temp_ocv_lut = pdata->batt_data->pc_temp_ocv_lut;
+	chip->pc_sf_lut = pdata->batt_data->pc_sf_lut;
+
+	rc = pm8921_bms_hw_init(chip);
+	if (rc) {
+		pr_err("couldn't init hardware rc = %d\n", rc);
+		goto free_chip;
+	}
+
+	rc = request_irqs(chip, pdev);
+	if (rc) {
+		pr_err("couldn't register interrupts rc = %d\n", rc);
+		goto free_chip;
+	}
+
+	platform_set_drvdata(pdev, chip);
+	the_chip = chip;
+	create_debugfs_entries(chip);
+
+	INIT_DELAYED_WORK(&chip->calib_work, calibrate_work);
+	schedule_delayed_work(&chip->calib_work,
+			round_jiffies_relative(msecs_to_jiffies
+			(chip->calib_delay_ms)));
+	return 0;
+
+free_chip:
+	kfree(chip);
+	return rc;
+}
+
+static int __devexit pm8921_bms_remove(struct platform_device *pdev)
+{
+	struct pm8921_bms_chip *chip = platform_get_drvdata(pdev);
+
+	free_irqs(chip);
+	platform_set_drvdata(pdev, NULL);
+	the_chip = NULL;
+	kfree(chip);
+	return 0;
+}
+
+static struct platform_driver pm8921_bms_driver = {
+	.probe	= pm8921_bms_probe,
+	.remove	= __devexit_p(pm8921_bms_remove),
+	.driver	= {
+		.name	= PM8921_BMS_DEV_NAME,
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init pm8921_bms_init(void)
+{
+	return platform_driver_register(&pm8921_bms_driver);
+}
+
+static void __exit pm8921_bms_exit(void)
+{
+	platform_driver_unregister(&pm8921_bms_driver);
+}
+
+late_initcall(pm8921_bms_init);
+module_exit(pm8921_bms_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PMIC8921 bms driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:" PM8921_BMS_DEV_NAME);
diff --git a/drivers/power/pm8921-charger.c b/drivers/power/pm8921-charger.c
new file mode 100644
index 0000000..6775fba
--- /dev/null
+++ b/drivers/power/pm8921-charger.c
@@ -0,0 +1,1560 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#define pr_fmt(fmt)	"%s: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/errno.h>
+#include <linux/mfd/pm8xxx/pm8921-charger.h>
+#include <linux/mfd/pm8xxx/pm8921-bms.h>
+#include <linux/mfd/pm8921-adc.h>
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/interrupt.h>
+#include <linux/power_supply.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/workqueue.h>
+#include <linux/debugfs.h>
+#include <linux/slab.h>
+
+#include <mach/msm_xo.h>
+#include <mach/msm_hsusb.h>
+
+#define CHG_BUCK_CLOCK_CTRL	0x14
+
+#define PBL_ACCESS1		0x04
+#define PBL_ACCESS2		0x05
+#define SYS_CONFIG_1		0x06
+#define SYS_CONFIG_2		0x07
+#define CHG_CNTRL		0x204
+#define CHG_IBAT_MAX		0x205
+#define CHG_TEST		0x206
+#define CHG_BUCK_CTRL_TEST1	0x207
+#define CHG_BUCK_CTRL_TEST2	0x208
+#define CHG_BUCK_CTRL_TEST3	0x209
+#define COMPARATOR_OVERRIDE	0x20A
+#define PSI_TXRX_SAMPLE_DATA_0	0x20B
+#define PSI_TXRX_SAMPLE_DATA_1	0x20C
+#define PSI_TXRX_SAMPLE_DATA_2	0x20D
+#define PSI_TXRX_SAMPLE_DATA_3	0x20E
+#define PSI_CONFIG_STATUS	0x20F
+#define CHG_IBAT_SAFE		0x210
+#define CHG_ITRICKLE		0x211
+#define CHG_CNTRL_2		0x212
+#define CHG_VBAT_DET		0x213
+#define CHG_VTRICKLE		0x214
+#define CHG_ITERM		0x215
+#define CHG_CNTRL_3		0x216
+#define CHG_VIN_MIN		0x217
+#define CHG_TWDOG		0x218
+#define CHG_TTRKL_MAX		0x219
+#define CHG_TEMP_THRESH		0x21A
+#define CHG_TCHG_MAX		0x21B
+#define USB_OVP_CONTROL		0x21C
+#define DC_OVP_CONTROL		0x21D
+#define USB_OVP_TEST		0x21E
+#define DC_OVP_TEST		0x21F
+#define CHG_VDD_MAX		0x220
+#define CHG_VDD_SAFE		0x221
+#define CHG_VBAT_BOOT_THRESH	0x222
+#define USB_OVP_TRIM		0x355
+#define BUCK_CONTROL_TRIM1	0x356
+#define BUCK_CONTROL_TRIM2	0x357
+#define BUCK_CONTROL_TRIM3	0x358
+#define BUCK_CONTROL_TRIM4	0x359
+#define CHG_DEFAULTS_TRIM	0x35A
+#define CHG_ITRIM		0x35B
+#define CHG_TTRIM		0x35C
+#define CHG_COMP_OVR		0x20A
+
+enum chg_fsm_state {
+	FSM_STATE_OFF_0 = 0,
+	FSM_STATE_BATFETDET_START_12 = 12,
+	FSM_STATE_BATFETDET_END_16 = 16,
+	FSM_STATE_ON_CHG_HIGHI_1 = 1,
+	FSM_STATE_ATC_2A = 2,
+	FSM_STATE_ATC_2B = 18,
+	FSM_STATE_ON_BAT_3 = 3,
+	FSM_STATE_ATC_FAIL_4 = 4 ,
+	FSM_STATE_DELAY_5 = 5,
+	FSM_STATE_ON_CHG_AND_BAT_6 = 6,
+	FSM_STATE_FAST_CHG_7 = 7,
+	FSM_STATE_TRKL_CHG_8 = 8,
+	FSM_STATE_CHG_FAIL_9 = 9,
+	FSM_STATE_EOC_10 = 10,
+	FSM_STATE_ON_CHG_VREGOK_11 = 11,
+	FSM_STATE_ATC_PAUSE_13 = 13,
+	FSM_STATE_FAST_CHG_PAUSE_14 = 14,
+	FSM_STATE_TRKL_CHG_PAUSE_15 = 15,
+	FSM_STATE_START_BOOT = 20,
+	FSM_STATE_FLCB_VREGOK = 21,
+	FSM_STATE_FLCB = 22,
+};
+
+enum pmic_chg_interrupts {
+	USBIN_VALID_IRQ = 0,
+	USBIN_OV_IRQ,
+	BATT_INSERTED_IRQ,
+	VBATDET_LOW_IRQ,
+	USBIN_UV_IRQ,
+	VBAT_OV_IRQ,
+	CHGWDOG_IRQ,
+	VCP_IRQ,
+	ATCDONE_IRQ,
+	ATCFAIL_IRQ,
+	CHGDONE_IRQ,
+	CHGFAIL_IRQ,
+	CHGSTATE_IRQ,
+	LOOP_CHANGE_IRQ,
+	FASTCHG_IRQ,
+	TRKLCHG_IRQ,
+	BATT_REMOVED_IRQ,
+	BATTTEMP_HOT_IRQ,
+	CHGHOT_IRQ,
+	BATTTEMP_COLD_IRQ,
+	CHG_GONE_IRQ,
+	BAT_TEMP_OK_IRQ,
+	COARSE_DET_LOW_IRQ,
+	VDD_LOOP_IRQ,
+	VREG_OV_IRQ,
+	VBATDET_IRQ,
+	BATFET_IRQ,
+	PSI_IRQ,
+	DCIN_VALID_IRQ,
+	DCIN_OV_IRQ,
+	DCIN_UV_IRQ,
+	PM_CHG_MAX_INTS,
+};
+
+struct bms_notify {
+	int			is_charging;
+	struct	work_struct	work;
+};
+
+/**
+ * struct pm8921_chg_chip -device information
+ * @dev:			device pointer to access the parent
+ * @is_usb_path_used:		indicates whether USB charging is used at all
+ * @is_usb_path_used:		indicates whether DC charging is used at all
+ * @usb_present:		present status of usb
+ * @dc_present:			present status of dc
+ * @usb_charger_current:	usb current to charge the battery with used when
+ *				the usb path is enabled or charging is resumed
+ * @safety_time:		max time for which charging will happen
+ * @update_time:		how frequently the userland needs to be updated
+ * @max_voltage:		the max volts the batt should be charged up to
+ * @min_voltage:		the min battery voltage before turning the FETon
+ * @resume_voltage:		the voltage at which the battery should resume
+ *				charging
+ * @term_current:		The charging based term current
+ *
+ */
+struct pm8921_chg_chip {
+	struct device		*dev;
+	unsigned int		usb_present;
+	unsigned int		dc_present;
+	unsigned int		usb_charger_current;
+	unsigned int		pmic_chg_irq[PM_CHG_MAX_INTS];
+	unsigned int		safety_time;
+	unsigned int		update_time;
+	unsigned int		max_voltage;
+	unsigned int		min_voltage;
+	unsigned int		resume_voltage;
+	unsigned int		term_current;
+	unsigned int		vbat_channel;
+	struct power_supply	usb_psy;
+	struct power_supply	dc_psy;
+	struct power_supply	batt_psy;
+	struct dentry		*dent;
+	struct bms_notify	bms_notify;
+	DECLARE_BITMAP(enabled_irqs, PM_CHG_MAX_INTS);
+};
+
+static int charging_disabled;
+
+static struct pm8921_chg_chip *the_chip;
+
+static int pm_chg_masked_write(struct pm8921_chg_chip *chip, u16 addr,
+							u8 mask, u8 val)
+{
+	int rc;
+	u8 reg;
+
+	rc = pm8xxx_readb(chip->dev->parent, addr, &reg);
+	if (rc) {
+		pr_err("pm8xxx_readb failed: addr=%03X, rc=%d\n", addr, rc);
+		return rc;
+	}
+	reg &= ~mask;
+	reg |= val & mask;
+	rc = pm8xxx_writeb(chip->dev->parent, addr, reg);
+	if (rc) {
+		pr_err("pm8xxx_writeb failed: addr=%03X, rc=%d\n", addr, rc);
+		return rc;
+	}
+	return 0;
+}
+
+#define CAPTURE_FSM_STATE_CMD	0xC2
+#define READ_BANK_7		0x70
+#define READ_BANK_4		0x40
+static int pm_chg_get_fsm_state(struct pm8921_chg_chip *chip)
+{
+	u8 temp;
+	int err, ret = 0;
+
+	temp = CAPTURE_FSM_STATE_CMD;
+	err = pm8xxx_writeb(chip->dev->parent, CHG_TEST, temp);
+	if (err) {
+		pr_err("Error %d writing %d to addr %d\n", err, temp, CHG_TEST);
+		return err;
+	}
+
+	temp = READ_BANK_7;
+	err = pm8xxx_writeb(chip->dev->parent, CHG_TEST, temp);
+	if (err) {
+		pr_err("Error %d writing %d to addr %d\n", err, temp, CHG_TEST);
+		return err;
+	}
+
+	err = pm8xxx_readb(chip->dev->parent, CHG_TEST, &temp);
+	if (err) {
+		pr_err("pm8xxx_readb fail: addr=%03X, rc=%d\n", CHG_TEST, err);
+		return err;
+	}
+	/* get the lower 4 bits */
+	ret = temp & 0xF;
+
+	temp = READ_BANK_4;
+	err = pm8xxx_writeb(chip->dev->parent, CHG_TEST, temp);
+	if (err) {
+		pr_err("Error %d writing %d to addr %d\n", err, temp, CHG_TEST);
+		return err;
+	}
+
+	err = pm8xxx_readb(chip->dev->parent, CHG_TEST, &temp);
+	if (err) {
+		pr_err("pm8xxx_readb fail: addr=%03X, rc=%d\n", CHG_TEST, err);
+		return err;
+	}
+	/* get the upper 1 bit */
+	ret |= (temp & 0x1) << 4;
+	return  ret;
+}
+
+#define CHG_USB_SUSPEND_BIT  BIT(2)
+static int pm_chg_usb_suspend_enable(struct pm8921_chg_chip *chip, int enable)
+{
+	return pm_chg_masked_write(chip, CHG_CNTRL_3, CHG_USB_SUSPEND_BIT,
+			enable ? CHG_USB_SUSPEND_BIT : 0);
+}
+
+#define CHG_EN_BIT	BIT(7)
+static int pm_chg_auto_enable(struct pm8921_chg_chip *chip, int enable)
+{
+	return pm_chg_masked_write(chip, CHG_CNTRL_3, CHG_EN_BIT,
+				enable ? CHG_EN_BIT : 0);
+}
+
+#define CHG_CHARGE_DIS_BIT	BIT(1)
+static int pm_chg_charge_dis(struct pm8921_chg_chip *chip, int disable)
+{
+	return pm_chg_masked_write(chip, CHG_CNTRL, CHG_CHARGE_DIS_BIT,
+				disable ? CHG_CHARGE_DIS_BIT : 0);
+}
+
+#define PM8921_CHG_V_MIN_MV	3240
+#define PM8921_CHG_V_STEP_MV	20
+#define PM8921_CHG_VDDMAX_MAX	4500
+#define PM8921_CHG_VDDMAX_MIN	3400
+#define PM8921_CHG_V_MASK	0x7F
+static int pm_chg_vddmax_set(struct pm8921_chg_chip *chip, int voltage)
+{
+	u8 temp;
+
+	if (voltage < PM8921_CHG_VDDMAX_MIN
+			|| voltage > PM8921_CHG_VDDMAX_MAX) {
+		pr_err("bad mV=%d asked to set\n", voltage);
+		return -EINVAL;
+	}
+	temp = (voltage - PM8921_CHG_V_MIN_MV) / PM8921_CHG_V_STEP_MV;
+	pr_debug("voltage=%d setting %02x\n", voltage, temp);
+	return pm_chg_masked_write(chip, CHG_VDD_MAX, PM8921_CHG_V_MASK, temp);
+}
+
+#define PM8921_CHG_VDDSAFE_MIN	3400
+#define PM8921_CHG_VDDSAFE_MAX	4500
+static int pm_chg_vddsafe_set(struct pm8921_chg_chip *chip, int voltage)
+{
+	u8 temp;
+
+	if (voltage < PM8921_CHG_VDDSAFE_MIN
+			|| voltage > PM8921_CHG_VDDSAFE_MAX) {
+		pr_err("bad mV=%d asked to set\n", voltage);
+		return -EINVAL;
+	}
+	temp = (voltage - PM8921_CHG_V_MIN_MV) / PM8921_CHG_V_STEP_MV;
+	pr_debug("voltage=%d setting %02x\n", voltage, temp);
+	return pm_chg_masked_write(chip, CHG_VDD_SAFE, PM8921_CHG_V_MASK, temp);
+}
+
+#define PM8921_CHG_VBATDET_MIN	3240
+#define PM8921_CHG_VBATDET_MAX	5780
+static int pm_chg_vbatdet_set(struct pm8921_chg_chip *chip, int voltage)
+{
+	u8 temp;
+
+	if (voltage < PM8921_CHG_VBATDET_MIN
+			|| voltage > PM8921_CHG_VBATDET_MAX) {
+		pr_err("bad mV=%d asked to set\n", voltage);
+		return -EINVAL;
+	}
+	temp = (voltage - PM8921_CHG_V_MIN_MV) / PM8921_CHG_V_STEP_MV;
+	pr_debug("voltage=%d setting %02x\n", voltage, temp);
+	return pm_chg_masked_write(chip, CHG_VBAT_DET, PM8921_CHG_V_MASK, temp);
+}
+
+#define PM8921_CHG_IBATMAX_MIN	325
+#define PM8921_CHG_IBATMAX_MAX	2000
+#define PM8921_CHG_I_MIN_MA	225
+#define PM8921_CHG_I_STEP_MA	50
+#define PM8921_CHG_I_MASK	0x3F
+static int pm_chg_ibatmax_set(struct pm8921_chg_chip *chip, int chg_current)
+{
+	u8 temp;
+
+	if (chg_current < PM8921_CHG_IBATMAX_MIN
+			|| chg_current > PM8921_CHG_IBATMAX_MAX) {
+		pr_err("bad mA=%d asked to set\n", chg_current);
+		return -EINVAL;
+	}
+	temp = (chg_current - PM8921_CHG_I_MIN_MA) / PM8921_CHG_I_STEP_MA;
+	return pm_chg_masked_write(chip, CHG_IBAT_MAX, PM8921_CHG_I_MASK, temp);
+}
+
+#define PM8921_CHG_IBATSAFE_MIN	225
+#define PM8921_CHG_IBATSAFE_MAX	3375
+static int pm_chg_ibatsafe_set(struct pm8921_chg_chip *chip, int chg_current)
+{
+	u8 temp;
+
+	if (chg_current < PM8921_CHG_IBATSAFE_MIN
+			|| chg_current > PM8921_CHG_IBATSAFE_MAX) {
+		pr_err("bad mA=%d asked to set\n", chg_current);
+		return -EINVAL;
+	}
+	temp = (chg_current - PM8921_CHG_I_MIN_MA) / PM8921_CHG_I_STEP_MA;
+	return pm_chg_masked_write(chip, CHG_IBAT_SAFE,
+						PM8921_CHG_I_MASK, temp);
+}
+
+#define PM8921_CHG_ITERM_MIN		50
+#define PM8921_CHG_ITERM_MAX		200
+#define PM8921_CHG_ITERM_MIN_MA		50
+#define PM8921_CHG_ITERM_STEP_MA	10
+#define PM8921_CHG_ITERM_MASK		0xF
+static int pm_chg_iterm_set(struct pm8921_chg_chip *chip, int chg_current)
+{
+	u8 temp;
+
+	if (chg_current < PM8921_CHG_ITERM_MIN
+			|| chg_current > PM8921_CHG_ITERM_MAX) {
+		pr_err("bad mA=%d asked to set\n", chg_current);
+		return -EINVAL;
+	}
+
+	temp = (chg_current - PM8921_CHG_ITERM_MIN_MA)
+				/ PM8921_CHG_ITERM_STEP_MA;
+	return pm_chg_masked_write(chip, CHG_IBAT_SAFE, PM8921_CHG_ITERM_MASK,
+					 temp);
+}
+
+#define PM8921_CHG_IUSB_MASK 0x1C
+#define PM8921_CHG_IUSB_MAX  7
+#define PM8921_CHG_IUSB_MIN  0
+static int pm_chg_iusbmax_set(struct pm8921_chg_chip *chip, int chg_current)
+{
+	u8 temp;
+
+	if (chg_current < PM8921_CHG_IUSB_MIN
+			|| chg_current > PM8921_CHG_IUSB_MAX) {
+		pr_err("bad mA=%d asked to set\n", chg_current);
+		return -EINVAL;
+	}
+	temp = chg_current << 2;
+	return pm_chg_masked_write(chip, PBL_ACCESS2, PM8921_CHG_IUSB_MASK,
+					 temp);
+}
+
+#define PM8921_CHG_WD_MASK 0x1F
+static int pm_chg_disable_wd(struct pm8921_chg_chip *chip)
+{
+	/* writing 0 to the wd timer disables it */
+	return pm_chg_masked_write(chip, CHG_TWDOG, PM8921_CHG_WD_MASK,
+					 0);
+}
+
+static void pm8921_chg_enable_irq(struct pm8921_chg_chip *chip, int interrupt)
+{
+	if (!__test_and_set_bit(interrupt, chip->enabled_irqs)) {
+		dev_dbg(chip->dev, "%d\n", chip->pmic_chg_irq[interrupt]);
+		enable_irq(chip->pmic_chg_irq[interrupt]);
+	}
+}
+
+static void pm8921_chg_disable_irq(struct pm8921_chg_chip *chip, int interrupt)
+{
+	if (__test_and_clear_bit(interrupt, chip->enabled_irqs)) {
+		dev_dbg(chip->dev, "%d\n", chip->pmic_chg_irq[interrupt]);
+		disable_irq_nosync(chip->pmic_chg_irq[interrupt]);
+	}
+}
+
+static int pm_chg_get_rt_status(struct pm8921_chg_chip *chip, int irq_id)
+{
+	return pm8xxx_read_irq_stat(chip->dev->parent,
+					chip->pmic_chg_irq[irq_id]);
+}
+
+/* Treat OverVoltage/UnderVoltage as source missing */
+static int is_usb_chg_plugged_in(struct pm8921_chg_chip *chip)
+{
+	int pres, ov, uv;
+
+	pres = pm_chg_get_rt_status(chip, USBIN_VALID_IRQ);
+	ov = pm_chg_get_rt_status(chip, USBIN_OV_IRQ);
+	uv = pm_chg_get_rt_status(chip, USBIN_UV_IRQ);
+
+	return pres && !ov && !uv;
+}
+
+/* Treat OverVoltage/UnderVoltage as source missing */
+static int is_dc_chg_plugged_in(struct pm8921_chg_chip *chip)
+{
+	int pres, ov, uv;
+
+	pres = pm_chg_get_rt_status(chip, DCIN_VALID_IRQ);
+	ov = pm_chg_get_rt_status(chip, DCIN_OV_IRQ);
+	uv = pm_chg_get_rt_status(chip, DCIN_UV_IRQ);
+
+	return pres && !ov && !uv;
+}
+
+static int is_battery_charging(int fsm_state)
+{
+	switch (fsm_state) {
+	case FSM_STATE_ATC_2A:
+	case FSM_STATE_ATC_2B:
+	case FSM_STATE_ON_CHG_AND_BAT_6:
+	case FSM_STATE_FAST_CHG_7:
+	case FSM_STATE_TRKL_CHG_8:
+		return 1;
+	}
+	return 0;
+}
+
+static void bms_notify(struct work_struct *work)
+{
+	struct bms_notify *n = container_of(work, struct bms_notify, work);
+
+	if (n->is_charging)
+		pm8921_bms_charging_began();
+	else
+		pm8921_bms_charging_end();
+}
+
+static enum power_supply_property pm_power_props[] = {
+	POWER_SUPPLY_PROP_PRESENT,
+};
+
+static char *pm_power_supplied_to[] = {
+	"battery",
+};
+
+static int pm_power_get_property(struct power_supply *psy,
+				  enum power_supply_property psp,
+				  union power_supply_propval *val)
+{
+	struct pm8921_chg_chip *chip;
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_PRESENT:
+		if (psy->type == POWER_SUPPLY_TYPE_MAINS) {
+			chip = container_of(psy, struct pm8921_chg_chip,
+							dc_psy);
+			val->intval = is_dc_chg_plugged_in(chip);
+		}
+		if (psy->type == POWER_SUPPLY_TYPE_USB) {
+			chip = container_of(psy, struct pm8921_chg_chip,
+							usb_psy);
+			val->intval = is_usb_chg_plugged_in(chip);
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static enum power_supply_property msm_batt_power_props[] = {
+	POWER_SUPPLY_PROP_STATUS,
+	POWER_SUPPLY_PROP_CHARGE_TYPE,
+	POWER_SUPPLY_PROP_HEALTH,
+	POWER_SUPPLY_PROP_PRESENT,
+	POWER_SUPPLY_PROP_TECHNOLOGY,
+	POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+	POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+	POWER_SUPPLY_PROP_VOLTAGE_NOW,
+	POWER_SUPPLY_PROP_CAPACITY,
+};
+
+static int get_prop_battery_mvolts(struct pm8921_chg_chip *chip)
+{
+	int rc;
+	struct pm8921_adc_chan_result result;
+
+	rc = pm8921_adc_read(chip->vbat_channel, &result);
+	if (rc) {
+		pr_err("error reading adc channel = %d, rc = %d\n",
+					chip->vbat_channel, rc);
+		return rc;
+	}
+	pr_debug("mvolts phy = %lld meas = 0x%llx", result.physical,
+						result.measurement);
+	return (int)result.physical;
+}
+
+static int get_prop_batt_capacity(struct pm8921_chg_chip *chip)
+{
+	return pm8921_bms_get_percent_charge();
+}
+
+static int get_prop_batt_health(struct pm8921_chg_chip *chip)
+{
+	int temp;
+
+	temp = pm_chg_get_rt_status(chip, BATTTEMP_HOT_IRQ);
+	if (temp)
+		return POWER_SUPPLY_HEALTH_OVERHEAT;
+
+	temp = pm_chg_get_rt_status(chip, BATTTEMP_COLD_IRQ);
+	if (temp)
+		return POWER_SUPPLY_HEALTH_COLD;
+
+	return POWER_SUPPLY_HEALTH_GOOD;
+}
+
+static int get_prop_batt_present(struct pm8921_chg_chip *chip)
+{
+	return pm_chg_get_rt_status(chip, BATT_INSERTED_IRQ);
+}
+
+static int get_prop_charge_type(struct pm8921_chg_chip *chip)
+{
+	int temp;
+
+	temp = pm_chg_get_rt_status(chip, TRKLCHG_IRQ);
+	if (temp)
+		return POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
+
+	temp = pm_chg_get_rt_status(chip, FASTCHG_IRQ);
+	if (temp)
+		return POWER_SUPPLY_CHARGE_TYPE_FAST;
+
+	return POWER_SUPPLY_CHARGE_TYPE_NONE;
+}
+
+static int get_prop_batt_status(struct pm8921_chg_chip *chip)
+{
+	int temp = 0;
+
+	/* TODO reading the FSM state is more reliable */
+	temp = pm_chg_get_rt_status(chip, TRKLCHG_IRQ);
+
+	temp |= pm_chg_get_rt_status(chip, FASTCHG_IRQ);
+	if (temp)
+		return POWER_SUPPLY_STATUS_CHARGING;
+	/*
+	 * The battery is not charging
+	 * check the FET - if on battery is discharging
+	 *		 - if off battery is isolated(full) and the system
+	 *		   is being driven from a charger
+	 */
+	temp = pm_chg_get_rt_status(chip, BATFET_IRQ);
+	if (temp)
+		return POWER_SUPPLY_STATUS_DISCHARGING;
+
+	return POWER_SUPPLY_STATUS_FULL;
+}
+
+static int pm_batt_power_get_property(struct power_supply *psy,
+				       enum power_supply_property psp,
+				       union power_supply_propval *val)
+{
+	struct pm8921_chg_chip *chip = container_of(psy, struct pm8921_chg_chip,
+								batt_psy);
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_STATUS:
+		val->intval = get_prop_batt_status(chip);
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_TYPE:
+		val->intval = get_prop_charge_type(chip);
+		break;
+	case POWER_SUPPLY_PROP_HEALTH:
+		val->intval = get_prop_batt_health(chip);
+		break;
+	case POWER_SUPPLY_PROP_PRESENT:
+		val->intval = get_prop_batt_present(chip);
+		break;
+	case POWER_SUPPLY_PROP_TECHNOLOGY:
+		val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+		val->intval = chip->max_voltage;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
+		val->intval = chip->min_voltage;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+		val->intval = get_prop_battery_mvolts(chip);
+		break;
+	case POWER_SUPPLY_PROP_CAPACITY:
+		val->intval = get_prop_batt_capacity(chip);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void (*notify_vbus_state_func_ptr)(int);
+static int usb_chg_current;
+static DEFINE_SPINLOCK(vbus_lock);
+
+int pm8921_charger_register_vbus_sn(void (*callback)(int))
+{
+	pr_debug("%p\n", callback);
+	notify_vbus_state_func_ptr = callback;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(pm8921_charger_register_vbus_sn);
+
+/* this is passed to the hsusb via platform_data msm_otg_pdata */
+void pm8921_charger_unregister_vbus_sn(void (*callback)(int))
+{
+	pr_debug("%p\n", callback);
+	notify_vbus_state_func_ptr = NULL;
+}
+EXPORT_SYMBOL_GPL(pm8921_charger_unregister_vbus_sn);
+
+static void notify_usb_of_the_plugin_event(int plugin)
+{
+	plugin = !!plugin;
+	if (notify_vbus_state_func_ptr) {
+		pr_debug("notifying plugin\n");
+		(*notify_vbus_state_func_ptr) (plugin);
+	} else {
+		pr_debug("unable to notify plugin\n");
+	}
+}
+
+struct usb_ma_limit_entry {
+	int usb_ma;
+	u8  chg_iusb_value;
+};
+
+static struct usb_ma_limit_entry usb_ma_table[] = {
+	{100, 0},
+	{500, 1},
+	{700, 2},
+	{850, 3},
+	{900, 4},
+	{1100, 5},
+	{1300, 6},
+	{1500, 7},
+};
+
+/* assumes vbus_lock is held */
+static void __pm8921_charger_vbus_draw(unsigned int mA)
+{
+	int i, rc;
+
+	if (mA > 0 && mA <= 2) {
+		usb_chg_current = 0;
+		rc = pm_chg_iusbmax_set(the_chip,
+				usb_ma_table[0].chg_iusb_value);
+		if (rc) {
+			pr_err("unable to set iusb to %d rc = %d\n",
+				usb_ma_table[0].chg_iusb_value, rc);
+		}
+		rc = pm_chg_usb_suspend_enable(the_chip, 1);
+		if (rc)
+			pr_err("fail to set suspend bit rc=%d\n", rc);
+	} else {
+		rc = pm_chg_usb_suspend_enable(the_chip, 0);
+		if (rc)
+			pr_err("fail to reset suspend bit rc=%d\n", rc);
+		for (i = ARRAY_SIZE(usb_ma_table) - 1; i >= 0; i--) {
+			if (usb_ma_table[i].usb_ma <= mA)
+				break;
+		}
+		if (i < 0)
+			i = 0;
+		rc = pm_chg_iusbmax_set(the_chip,
+				usb_ma_table[i].chg_iusb_value);
+		if (rc) {
+			pr_err("unable to set iusb to %d rc = %d\n",
+				usb_ma_table[i].chg_iusb_value, rc);
+		}
+	}
+}
+
+/* USB calls these to tell us how much max usb current the system can draw */
+void pm8921_charger_vbus_draw(unsigned int mA)
+{
+	unsigned long flags;
+
+	pr_debug("Enter charge=%d\n", mA);
+	spin_lock_irqsave(&vbus_lock, flags);
+	if (the_chip) {
+		__pm8921_charger_vbus_draw(mA);
+	} else {
+		/*
+		 * called before pmic initialized,
+		 * save this value and use it at probe
+		 */
+		usb_chg_current = mA;
+	}
+	spin_unlock_irqrestore(&vbus_lock, flags);
+}
+EXPORT_SYMBOL_GPL(pm8921_charger_vbus_draw);
+
+static void handle_usb_insertion_removal(struct pm8921_chg_chip *chip)
+{
+	int usb_present;
+
+	usb_present = is_usb_chg_plugged_in(chip);
+	if (chip->usb_present ^ usb_present) {
+		notify_usb_of_the_plugin_event(usb_present);
+		chip->usb_present = usb_present;
+		power_supply_changed(&chip->usb_psy);
+	}
+}
+
+static void handle_dc_removal_insertion(struct pm8921_chg_chip *chip)
+{
+	int dc_present;
+
+	dc_present = is_dc_chg_plugged_in(chip);
+	if (chip->dc_present ^ dc_present) {
+		chip->dc_present = dc_present;
+		power_supply_changed(&chip->dc_psy);
+	}
+}
+
+static irqreturn_t usbin_valid_irq_handler(int irq, void *data)
+{
+	handle_usb_insertion_removal(data);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t usbin_ov_irq_handler(int irq, void *data)
+{
+	handle_usb_insertion_removal(data);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t batt_inserted_irq_handler(int irq, void *data)
+{
+	struct pm8921_chg_chip *chip = data;
+	int status;
+
+	status = pm_chg_get_rt_status(chip,
+				BATT_INSERTED_IRQ);
+	pr_debug("battery present=%d", status);
+	power_supply_changed(&chip->batt_psy);
+	return IRQ_HANDLED;
+}
+/* this interrupt used to restart charging a battery */
+static irqreturn_t vbatdet_low_irq_handler(int irq, void *data)
+{
+	struct pm8921_chg_chip *chip = data;
+
+	pr_debug("fsm_state=%d\n", pm_chg_get_fsm_state(data));
+	power_supply_changed(&chip->batt_psy);
+	power_supply_changed(&chip->usb_psy);
+	power_supply_changed(&chip->dc_psy);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t usbin_uv_irq_handler(int irq, void *data)
+{
+	handle_usb_insertion_removal(data);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t vbat_ov_irq_handler(int irq, void *data)
+{
+	pr_debug("fsm_state=%d\n", pm_chg_get_fsm_state(data));
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t chgwdog_irq_handler(int irq, void *data)
+{
+	pr_debug("fsm_state=%d\n", pm_chg_get_fsm_state(data));
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t vcp_irq_handler(int irq, void *data)
+{
+	pr_warning("VCP triggered BATDET forced on\n");
+	pr_debug("state_changed_to=%d\n", pm_chg_get_fsm_state(data));
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t atcdone_irq_handler(int irq, void *data)
+{
+	pr_debug("fsm_state=%d\n", pm_chg_get_fsm_state(data));
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t atcfail_irq_handler(int irq, void *data)
+{
+	pr_debug("fsm_state=%d\n", pm_chg_get_fsm_state(data));
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t chgdone_irq_handler(int irq, void *data)
+{
+	struct pm8921_chg_chip *chip = data;
+
+	pr_debug("state_changed_to=%d\n", pm_chg_get_fsm_state(data));
+	power_supply_changed(&chip->batt_psy);
+	power_supply_changed(&chip->usb_psy);
+	power_supply_changed(&chip->dc_psy);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t chgfail_irq_handler(int irq, void *data)
+{
+	struct pm8921_chg_chip *chip = data;
+
+	pr_debug("state_changed_to=%d\n", pm_chg_get_fsm_state(data));
+	power_supply_changed(&chip->batt_psy);
+	power_supply_changed(&chip->usb_psy);
+	power_supply_changed(&chip->dc_psy);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t chgstate_irq_handler(int irq, void *data)
+{
+	struct pm8921_chg_chip *chip = data;
+	int new_is_charging = 0, fsm_state;
+
+	pr_debug("state_changed_to=%d\n", pm_chg_get_fsm_state(data));
+	power_supply_changed(&chip->batt_psy);
+	power_supply_changed(&chip->usb_psy);
+	power_supply_changed(&chip->dc_psy);
+
+	fsm_state = pm_chg_get_fsm_state(chip);
+	new_is_charging = is_battery_charging(fsm_state);
+
+	if (chip->bms_notify.is_charging ^ new_is_charging) {
+		chip->bms_notify.is_charging = new_is_charging;
+		schedule_work(&(chip->bms_notify.work));
+	}
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t loop_change_irq_handler(int irq, void *data)
+{
+	pr_debug("fsm_state=%d\n", pm_chg_get_fsm_state(data));
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t fastchg_irq_handler(int irq, void *data)
+{
+	struct pm8921_chg_chip *chip = data;
+
+	power_supply_changed(&chip->batt_psy);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t trklchg_irq_handler(int irq, void *data)
+{
+	struct pm8921_chg_chip *chip = data;
+
+	power_supply_changed(&chip->batt_psy);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t batt_removed_irq_handler(int irq, void *data)
+{
+	struct pm8921_chg_chip *chip = data;
+	int status;
+
+	status = pm_chg_get_rt_status(chip, BATT_REMOVED_IRQ);
+	pr_debug("battery present=%d state=%d", !status,
+					 pm_chg_get_fsm_state(data));
+	power_supply_changed(&chip->batt_psy);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t batttemp_hot_irq_handler(int irq, void *data)
+{
+	struct pm8921_chg_chip *chip = data;
+
+	power_supply_changed(&chip->batt_psy);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t chghot_irq_handler(int irq, void *data)
+{
+	struct pm8921_chg_chip *chip = data;
+
+	pr_debug("Chg hot fsm_state=%d\n", pm_chg_get_fsm_state(data));
+	power_supply_changed(&chip->batt_psy);
+	power_supply_changed(&chip->usb_psy);
+	power_supply_changed(&chip->dc_psy);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t batttemp_cold_irq_handler(int irq, void *data)
+{
+	struct pm8921_chg_chip *chip = data;
+
+	pr_debug("Batt cold fsm_state=%d\n", pm_chg_get_fsm_state(data));
+	power_supply_changed(&chip->batt_psy);
+	power_supply_changed(&chip->usb_psy);
+	power_supply_changed(&chip->dc_psy);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t chg_gone_irq_handler(int irq, void *data)
+{
+	struct pm8921_chg_chip *chip = data;
+
+	pr_debug("Chg gone fsm_state=%d\n", pm_chg_get_fsm_state(data));
+	power_supply_changed(&chip->batt_psy);
+	power_supply_changed(&chip->usb_psy);
+	power_supply_changed(&chip->dc_psy);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t bat_temp_ok_irq_handler(int irq, void *data)
+{
+	struct pm8921_chg_chip *chip = data;
+
+	pr_debug("batt temp ok fsm_state=%d\n", pm_chg_get_fsm_state(data));
+	power_supply_changed(&chip->batt_psy);
+	power_supply_changed(&chip->usb_psy);
+	power_supply_changed(&chip->dc_psy);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t coarse_det_low_irq_handler(int irq, void *data)
+{
+	pr_debug("fsm_state=%d\n", pm_chg_get_fsm_state(data));
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t vdd_loop_irq_handler(int irq, void *data)
+{
+	pr_debug("fsm_state=%d\n", pm_chg_get_fsm_state(data));
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t vreg_ov_irq_handler(int irq, void *data)
+{
+	pr_debug("fsm_state=%d\n", pm_chg_get_fsm_state(data));
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t vbatdet_irq_handler(int irq, void *data)
+{
+	pr_debug("fsm_state=%d\n", pm_chg_get_fsm_state(data));
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t batfet_irq_handler(int irq, void *data)
+{
+	struct pm8921_chg_chip *chip = data;
+
+	pr_debug("vreg ov\n");
+	power_supply_changed(&chip->batt_psy);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t dcin_valid_irq_handler(int irq, void *data)
+{
+	handle_dc_removal_insertion(data);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t dcin_ov_irq_handler(int irq, void *data)
+{
+	handle_dc_removal_insertion(data);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t dcin_uv_irq_handler(int irq, void *data)
+{
+	handle_dc_removal_insertion(data);
+	return IRQ_HANDLED;
+}
+
+static int set_disable_status_param(const char *val, struct kernel_param *kp)
+{
+	int ret;
+	struct pm8921_chg_chip *chip = the_chip;
+
+	ret = param_set_int(val, kp);
+	if (ret) {
+		pr_err("error setting value %d\n", ret);
+		return ret;
+	}
+	pr_info("factory set disable param to %d\n", charging_disabled);
+	if (chip) {
+		pm_chg_auto_enable(chip, !charging_disabled);
+		pm_chg_charge_dis(chip, charging_disabled);
+	}
+	return 0;
+}
+module_param_call(disabled, set_disable_status_param, param_get_uint,
+					&charging_disabled, 0644);
+
+static void free_irqs(struct pm8921_chg_chip *chip)
+{
+	int i;
+
+	for (i = 0; i < PM_CHG_MAX_INTS; i++)
+		if (chip->pmic_chg_irq[i]) {
+			free_irq(chip->pmic_chg_irq[i], chip);
+			chip->pmic_chg_irq[i] = 0;
+		}
+}
+
+/* determines the initial present states and notifies msm_charger */
+static void __devinit determine_initial_state(struct pm8921_chg_chip *chip)
+{
+	unsigned long flags;
+	int fsm_state;
+
+	chip->dc_present = !!is_dc_chg_plugged_in(chip);
+	chip->usb_present = !!is_usb_chg_plugged_in(chip);
+
+	notify_usb_of_the_plugin_event(chip->usb_present);
+
+	pm8921_chg_enable_irq(chip, DCIN_VALID_IRQ);
+	pm8921_chg_enable_irq(chip, USBIN_VALID_IRQ);
+	pm8921_chg_enable_irq(chip, BATT_REMOVED_IRQ);
+	pm8921_chg_enable_irq(chip, BATT_REMOVED_IRQ);
+	pm8921_chg_enable_irq(chip, CHGSTATE_IRQ);
+
+	spin_lock_irqsave(&vbus_lock, flags);
+	if (usb_chg_current) {
+		/* reissue a vbus draw call */
+		__pm8921_charger_vbus_draw(usb_chg_current);
+	}
+	spin_unlock_irqrestore(&vbus_lock, flags);
+
+	fsm_state = pm_chg_get_fsm_state(chip);
+	if (is_battery_charging(fsm_state)) {
+		chip->bms_notify.is_charging = 1;
+		pm8921_bms_charging_began();
+	}
+
+	pr_debug("usb = %d, dc = %d  batt = %d state=%d\n",
+			chip->usb_present,
+			chip->dc_present,
+			get_prop_batt_present(chip),
+			fsm_state);
+}
+
+struct pm_chg_irq_init_data {
+	unsigned int	irq_id;
+	char		*name;
+	unsigned long	flags;
+	irqreturn_t	(*handler)(int, void *);
+};
+
+#define CHG_IRQ(_id, _flags, _handler) \
+{ \
+	.irq_id		= _id, \
+	.name		= #_id, \
+	.flags		= _flags, \
+	.handler	= _handler, \
+}
+struct pm_chg_irq_init_data chg_irq_data[] = {
+	CHG_IRQ(USBIN_VALID_IRQ, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+						usbin_valid_irq_handler),
+	CHG_IRQ(USBIN_OV_IRQ, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+						usbin_ov_irq_handler),
+	CHG_IRQ(BATT_INSERTED_IRQ, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+						batt_inserted_irq_handler),
+	CHG_IRQ(VBATDET_LOW_IRQ, IRQF_TRIGGER_RISING, vbatdet_low_irq_handler),
+	CHG_IRQ(USBIN_UV_IRQ, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+							usbin_uv_irq_handler),
+	CHG_IRQ(VBAT_OV_IRQ, IRQF_TRIGGER_RISING, vbat_ov_irq_handler),
+	CHG_IRQ(CHGWDOG_IRQ, IRQF_TRIGGER_RISING, chgwdog_irq_handler),
+	CHG_IRQ(VCP_IRQ, IRQF_TRIGGER_RISING, vcp_irq_handler),
+	CHG_IRQ(ATCDONE_IRQ, IRQF_TRIGGER_RISING, atcdone_irq_handler),
+	CHG_IRQ(ATCFAIL_IRQ, IRQF_TRIGGER_RISING, atcfail_irq_handler),
+	CHG_IRQ(CHGDONE_IRQ, IRQF_TRIGGER_RISING, chgdone_irq_handler),
+	CHG_IRQ(CHGFAIL_IRQ, IRQF_TRIGGER_RISING, chgfail_irq_handler),
+	CHG_IRQ(CHGSTATE_IRQ, IRQF_TRIGGER_RISING, chgstate_irq_handler),
+	CHG_IRQ(LOOP_CHANGE_IRQ, IRQF_TRIGGER_RISING, loop_change_irq_handler),
+	CHG_IRQ(FASTCHG_IRQ, IRQF_TRIGGER_RISING, fastchg_irq_handler),
+	CHG_IRQ(TRKLCHG_IRQ, IRQF_TRIGGER_RISING, trklchg_irq_handler),
+	CHG_IRQ(BATT_REMOVED_IRQ, IRQF_TRIGGER_RISING,
+						batt_removed_irq_handler),
+	CHG_IRQ(BATTTEMP_HOT_IRQ, IRQF_TRIGGER_RISING,
+						batttemp_hot_irq_handler),
+	CHG_IRQ(CHGHOT_IRQ, IRQF_TRIGGER_RISING, chghot_irq_handler),
+	CHG_IRQ(BATTTEMP_COLD_IRQ, IRQF_TRIGGER_RISING,
+						batttemp_cold_irq_handler),
+	CHG_IRQ(CHG_GONE_IRQ, IRQF_TRIGGER_RISING, chg_gone_irq_handler),
+	CHG_IRQ(BAT_TEMP_OK_IRQ, IRQF_TRIGGER_RISING, bat_temp_ok_irq_handler),
+	CHG_IRQ(COARSE_DET_LOW_IRQ, IRQF_TRIGGER_RISING,
+						coarse_det_low_irq_handler),
+	CHG_IRQ(VDD_LOOP_IRQ, IRQF_TRIGGER_RISING, vdd_loop_irq_handler),
+	CHG_IRQ(VREG_OV_IRQ, IRQF_TRIGGER_RISING, vreg_ov_irq_handler),
+	CHG_IRQ(VBATDET_IRQ, IRQF_TRIGGER_RISING, vbatdet_irq_handler),
+	CHG_IRQ(BATFET_IRQ, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+						batfet_irq_handler),
+	CHG_IRQ(DCIN_VALID_IRQ, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+						dcin_valid_irq_handler),
+	CHG_IRQ(DCIN_OV_IRQ, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+						dcin_ov_irq_handler),
+	CHG_IRQ(DCIN_UV_IRQ, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+						dcin_uv_irq_handler),
+};
+
+static int __devinit request_irqs(struct pm8921_chg_chip *chip,
+					struct platform_device *pdev)
+{
+	struct resource *res;
+	int ret, i;
+
+	ret = 0;
+	bitmap_fill(chip->enabled_irqs, PM_CHG_MAX_INTS);
+
+	for (i = 0; i < ARRAY_SIZE(chg_irq_data); i++) {
+		res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+				chg_irq_data[i].name);
+		if (res == NULL) {
+			pr_err("couldn't find %s\n", chg_irq_data[i].name);
+			goto err_out;
+		}
+		ret = request_irq(res->start, chg_irq_data[i].handler,
+			chg_irq_data[i].flags,
+			chg_irq_data[i].name, chip);
+		if (ret < 0) {
+			pr_err("couldn't request %d (%s) %d\n", res->start,
+					chg_irq_data[i].name, ret);
+			goto err_out;
+		}
+		chip->pmic_chg_irq[chg_irq_data[i].irq_id] = res->start;
+		pm8921_chg_disable_irq(chip, chg_irq_data[i].irq_id);
+	}
+	return 0;
+
+err_out:
+	free_irqs(chip);
+	return -EINVAL;
+}
+
+#define ENUM_TIMER_STOP_BIT	BIT(1)
+#define BOOT_DONE_BIT		BIT(6)
+#define CHG_BATFET_ON_BIT	BIT(3)
+#define CHG_VCP_EN		BIT(0)
+#define CHG_BAT_TEMP_DIS_BIT	BIT(2)
+#define SAFE_CURRENT_MA		1500
+static int __devinit pm8921_chg_hw_init(struct pm8921_chg_chip *chip)
+{
+	int rc;
+
+	rc = pm_chg_masked_write(chip, SYS_CONFIG_2,
+					BOOT_DONE_BIT, BOOT_DONE_BIT);
+	if (rc) {
+		pr_err("Failed to set BOOT_DONE_BIT rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = pm_chg_vddsafe_set(chip, chip->max_voltage);
+	if (rc) {
+		pr_err("Failed to set safe voltage to %d rc=%d\n",
+						chip->max_voltage, rc);
+		return rc;
+	}
+	rc = pm_chg_vbatdet_set(chip, chip->resume_voltage);
+	if (rc) {
+		pr_err("Failed to set vbatdet comprator voltage to %d rc=%d\n",
+						chip->resume_voltage, rc);
+		return rc;
+	}
+
+	rc = pm_chg_vddmax_set(chip, chip->max_voltage);
+	if (rc) {
+		pr_err("Failed to set max voltage to %d rc=%d\n",
+						chip->max_voltage, rc);
+		return rc;
+	}
+	rc = pm_chg_ibatsafe_set(chip, SAFE_CURRENT_MA);
+	if (rc) {
+		pr_err("Failed to set max voltage to %d rc=%d\n",
+						SAFE_CURRENT_MA, rc);
+		return rc;
+	}
+
+	/* TODO needs to be changed as per the temeperature of the battery */
+	rc = pm_chg_ibatmax_set(chip, 400);
+	if (rc) {
+		pr_err("Failed to set max current to 400 rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = pm_chg_iterm_set(chip, chip->term_current);
+	if (rc) {
+		pr_err("Failed to set term current to %d rc=%d\n",
+						chip->term_current, rc);
+		return rc;
+	}
+
+	/* Disable the ENUM TIMER */
+	rc = pm_chg_masked_write(chip, PBL_ACCESS2, ENUM_TIMER_STOP_BIT,
+			ENUM_TIMER_STOP_BIT);
+	if (rc) {
+		pr_err("Failed to set enum timer stop rc=%d\n", rc);
+		return rc;
+	}
+
+	/* init with the lowest USB current */
+	rc = pm_chg_iusbmax_set(chip, usb_ma_table[0].chg_iusb_value);
+	if (rc) {
+		pr_err("Failed to set usb max to %d rc=%d\n",
+					usb_ma_table[0].chg_iusb_value, rc);
+		return rc;
+	}
+	rc = pm_chg_disable_wd(chip);
+	if (rc) {
+		pr_err("Failed to disable wd rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = pm_chg_masked_write(chip, CHG_CNTRL_2,
+				CHG_BAT_TEMP_DIS_BIT, 0);
+	if (rc) {
+		pr_err("Failed to enable temp control chg rc=%d\n", rc);
+		return rc;
+	}
+	/* switch to a 3.2Mhz for the buck */
+	rc = pm8xxx_writeb(chip->dev->parent, CHG_BUCK_CLOCK_CTRL, 0x15);
+	if (rc) {
+		pr_err("Failed to switch buck clk rc=%d\n", rc);
+		return rc;
+	}
+
+	/* Workarounds for die 1.1 and 1.0 */
+	if (pm8xxx_get_revision(chip->dev->parent) < PM8XXX_REVISION_8921_2p0) {
+		pm8xxx_writeb(chip->dev->parent, CHG_BUCK_CTRL_TEST2, 0xF1);
+		pm8xxx_writeb(chip->dev->parent, CHG_BUCK_CTRL_TEST3, 0x8C);
+		pm8xxx_writeb(chip->dev->parent, CHG_BUCK_CTRL_TEST3, 0xCE);
+		pm8xxx_writeb(chip->dev->parent, CHG_BUCK_CTRL_TEST3, 0xD8);
+	}
+
+	rc = pm_chg_charge_dis(chip, charging_disabled);
+	if (rc) {
+		pr_err("Failed to disable CHG_CHARGE_DIS bit rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = pm_chg_auto_enable(chip, !charging_disabled);
+	if (rc) {
+		pr_err("Failed to enable charging rc=%d\n", rc);
+		return rc;
+	}
+
+	return 0;
+}
+
+static int get_rt_status(void *data, u64 * val)
+{
+	int i = (int)data;
+	int ret;
+
+	/* global irq number is passed in via data */
+	ret = pm_chg_get_rt_status(the_chip, i);
+	*val = ret;
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(rt_fops, get_rt_status, NULL, "%llu\n");
+
+static int get_fsm_status(void *data, u64 * val)
+{
+	u8 temp;
+
+	temp = pm_chg_get_fsm_state(the_chip);
+	*val = temp;
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(fsm_fops, get_fsm_status, NULL, "%llu\n");
+
+static int get_reg(void *data, u64 * val)
+{
+	int addr = (int)data;
+	int ret;
+	u8 temp;
+
+	ret = pm8xxx_readb(the_chip->dev->parent, addr, &temp);
+	if (ret) {
+		pr_err("pm8xxx_readb to %x value =%d errored = %d\n",
+			addr, temp, ret);
+		return -EAGAIN;
+	}
+	*val = temp;
+	return 0;
+}
+
+static int set_reg(void *data, u64 val)
+{
+	int addr = (int)data;
+	int ret;
+	u8 temp;
+
+	temp = (u8) val;
+	ret = pm8xxx_writeb(the_chip->dev->parent, addr, temp);
+	if (ret) {
+		pr_err("pm8xxx_writeb to %x value =%d errored = %d\n",
+			addr, temp, ret);
+		return -EAGAIN;
+	}
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(reg_fops, get_reg, set_reg, "0x%02llx\n");
+
+static void create_debugfs_entries(struct pm8921_chg_chip *chip)
+{
+	int i;
+
+	chip->dent = debugfs_create_dir("pm8921_chg", NULL);
+
+	if (IS_ERR(chip->dent)) {
+		pr_err("pmic charger couldnt create debugfs dir\n");
+		return;
+	}
+
+	debugfs_create_file("CHG_CNTRL", 0644, chip->dent,
+			    (void *)CHG_CNTRL, &reg_fops);
+	debugfs_create_file("CHG_CNTRL_2", 0644, chip->dent,
+			    (void *)CHG_CNTRL_2, &reg_fops);
+	debugfs_create_file("CHG_CNTRL_3", 0644, chip->dent,
+			    (void *)CHG_CNTRL_3, &reg_fops);
+	debugfs_create_file("PBL_ACCESS1", 0644, chip->dent,
+			    (void *)PBL_ACCESS1, &reg_fops);
+	debugfs_create_file("PBL_ACCESS2", 0644, chip->dent,
+			    (void *)PBL_ACCESS2, &reg_fops);
+	debugfs_create_file("SYS_CONFIG_1", 0644, chip->dent,
+			    (void *)SYS_CONFIG_1, &reg_fops);
+	debugfs_create_file("SYS_CONFIG_2", 0644, chip->dent,
+			    (void *)SYS_CONFIG_2, &reg_fops);
+	debugfs_create_file("CHG_VDD_MAX", 0644, chip->dent,
+			    (void *)CHG_VDD_MAX, &reg_fops);
+	debugfs_create_file("CHG_VDD_SAFE", 0644, chip->dent,
+			    (void *)CHG_VDD_SAFE, &reg_fops);
+	debugfs_create_file("CHG_VBAT_DET", 0644, chip->dent,
+			    (void *)CHG_VBAT_DET, &reg_fops);
+	debugfs_create_file("CHG_IBAT_MAX", 0644, chip->dent,
+			    (void *)CHG_IBAT_MAX, &reg_fops);
+	debugfs_create_file("CHG_IBAT_SAFE", 0644, chip->dent,
+			    (void *)CHG_IBAT_SAFE, &reg_fops);
+	debugfs_create_file("CHG_VIN_MIN", 0644, chip->dent,
+			    (void *)CHG_VIN_MIN, &reg_fops);
+	debugfs_create_file("CHG_VTRICKLE", 0644, chip->dent,
+			    (void *)CHG_VTRICKLE, &reg_fops);
+	debugfs_create_file("CHG_ITRICKLE", 0644, chip->dent,
+			    (void *)CHG_ITRICKLE, &reg_fops);
+	debugfs_create_file("CHG_ITERM", 0644, chip->dent,
+			    (void *)CHG_ITERM, &reg_fops);
+	debugfs_create_file("CHG_TCHG_MAX", 0644, chip->dent,
+			    (void *)CHG_TCHG_MAX, &reg_fops);
+	debugfs_create_file("CHG_TWDOG", 0644, chip->dent,
+			    (void *)CHG_TWDOG, &reg_fops);
+	debugfs_create_file("CHG_TEMP_THRESH", 0644, chip->dent,
+			    (void *)CHG_TEMP_THRESH, &reg_fops);
+	debugfs_create_file("CHG_COMP_OVR", 0644, chip->dent,
+			    (void *)CHG_COMP_OVR, &reg_fops);
+	debugfs_create_file("CHG_BUCK_CTRL_TEST1", 0644, chip->dent,
+			    (void *)CHG_BUCK_CTRL_TEST1, &reg_fops);
+	debugfs_create_file("CHG_BUCK_CTRL_TEST2", 0644, chip->dent,
+			    (void *)CHG_BUCK_CTRL_TEST2, &reg_fops);
+	debugfs_create_file("CHG_BUCK_CTRL_TEST3", 0644, chip->dent,
+			    (void *)CHG_BUCK_CTRL_TEST3, &reg_fops);
+	debugfs_create_file("CHG_TEST", 0644, chip->dent,
+			    (void *)CHG_TEST, &reg_fops);
+
+	debugfs_create_file("FSM_STATE", 0644, chip->dent, NULL,
+			    &fsm_fops);
+
+	for (i = 0; i < ARRAY_SIZE(chg_irq_data); i++) {
+		if (chip->pmic_chg_irq[chg_irq_data[i].irq_id])
+			debugfs_create_file(chg_irq_data[i].name, 0444,
+				chip->dent,
+				(void *)chg_irq_data[i].irq_id,
+				&rt_fops);
+	}
+}
+
+static int __devinit pm8921_charger_probe(struct platform_device *pdev)
+{
+	int rc = 0;
+	struct pm8921_chg_chip *chip;
+	const struct pm8921_charger_platform_data *pdata
+				= pdev->dev.platform_data;
+
+	if (!pdata) {
+		pr_err("missing platform data\n");
+		return -EINVAL;
+	}
+
+	chip = kzalloc(sizeof(struct pm8921_chg_chip),
+					GFP_KERNEL);
+	if (!chip) {
+		pr_err("Cannot allocate pm_chg_chip\n");
+		return -ENOMEM;
+	}
+
+	chip->dev = &pdev->dev;
+	chip->safety_time = pdata->safety_time;
+	chip->update_time = pdata->update_time;
+	chip->max_voltage = pdata->max_voltage;
+	chip->min_voltage = pdata->min_voltage;
+	chip->resume_voltage = pdata->resume_voltage;
+	chip->term_current = pdata->term_current;
+	chip->vbat_channel = pdata->charger_cdata.vbat_channel;
+
+	rc = pm8921_chg_hw_init(chip);
+	if (rc) {
+		pr_err("couldn't init hardware rc=%d\n", rc);
+		goto free_chip;
+	}
+
+	chip->usb_psy.name = "usb",
+	chip->usb_psy.type = POWER_SUPPLY_TYPE_USB,
+	chip->usb_psy.supplied_to = pm_power_supplied_to,
+	chip->usb_psy.num_supplicants = ARRAY_SIZE(pm_power_supplied_to),
+	chip->usb_psy.properties = pm_power_props,
+	chip->usb_psy.num_properties = ARRAY_SIZE(pm_power_props),
+	chip->usb_psy.get_property = pm_power_get_property,
+
+	chip->dc_psy.name = "ac",
+	chip->dc_psy.type = POWER_SUPPLY_TYPE_MAINS,
+	chip->dc_psy.supplied_to = pm_power_supplied_to,
+	chip->dc_psy.num_supplicants = ARRAY_SIZE(pm_power_supplied_to),
+	chip->dc_psy.properties = pm_power_props,
+	chip->dc_psy.num_properties = ARRAY_SIZE(pm_power_props),
+	chip->dc_psy.get_property = pm_power_get_property,
+
+	chip->batt_psy.name = "battery",
+	chip->batt_psy.type = POWER_SUPPLY_TYPE_BATTERY,
+	chip->batt_psy.properties = msm_batt_power_props,
+	chip->batt_psy.num_properties = ARRAY_SIZE(msm_batt_power_props),
+	chip->batt_psy.get_property = pm_batt_power_get_property,
+
+	rc = power_supply_register(chip->dev, &chip->usb_psy);
+	if (rc < 0) {
+		pr_err("power_supply_register usb failed rc = %d\n", rc);
+		goto free_irq;
+	}
+
+	rc = power_supply_register(chip->dev, &chip->dc_psy);
+	if (rc < 0) {
+		pr_err("power_supply_register dc failed rc = %d\n", rc);
+		goto unregister_usb;
+	}
+
+	rc = power_supply_register(chip->dev, &chip->batt_psy);
+	if (rc < 0) {
+		pr_err("power_supply_register batt failed rc = %d\n", rc);
+		goto unregister_dc;
+	}
+
+	rc = request_irqs(chip, pdev);
+	if (rc) {
+		pr_err("couldn't register interrupts rc=%d\n", rc);
+		goto unregister_batt;
+	}
+
+	platform_set_drvdata(pdev, chip);
+	the_chip = chip;
+	create_debugfs_entries(chip);
+
+	INIT_WORK(&chip->bms_notify.work, bms_notify);
+	/* determine what state the charger is in */
+	determine_initial_state(chip);
+
+	return 0;
+
+free_irq:
+	free_irqs(chip);
+unregister_batt:
+	power_supply_unregister(&chip->batt_psy);
+unregister_dc:
+	power_supply_unregister(&chip->dc_psy);
+unregister_usb:
+	power_supply_unregister(&chip->usb_psy);
+free_chip:
+	kfree(chip);
+	return rc;
+}
+
+static int __devexit pm8921_charger_remove(struct platform_device *pdev)
+{
+	struct pm8921_chg_chip *chip = platform_get_drvdata(pdev);
+
+	free_irqs(chip);
+	platform_set_drvdata(pdev, NULL);
+	the_chip = NULL;
+	kfree(chip);
+	return 0;
+}
+
+static struct platform_driver pm8921_charger_driver = {
+	.probe	= pm8921_charger_probe,
+	.remove	= __devexit_p(pm8921_charger_remove),
+	.driver	= {
+		.name	= PM8921_CHARGER_DEV_NAME,
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init pm8921_charger_init(void)
+{
+	return platform_driver_register(&pm8921_charger_driver);
+}
+
+static void __exit pm8921_charger_exit(void)
+{
+	platform_driver_unregister(&pm8921_charger_driver);
+}
+
+late_initcall(pm8921_charger_init);
+module_exit(pm8921_charger_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PMIC8921 charger/battery driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:" PM8921_CHARGER_DEV_NAME);
diff --git a/drivers/power/pmic8058-charger.c b/drivers/power/pmic8058-charger.c
new file mode 100644
index 0000000..8ea7949
--- /dev/null
+++ b/drivers/power/pmic8058-charger.c
@@ -0,0 +1,1954 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/errno.h>
+#include <linux/mfd/pmic8058.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/workqueue.h>
+#include <linux/msm-charger.h>
+#include <linux/debugfs.h>
+#include <linux/slab.h>
+#include <linux/msm_adc.h>
+#include <linux/notifier.h>
+#include <linux/pmic8058-batt-alarm.h>
+
+#include <mach/msm_xo.h>
+#include <mach/msm_hsusb.h>
+
+/* Config Regs  and their bits*/
+#define PM8058_CHG_TEST			0x75
+#define IGNORE_LL			2
+#define PM8058_CHG_TEST_2		0xEA
+#define PM8058_CHG_TEST_3		0xEB
+#define PM8058_OVP_TEST_REG		0xF6
+#define FORCE_OVP_OFF			3
+
+#define PM8058_CHG_CNTRL		0x1E
+#define CHG_TRICKLE_EN			7
+#define CHG_USB_SUSPEND			6
+#define CHG_IMON_CAL			5
+#define CHG_IMON_GAIN			4
+#define CHG_CHARGE_BAT			3
+#define CHG_VBUS_FROM_BOOST_OVRD	2
+#define CHG_CHARGE_DIS			1
+#define CHG_VCP_EN			0
+
+#define PM8058_CHG_CNTRL_2		0xD8
+#define ATC_DIS				7	/* coincell backed */
+#define CHARGE_AUTO_DIS			6
+#define DUMB_CHG_OVRD			5	/* coincell backed */
+#define ENUM_DONE			4
+#define CHG_TEMP_MODE			3
+#define CHG_BATT_TEMP_DIS		1	/* coincell backed */
+#define CHG_FAILED_CLEAR		0
+
+#define PM8058_CHG_VMAX_SEL		0x21
+#define PM8058_CHG_VBAT_DET		0xD9
+#define PM8058_CHG_IMAX			0x1F
+#define PM8058_CHG_TRICKLE		0xDB
+#define PM8058_CHG_ITERM		0xDC
+#define PM8058_CHG_TTRKL_MAX		0xE1
+#define PM8058_CHG_TCHG_MAX		0xE4
+#define PM8058_CHG_TEMP_THRESH		0xE2
+#define PM8058_CHG_TEMP_REG		0xE3
+#define PM8058_CHG_PULSE		0x22
+
+/* IRQ STATUS and CLEAR */
+#define PM8058_CHG_STATUS_CLEAR_IRQ_1	0x31
+#define PM8058_CHG_STATUS_CLEAR_IRQ_3	0x33
+#define PM8058_CHG_STATUS_CLEAR_IRQ_10	0xB3
+#define PM8058_CHG_STATUS_CLEAR_IRQ_11	0xB4
+
+/* IRQ MASKS */
+#define PM8058_CHG_MASK_IRQ_1		0x38
+
+#define PM8058_CHG_MASK_IRQ_3		0x3A
+#define PM8058_CHG_MASK_IRQ_10		0xBA
+#define PM8058_CHG_MASK_IRQ_11		0xBB
+
+/* IRQ Real time status regs */
+#define PM8058_CHG_STATUS_RT_1		0x3F
+#define STATUS_RTCHGVAL			7
+#define STATUS_RTCHGINVAL		6
+#define STATUS_RTBATT_REPLACE		5
+#define STATUS_RTVBATDET_LOW		4
+#define STATUS_RTCHGILIM		3
+#define STATUS_RTPCTDONE		1
+#define STATUS_RTVCP			0
+#define PM8058_CHG_STATUS_RT_3		0x41
+#define PM8058_CHG_STATUS_RT_10		0xC1
+#define PM8058_CHG_STATUS_RT_11		0xC2
+
+/* VTRIM */
+#define PM8058_CHG_VTRIM		0x1D
+#define PM8058_CHG_VBATDET_TRIM		0x1E
+#define PM8058_CHG_ITRIM		0x1F
+#define PM8058_CHG_TTRIM		0x20
+
+#define AUTO_CHARGING_VMAXSEL				4200
+#define AUTO_CHARGING_FAST_TIME_MAX_MINUTES		512
+#define AUTO_CHARGING_TRICKLE_TIME_MINUTES		30
+#define AUTO_CHARGING_VEOC_ITERM			100
+#define AUTO_CHARGING_IEOC_ITERM			160
+#define AUTO_CHARGING_RESUME_MV				4100
+
+#define AUTO_CHARGING_VBATDET				4150
+#define AUTO_CHARGING_VBATDET_DEBOUNCE_TIME_MS		3000
+#define AUTO_CHARGING_VEOC_VBATDET			4100
+#define AUTO_CHARGING_VEOC_TCHG				16
+#define AUTO_CHARGING_VEOC_TCHG_FINAL_CYCLE		32
+#define AUTO_CHARGING_VEOC_BEGIN_TIME_MS		5400000
+
+#define AUTO_CHARGING_VEOC_VBAT_LOW_CHECK_TIME_MS	60000
+#define AUTO_CHARGING_RESUME_CHARGE_DETECTION_COUNTER	5
+
+#define AUTO_CHARGING_DONE_CHECK_TIME_MS		1000
+
+#define PM8058_CHG_I_STEP_MA 50
+#define PM8058_CHG_I_MIN_MA 50
+#define PM8058_CHG_T_TCHG_SHIFT 2
+#define PM8058_CHG_I_TERM_STEP_MA 10
+#define PM8058_CHG_V_STEP_MV 25
+#define PM8058_CHG_V_MIN_MV  2400
+/*
+ * enum pmic_chg_interrupts: pmic interrupts
+ * @CHGVAL_IRQ: charger V between 3.3 and 7.9
+ * @CHGINVAL_IRQ: charger V outside 3.3 and 7.9
+ * @VBATDET_LOW_IRQ: VBAT < VBATDET
+ * @VCP_IRQ: VDD went below VBAT: BAT_FET is turned on
+ * @CHGILIM_IRQ: mA consumed>IMAXSEL: chgloop draws less mA
+ * @ATC_DONE_IRQ: Auto Trickle done
+ * @ATCFAIL_IRQ: Auto Trickle fail
+ * @AUTO_CHGDONE_IRQ: Auto chg done
+ * @AUTO_CHGFAIL_IRQ: time exceeded w/o reaching term current
+ * @CHGSTATE_IRQ: something happend causing a state change
+ * @FASTCHG_IRQ: trkl charging completed: moving to fastchg
+ * @CHG_END_IRQ: mA has dropped to termination current
+ * @BATTTEMP_IRQ: batt temp is out of range
+ * @CHGHOT_IRQ: the pass device is too hot
+ * @CHGTLIMIT_IRQ: unused
+ * @CHG_GONE_IRQ: charger was removed
+ * @VCPMAJOR_IRQ: vcp major
+ * @VBATDET_IRQ: VBAT >= VBATDET
+ * @BATFET_IRQ: BATFET closed
+ * @BATT_REPLACE_IRQ:
+ * @BATTCONNECT_IRQ:
+ */
+enum pmic_chg_interrupts {
+	CHGVAL_IRQ,
+	CHGINVAL_IRQ,
+	VBATDET_LOW_IRQ,
+	VCP_IRQ,
+	CHGILIM_IRQ,
+	ATC_DONE_IRQ,
+	ATCFAIL_IRQ,
+	AUTO_CHGDONE_IRQ,
+	AUTO_CHGFAIL_IRQ,
+	CHGSTATE_IRQ,
+	FASTCHG_IRQ,
+	CHG_END_IRQ,
+	BATTTEMP_IRQ,
+	CHGHOT_IRQ,
+	CHGTLIMIT_IRQ,
+	CHG_GONE_IRQ,
+	VCPMAJOR_IRQ,
+	VBATDET_IRQ,
+	BATFET_IRQ,
+	BATT_REPLACE_IRQ,
+	BATTCONNECT_IRQ,
+	PMIC_CHG_MAX_INTS
+};
+
+struct pm8058_charger {
+	struct pmic_charger_pdata *pdata;
+	struct pm8058_chip *pm_chip;
+	struct device *dev;
+
+	int pmic_chg_irq[PMIC_CHG_MAX_INTS];
+	DECLARE_BITMAP(enabled_irqs, PMIC_CHG_MAX_INTS);
+
+	struct delayed_work chg_done_check_work;
+	struct delayed_work check_vbat_low_work;
+	struct delayed_work veoc_begin_work;
+	struct delayed_work charging_check_work;
+	int waiting_for_topoff;
+	int waiting_for_veoc;
+	int vbatdet;
+	struct msm_hardware_charger hw_chg;
+	int current_charger_current;
+	int disabled;
+
+	struct msm_xo_voter *voter;
+	struct dentry *dent;
+
+	int inited;
+	int present;
+};
+
+static struct pm8058_charger pm8058_chg;
+static struct msm_hardware_charger usb_hw_chg;
+
+
+static int msm_battery_gauge_alarm_notify(struct notifier_block *nb,
+					  unsigned long status, void *unused);
+
+static struct notifier_block alarm_notifier = {
+	.notifier_call = msm_battery_gauge_alarm_notify,
+};
+
+static int resume_mv = AUTO_CHARGING_RESUME_MV;
+static DEFINE_MUTEX(batt_alarm_lock);
+static int resume_mv_set(const char *val, struct kernel_param *kp);
+module_param_call(resume_mv, resume_mv_set, param_get_int,
+				&resume_mv, S_IRUGO | S_IWUSR);
+
+static int resume_mv_set(const char *val, struct kernel_param *kp)
+{
+	int rc;
+
+	mutex_lock(&batt_alarm_lock);
+
+	rc = param_set_int(val, kp);
+	if (rc)
+		goto out;
+
+	rc = pm8058_batt_alarm_threshold_set(resume_mv, 4300);
+
+out:
+	mutex_unlock(&batt_alarm_lock);
+	return rc;
+}
+
+static void pm8058_chg_enable_irq(int interrupt)
+{
+	if (!__test_and_set_bit(interrupt, pm8058_chg.enabled_irqs)) {
+		dev_dbg(pm8058_chg.dev, "%s %d\n", __func__,
+			pm8058_chg.pmic_chg_irq[interrupt]);
+		enable_irq(pm8058_chg.pmic_chg_irq[interrupt]);
+	}
+}
+
+static void pm8058_chg_disable_irq(int interrupt)
+{
+	if (__test_and_clear_bit(interrupt, pm8058_chg.enabled_irqs)) {
+		dev_dbg(pm8058_chg.dev, "%s %d\n", __func__,
+			pm8058_chg.pmic_chg_irq[interrupt]);
+		disable_irq_nosync(pm8058_chg.pmic_chg_irq[interrupt]);
+	}
+}
+
+static int pm_chg_get_rt_status(int irq)
+{
+	int count = 3;
+	int ret;
+
+	while ((ret =
+		pm8058_irq_get_rt_status(pm8058_chg.pm_chip, irq)) == -EAGAIN
+	       && count--) {
+		dev_info(pm8058_chg.dev, "%s trycount=%d\n", __func__, count);
+		cpu_relax();
+	}
+	if (ret == -EAGAIN)
+		return 0;
+	else
+		return ret;
+}
+
+static int is_chg_plugged_in(void)
+{
+	return pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[CHGVAL_IRQ]);
+}
+
+#ifdef DEBUG
+static void __dump_chg_regs(void)
+{
+	u8 temp;
+	int temp2;
+
+	pm8058_read(pm8058_chg.pm_chip, PM8058_CHG_CNTRL, &temp, 1);
+	dev_dbg(pm8058_chg.dev, "PM8058_CHG_CNTRL = 0x%x\n", temp);
+	pm8058_read(pm8058_chg.pm_chip, PM8058_CHG_CNTRL_2, &temp, 1);
+	dev_dbg(pm8058_chg.dev, "PM8058_CHG_CNTRL_2 = 0x%x\n", temp);
+	pm8058_read(pm8058_chg.pm_chip, PM8058_CHG_VMAX_SEL, &temp, 1);
+	dev_dbg(pm8058_chg.dev, "PM8058_CHG_VMAX_SEL = 0x%x\n", temp);
+	pm8058_read(pm8058_chg.pm_chip, PM8058_CHG_VBAT_DET, &temp, 1);
+	dev_dbg(pm8058_chg.dev, "PM8058_CHG_VBAT_DET = 0x%x\n", temp);
+	pm8058_read(pm8058_chg.pm_chip, PM8058_CHG_IMAX, &temp, 1);
+	dev_dbg(pm8058_chg.dev, "PM8058_CHG_IMAX = 0x%x\n", temp);
+	pm8058_read(pm8058_chg.pm_chip, PM8058_CHG_TRICKLE, &temp, 1);
+	dev_dbg(pm8058_chg.dev, "PM8058_CHG_TRICKLE = 0x%x\n", temp);
+	pm8058_read(pm8058_chg.pm_chip, PM8058_CHG_ITERM, &temp, 1);
+	dev_dbg(pm8058_chg.dev, "PM8058_CHG_ITERM = 0x%x\n", temp);
+	pm8058_read(pm8058_chg.pm_chip, PM8058_CHG_TTRKL_MAX, &temp, 1);
+	dev_dbg(pm8058_chg.dev, "PM8058_CHG_TTRKL_MAX = 0x%x\n", temp);
+	pm8058_read(pm8058_chg.pm_chip, PM8058_CHG_TCHG_MAX, &temp, 1);
+	dev_dbg(pm8058_chg.dev, "PM8058_CHG_TCHG_MAX = 0x%x\n", temp);
+	pm8058_read(pm8058_chg.pm_chip, PM8058_CHG_TEMP_THRESH, &temp, 1);
+	dev_dbg(pm8058_chg.dev, "PM8058_CHG_TEMP_THRESH = 0x%x\n", temp);
+	pm8058_read(pm8058_chg.pm_chip, PM8058_CHG_TEMP_REG, &temp, 1);
+	dev_dbg(pm8058_chg.dev, "PM8058_CHG_TEMP_REG = 0x%x\n", temp);
+	pm8058_read(pm8058_chg.pm_chip, PM8058_CHG_PULSE, &temp, 1);
+	dev_dbg(pm8058_chg.dev, "PM8058_CHG_PULSE = 0x%x\n", temp);
+
+	pm8058_read(pm8058_chg.pm_chip, PM8058_CHG_STATUS_CLEAR_IRQ_1,
+		    &temp, 1);
+	dev_dbg(pm8058_chg.dev, "PM8058_CHG_STATUS_CLEAR_IRQ_1 = 0x%x\n", temp);
+	pm8058_read(pm8058_chg.pm_chip, PM8058_CHG_STATUS_CLEAR_IRQ_3,
+		    &temp, 1);
+	dev_dbg(pm8058_chg.dev, "PM8058_CHG_STATUS_CLEAR_IRQ_3 = 0x%x\n", temp);
+	pm8058_read(pm8058_chg.pm_chip, PM8058_CHG_STATUS_CLEAR_IRQ_10,
+		    &temp, 1);
+	dev_dbg(pm8058_chg.dev, "PM8058_CHG_STATUS_CLEAR_IRQ_10 = 0x%x\n",
+		temp);
+	pm8058_read(pm8058_chg.pm_chip, PM8058_CHG_STATUS_CLEAR_IRQ_11,
+		    &temp, 1);
+	dev_dbg(pm8058_chg.dev, "PM8058_CHG_STATUS_CLEAR_IRQ_11 = 0x%x\n",
+		temp);
+
+	pm8058_read(pm8058_chg.pm_chip, PM8058_CHG_MASK_IRQ_1, &temp, 1);
+	dev_dbg(pm8058_chg.dev, "PM8058_CHG_MASK_IRQ_1 = 0x%x\n", temp);
+	pm8058_read(pm8058_chg.pm_chip, PM8058_CHG_MASK_IRQ_3, &temp, 1);
+	dev_dbg(pm8058_chg.dev, "PM8058_CHG_MASK_IRQ_3 = 0x%x\n", temp);
+	pm8058_read(pm8058_chg.pm_chip, PM8058_CHG_MASK_IRQ_10, &temp, 1);
+	dev_dbg(pm8058_chg.dev, "PM8058_CHG_MASK_IRQ_10 = 0x%x\n", temp);
+	pm8058_read(pm8058_chg.pm_chip, PM8058_CHG_MASK_IRQ_11, &temp, 1);
+	dev_dbg(pm8058_chg.dev, "PM8058_CHG_MASK_IRQ_11 = 0x%x\n", temp);
+
+	temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[CHGVAL_IRQ]);
+	dev_dbg(pm8058_chg.dev, "CHGVAL_IRQ = %d\n", temp2);
+
+	temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[CHGINVAL_IRQ]);
+	dev_dbg(pm8058_chg.dev, "CHGINVAL_IRQ = %d\n", temp2);
+
+	temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[VBATDET_LOW_IRQ]);
+	dev_dbg(pm8058_chg.dev, "VBATDET_LOW_IRQ= %d\n", temp2);
+
+	temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[VCP_IRQ]);
+	dev_dbg(pm8058_chg.dev, "VCP_IRQ= %d\n", temp2);
+
+	temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[CHGILIM_IRQ]);
+	dev_dbg(pm8058_chg.dev, "CHGILIM_IRQ= %d\n", temp2);
+
+	temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[ATC_DONE_IRQ]);
+	dev_dbg(pm8058_chg.dev, "ATC_DONE_IRQ= %d\n", temp2);
+
+	temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[ATCFAIL_IRQ]);
+	dev_dbg(pm8058_chg.dev, "ATCFAIL_IRQ= %d\n", temp2);
+
+	temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[AUTO_CHGDONE_IRQ]);
+	dev_dbg(pm8058_chg.dev, "AUTO_CHGDONE_IRQ= %d\n", temp2);
+
+	temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[AUTO_CHGFAIL_IRQ]);
+	dev_dbg(pm8058_chg.dev, "AUTO_CHGFAIL_IRQ= %d\n", temp2);
+
+	temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[CHGSTATE_IRQ]);
+	dev_dbg(pm8058_chg.dev, "CHGSTATE_IRQ= %d\n", temp2);
+
+	temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[FASTCHG_IRQ]);
+	dev_dbg(pm8058_chg.dev, "FASTCHG_IRQ= %d\n", temp2);
+
+	temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[CHG_END_IRQ]);
+	dev_dbg(pm8058_chg.dev, "CHG_END_IRQ= %d\n", temp2);
+
+	temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[BATTTEMP_IRQ]);
+	dev_dbg(pm8058_chg.dev, "BATTTEMP_IRQ= %d\n", temp2);
+
+	temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[CHGHOT_IRQ]);
+	dev_dbg(pm8058_chg.dev, "CHGHOT_IRQ= %d\n", temp2);
+
+	temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[CHGTLIMIT_IRQ]);
+	dev_dbg(pm8058_chg.dev, "CHGTLIMIT_IRQ= %d\n", temp2);
+
+	temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[CHG_GONE_IRQ]);
+	dev_dbg(pm8058_chg.dev, "CHG_GONE_IRQ= %d\n", temp2);
+
+	temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[VCPMAJOR_IRQ]);
+	dev_dbg(pm8058_chg.dev, "VCPMAJOR_IRQ= %d\n", temp2);
+
+	temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[VBATDET_IRQ]);
+	dev_dbg(pm8058_chg.dev, "VBATDET_IRQ= %d\n", temp2);
+
+	temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[BATFET_IRQ]);
+	dev_dbg(pm8058_chg.dev, "BATFET_IRQ= %d\n", temp2);
+
+	temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[BATT_REPLACE_IRQ]);
+	dev_dbg(pm8058_chg.dev, "BATT_REPLACE_IRQ= %d\n", temp2);
+
+	temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[BATTCONNECT_IRQ]);
+	dev_dbg(pm8058_chg.dev, "BATTCONNECT_IRQ= %d\n", temp2);
+}
+#else
+static inline void __dump_chg_regs(void)
+{
+}
+#endif
+
+/* SSBI register access helper functions */
+static int pm_chg_suspend(int value)
+{
+	u8 temp;
+	int ret;
+
+	ret = pm8058_read(pm8058_chg.pm_chip, PM8058_CHG_CNTRL, &temp, 1);
+	if (ret)
+		return ret;
+	if (value)
+		temp |= BIT(CHG_USB_SUSPEND);
+	else
+		temp &= ~BIT(CHG_USB_SUSPEND);
+
+	return pm8058_write(pm8058_chg.pm_chip, PM8058_CHG_CNTRL, &temp, 1);
+}
+
+static int pm_chg_auto_disable(int value)
+{
+	u8 temp;
+	int ret;
+
+	ret = pm8058_read(pm8058_chg.pm_chip, PM8058_CHG_CNTRL_2, &temp, 1);
+	if (ret)
+		return ret;
+	if (value)
+		temp |= BIT(CHARGE_AUTO_DIS);
+	else
+		temp &= ~BIT(CHARGE_AUTO_DIS);
+
+	return pm8058_write(pm8058_chg.pm_chip, PM8058_CHG_CNTRL_2, &temp, 1);
+}
+
+static int pm_chg_batt_temp_disable(int value)
+{
+	u8 temp;
+	int ret;
+
+	ret = pm8058_read(pm8058_chg.pm_chip, PM8058_CHG_CNTRL_2, &temp, 1);
+	if (ret)
+		return ret;
+	if (value)
+		temp |= BIT(CHG_BATT_TEMP_DIS);
+	else
+		temp &= ~BIT(CHG_BATT_TEMP_DIS);
+
+	return pm8058_write(pm8058_chg.pm_chip, PM8058_CHG_CNTRL_2, &temp, 1);
+}
+
+static int pm_chg_vbatdet_set(int voltage)
+{
+	u8 temp;
+	int diff;
+
+	diff = (voltage - PM8058_CHG_V_MIN_MV);
+	if (diff < 0) {
+		dev_warn(pm8058_chg.dev, "%s bad mV=%d asked to set\n",
+			 __func__, voltage);
+		return -EINVAL;
+	}
+
+	temp = diff / PM8058_CHG_V_STEP_MV;
+	dev_dbg(pm8058_chg.dev, "%s voltage=%d setting %02x\n", __func__,
+		voltage, temp);
+	return pm8058_write(pm8058_chg.pm_chip, PM8058_CHG_VBAT_DET, &temp, 1);
+}
+
+static int pm_chg_imaxsel_set(int chg_current)
+{
+	u8 temp;
+	int diff;
+
+	diff = chg_current - PM8058_CHG_I_MIN_MA;
+	if (diff < 0) {
+		dev_warn(pm8058_chg.dev, "%s bad mA=%d asked to set\n",
+			 __func__, chg_current);
+		return -EINVAL;
+	}
+	temp = diff / PM8058_CHG_I_STEP_MA;
+	/* make sure we arent writing more than 5 bits of data */
+	if (temp > 31) {
+		dev_warn(pm8058_chg.dev, "%s max mA=1500 requested mA=%d\n",
+			__func__, chg_current);
+		temp = 31;
+	}
+	return pm8058_write(pm8058_chg.pm_chip, PM8058_CHG_IMAX, &temp, 1);
+}
+
+#define PM8058_CHG_VMAX_MIN  3300
+#define PM8058_CHG_VMAX_MAX  5500
+static int pm_chg_vmaxsel_set(int voltage)
+{
+	u8 temp;
+
+	if (voltage < PM8058_CHG_VMAX_MIN || voltage > PM8058_CHG_VMAX_MAX) {
+		dev_warn(pm8058_chg.dev, "%s bad mV=%d asked to set\n",
+			 __func__, voltage);
+		return -EINVAL;
+	}
+	temp = (voltage - PM8058_CHG_V_MIN_MV) / PM8058_CHG_V_STEP_MV;
+	dev_dbg(pm8058_chg.dev, "%s mV=%d setting %02x\n", __func__, voltage,
+		temp);
+	return pm8058_write(pm8058_chg.pm_chip, PM8058_CHG_VMAX_SEL, &temp, 1);
+}
+
+static int pm_chg_failed_clear(int value)
+{
+	u8 temp;
+	int ret;
+
+	ret = pm8058_read(pm8058_chg.pm_chip, PM8058_CHG_CNTRL_2, &temp, 1);
+	if (ret)
+		return ret;
+	if (value)
+		temp |= BIT(CHG_FAILED_CLEAR);
+	else
+		temp &= ~BIT(CHG_FAILED_CLEAR);
+	return pm8058_write(pm8058_chg.pm_chip, PM8058_CHG_CNTRL_2, &temp, 1);
+}
+
+static int pm_chg_iterm_set(int chg_current)
+{
+	u8 temp;
+
+	temp = (chg_current / PM8058_CHG_I_TERM_STEP_MA) - 1;
+	return pm8058_write(pm8058_chg.pm_chip, PM8058_CHG_ITERM, &temp, 1);
+}
+
+static int pm_chg_tchg_set(int minutes)
+{
+	u8 temp;
+
+	temp = (minutes >> PM8058_CHG_T_TCHG_SHIFT) - 1;
+	return pm8058_write(pm8058_chg.pm_chip, PM8058_CHG_TCHG_MAX, &temp, 1);
+}
+
+static int pm_chg_ttrkl_set(int minutes)
+{
+	u8 temp;
+
+	temp = minutes - 1;
+	return pm8058_write(pm8058_chg.pm_chip, PM8058_CHG_TTRKL_MAX, &temp, 1);
+}
+
+static int pm_chg_enum_done_enable(int value)
+{
+	u8 temp;
+	int ret;
+
+	ret = pm8058_read(pm8058_chg.pm_chip, PM8058_CHG_CNTRL_2, &temp, 1);
+	if (ret)
+		return ret;
+	if (value)
+		temp |= BIT(ENUM_DONE);
+	else
+		temp &= ~BIT(ENUM_DONE);
+
+	return pm8058_write(pm8058_chg.pm_chip, PM8058_CHG_CNTRL_2, &temp, 1);
+}
+
+static uint32_t get_fsm_state(void)
+{
+	u8 temp;
+
+	temp = 0x00;
+	pm8058_write(pm8058_chg.pm_chip, PM8058_CHG_TEST_3, &temp, 1);
+	pm8058_read(pm8058_chg.pm_chip, PM8058_CHG_TEST_3, &temp, 1);
+	return (uint32_t)temp;
+}
+
+static int get_fsm_status(void *data, u64 * val)
+{
+	*val = get_fsm_state();
+	return 0;
+}
+
+static int __pm8058_start_charging(int chg_current, int termination_current,
+				   int time)
+{
+	int ret = 0;
+
+	if (pm8058_chg.disabled)
+		goto out;
+
+	dev_info(pm8058_chg.dev, "%s %dmA %dmin\n",
+			__func__, chg_current, time);
+
+	ret = pm_chg_auto_disable(1);
+	if (ret)
+		goto out;
+
+	ret = pm_chg_suspend(0);
+	if (ret)
+		goto out;
+
+	ret = pm_chg_imaxsel_set(chg_current);
+	if (ret)
+		goto out;
+
+	ret = pm_chg_failed_clear(1);
+	if (ret)
+		goto out;
+
+	ret = pm_chg_iterm_set(termination_current);
+	if (ret)
+		goto out;
+
+	ret = pm_chg_tchg_set(time);
+	if (ret)
+		goto out;
+
+	ret = pm_chg_ttrkl_set(AUTO_CHARGING_TRICKLE_TIME_MINUTES);
+	if (ret)
+		goto out;
+
+	ret = pm_chg_batt_temp_disable(0);
+	if (ret)
+		goto out;
+
+	if (pm8058_chg.voter == NULL)
+		pm8058_chg.voter = msm_xo_get(MSM_XO_TCXO_D1, "pm8058_charger");
+	msm_xo_mode_vote(pm8058_chg.voter, MSM_XO_MODE_ON);
+
+	ret = pm_chg_enum_done_enable(1);
+	if (ret)
+		goto out;
+
+	wmb();
+
+	ret = pm_chg_auto_disable(0);
+	if (ret)
+		goto out;
+
+	/* wait for the enable to update interrupt status*/
+	msleep(20);
+
+	pm8058_chg_enable_irq(AUTO_CHGFAIL_IRQ);
+	pm8058_chg_enable_irq(CHGHOT_IRQ);
+	pm8058_chg_enable_irq(AUTO_CHGDONE_IRQ);
+	pm8058_chg_enable_irq(CHG_END_IRQ);
+	pm8058_chg_enable_irq(CHGSTATE_IRQ);
+
+out:
+	return ret;
+}
+
+static void chg_done_cleanup(void)
+{
+	dev_info(pm8058_chg.dev, "%s notify pm8058 charging completion"
+		"\n", __func__);
+
+	pm8058_chg_disable_irq(AUTO_CHGDONE_IRQ);
+	cancel_delayed_work_sync(&pm8058_chg.veoc_begin_work);
+	cancel_delayed_work_sync(&pm8058_chg.check_vbat_low_work);
+
+	pm8058_chg_disable_irq(CHG_END_IRQ);
+
+	pm8058_chg_disable_irq(VBATDET_LOW_IRQ);
+	pm8058_chg_disable_irq(VBATDET_IRQ);
+	pm8058_chg.waiting_for_veoc = 0;
+	pm8058_chg.waiting_for_topoff = 0;
+
+	pm_chg_auto_disable(1);
+
+	msm_charger_notify_event(&usb_hw_chg, CHG_DONE_EVENT);
+}
+
+static void chg_done_check_work(struct work_struct *work)
+{
+	chg_done_cleanup();
+}
+
+static void charging_check_work(struct work_struct *work)
+{
+	uint32_t fsm_state = get_fsm_state();
+	int rc;
+
+	switch (fsm_state) {
+	/* We're charging, so disarm alarm */
+	case 2:
+	case 7:
+	case 8:
+		rc = pm8058_batt_alarm_state_set(0, 0);
+		if (rc)
+			dev_err(pm8058_chg.dev,
+				"%s: unable to set alarm state\n", __func__);
+		break;
+	default:
+		/* Still not charging, so update driver state */
+		chg_done_cleanup();
+		break;
+	};
+}
+
+static int pm8058_start_charging(struct msm_hardware_charger *hw_chg,
+				 int chg_voltage, int chg_current)
+{
+	int vbat_higher_than_vbatdet;
+	int ret = 0;
+
+	cancel_delayed_work_sync(&pm8058_chg.charging_check_work);
+
+	/*
+	 * adjust the max current for PC USB connection - set the higher limit
+	 * to 450 and make sure we never cross it
+	 */
+	if (chg_current == 500)
+		chg_current = 450;
+	pm8058_chg.current_charger_current = chg_current;
+	pm8058_chg_enable_irq(FASTCHG_IRQ);
+
+	ret = pm_chg_vmaxsel_set(chg_voltage);
+	if (ret)
+		goto out;
+
+	/* set vbat to  CC to CV threshold */
+	ret = pm_chg_vbatdet_set(AUTO_CHARGING_VBATDET);
+	if (ret)
+		goto out;
+
+	pm8058_chg.vbatdet = AUTO_CHARGING_VBATDET;
+	/*
+	 * get the state of vbat and if it is higher than
+	 * AUTO_CHARGING_VBATDET we start the veoc start timer
+	 * else wait for the voltage to go to AUTO_CHARGING_VBATDET
+	 * and then start the 90 min timer
+	 */
+	vbat_higher_than_vbatdet =
+	    pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[VBATDET_IRQ]);
+	if (vbat_higher_than_vbatdet) {
+		/*
+		 * we are in constant voltage phase of charging
+		 * IEOC should happen withing 90 mins of this instant
+		 * else we enable VEOC
+		 */
+		dev_info(pm8058_chg.dev, "%s begin veoc timer\n", __func__);
+		schedule_delayed_work(&pm8058_chg.veoc_begin_work,
+				      round_jiffies_relative(msecs_to_jiffies
+				     (AUTO_CHARGING_VEOC_BEGIN_TIME_MS)));
+	} else
+		pm8058_chg_enable_irq(VBATDET_IRQ);
+
+	ret = __pm8058_start_charging(chg_current, AUTO_CHARGING_IEOC_ITERM,
+				AUTO_CHARGING_FAST_TIME_MAX_MINUTES);
+	pm8058_chg.current_charger_current = chg_current;
+
+	/*
+	 * We want to check the FSM state to verify we're charging. We must
+	 * wait before doing this to allow the VBATDET to settle. The worst
+	 * case for this is two seconds. The batt alarm does not have this
+	 * delay.
+	 */
+	schedule_delayed_work(&pm8058_chg.charging_check_work,
+				      round_jiffies_relative(msecs_to_jiffies
+			     (AUTO_CHARGING_VBATDET_DEBOUNCE_TIME_MS)));
+
+out:
+	return ret;
+}
+
+static void veoc_begin_work(struct work_struct *work)
+{
+	/* we have been doing CV for 90mins with no signs of IEOC
+	 * start checking for VEOC in addition with 16min charges*/
+	dev_info(pm8058_chg.dev, "%s begin veoc detection\n", __func__);
+	pm8058_chg.waiting_for_veoc = 1;
+	/*
+	 * disable VBATDET irq we dont need it unless we are at the end of
+	 * charge cycle
+	 */
+	pm8058_chg_disable_irq(VBATDET_IRQ);
+	__pm8058_start_charging(pm8058_chg.current_charger_current,
+				AUTO_CHARGING_VEOC_ITERM,
+				AUTO_CHARGING_VEOC_TCHG);
+}
+
+static void vbat_low_work(struct work_struct *work)
+{
+	/*
+	 * It has been one minute and the battery still holds voltage
+	 * start the final topoff - charging is almost done
+	 */
+	dev_info(pm8058_chg.dev, "%s vbatt maintains for a minute"
+		"starting topoff\n", __func__);
+	pm8058_chg.waiting_for_veoc = 0;
+	pm8058_chg.waiting_for_topoff = 1;
+	pm8058_chg_disable_irq(VBATDET_LOW_IRQ);
+	pm8058_chg_disable_irq(VBATDET_IRQ);
+	__pm8058_start_charging(pm8058_chg.current_charger_current,
+				AUTO_CHARGING_VEOC_ITERM,
+				AUTO_CHARGING_VEOC_TCHG_FINAL_CYCLE);
+}
+
+
+static irqreturn_t pm8058_chg_chgval_handler(int irq, void *dev_id)
+{
+	u8 old, temp;
+	int ret;
+
+	if (is_chg_plugged_in()) {	/* this debounces it */
+		if (!pm8058_chg.present) {
+			msm_charger_notify_event(&usb_hw_chg,
+						CHG_INSERTED_EVENT);
+			pm8058_chg.present = 1;
+		}
+	} else {
+		if (pm8058_chg.present) {
+			ret = pm8058_read(pm8058_chg.pm_chip,
+						PM8058_OVP_TEST_REG,
+						&old, 1);
+			temp = old | BIT(FORCE_OVP_OFF);
+			ret = pm8058_write(pm8058_chg.pm_chip,
+						PM8058_OVP_TEST_REG,
+						&temp, 1);
+			temp = 0xFC;
+			ret = pm8058_write(pm8058_chg.pm_chip, PM8058_CHG_TEST,
+						&temp, 1);
+			/* 10 ms sleep is for the VCHG to discharge */
+			msleep(10);
+			temp = 0xF0;
+			ret = pm8058_write(pm8058_chg.pm_chip,
+						PM8058_CHG_TEST,
+						&temp, 1);
+			ret = pm8058_write(pm8058_chg.pm_chip,
+						PM8058_OVP_TEST_REG,
+						&old, 1);
+
+			pm_chg_enum_done_enable(0);
+			pm_chg_auto_disable(1);
+			msm_charger_notify_event(&usb_hw_chg,
+						CHG_REMOVED_EVENT);
+			pm8058_chg.present = 0;
+		}
+	}
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t pm8058_chg_chginval_handler(int irq, void *dev_id)
+{
+	u8 old, temp;
+	int ret;
+
+	if (pm8058_chg.present) {
+		pm8058_chg_disable_irq(CHGINVAL_IRQ);
+
+		pm_chg_enum_done_enable(0);
+		pm_chg_auto_disable(1);
+		ret = pm8058_read(pm8058_chg.pm_chip,
+				PM8058_OVP_TEST_REG, &old, 1);
+		temp = old | BIT(FORCE_OVP_OFF);
+		ret = pm8058_write(pm8058_chg.pm_chip,
+				PM8058_OVP_TEST_REG, &temp, 1);
+		temp = 0xFC;
+		ret = pm8058_write(pm8058_chg.pm_chip,
+				PM8058_CHG_TEST, &temp, 1);
+		/* 10 ms sleep is for the VCHG to discharge */
+		msleep(10);
+		temp = 0xF0;
+		ret = pm8058_write(pm8058_chg.pm_chip,
+				PM8058_CHG_TEST, &temp, 1);
+		ret = pm8058_write(pm8058_chg.pm_chip,
+				PM8058_OVP_TEST_REG, &old, 1);
+
+		if (!is_chg_plugged_in()) {
+			msm_charger_notify_event(&usb_hw_chg,
+					CHG_REMOVED_EVENT);
+			pm8058_chg.present = 0;
+		} else {
+			/* was a fake */
+			pm8058_chg_enable_irq(CHGINVAL_IRQ);
+		}
+	}
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t pm8058_chg_auto_chgdone_handler(int irq, void *dev_id)
+{
+	dev_info(pm8058_chg.dev, "%s waiting a sec to confirm\n",
+		__func__);
+	pm8058_chg_disable_irq(VBATDET_IRQ);
+	if (!delayed_work_pending(&pm8058_chg.chg_done_check_work)) {
+		schedule_delayed_work(&pm8058_chg.chg_done_check_work,
+				      round_jiffies_relative(msecs_to_jiffies
+			     (AUTO_CHARGING_DONE_CHECK_TIME_MS)));
+	}
+	return IRQ_HANDLED;
+}
+
+/* can only happen with the pmic charger when it has been charing
+ * for either 16 mins wating for VEOC or 32 mins for topoff
+ * without a IEOC indication */
+static irqreturn_t pm8058_chg_auto_chgfail_handler(int irq, void *dev_id)
+{
+	pm8058_chg_disable_irq(AUTO_CHGFAIL_IRQ);
+
+	if (pm8058_chg.waiting_for_topoff == 1) {
+		dev_info(pm8058_chg.dev, "%s topoff done, charging done\n",
+			__func__);
+		pm8058_chg.waiting_for_topoff = 0;
+		/* notify we are done charging */
+		msm_charger_notify_event(&usb_hw_chg, CHG_DONE_EVENT);
+	} else {
+		/* start one minute timer and monitor VBATDET_LOW */
+		dev_info(pm8058_chg.dev, "%s monitoring vbat_low for a"
+			"minute\n", __func__);
+		schedule_delayed_work(&pm8058_chg.check_vbat_low_work,
+				      round_jiffies_relative(msecs_to_jiffies
+			     (AUTO_CHARGING_VEOC_VBAT_LOW_CHECK_TIME_MS)));
+
+		/* note we are waiting on veoc */
+		pm8058_chg.waiting_for_veoc = 1;
+
+		pm_chg_vbatdet_set(AUTO_CHARGING_VEOC_VBATDET);
+		pm8058_chg.vbatdet = AUTO_CHARGING_VEOC_VBATDET;
+		pm8058_chg_enable_irq(VBATDET_LOW_IRQ);
+	}
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t pm8058_chg_chgstate_handler(int irq, void *dev_id)
+{
+	u8 temp;
+
+	temp = 0x00;
+	if (!pm8058_write(pm8058_chg.pm_chip, PM8058_CHG_TEST_3, &temp, 1)) {
+		pm8058_read(pm8058_chg.pm_chip, PM8058_CHG_TEST_3, &temp, 1);
+		dev_dbg(pm8058_chg.dev, "%s state=%d\n", __func__, temp);
+	}
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t pm8058_chg_fastchg_handler(int irq, void *dev_id)
+{
+	pm8058_chg_disable_irq(FASTCHG_IRQ);
+
+	/* we have begun the fast charging state */
+	dev_info(pm8058_chg.dev, "%s begin fast charging"
+		, __func__);
+	msm_charger_notify_event(&usb_hw_chg, CHG_BATT_BEGIN_FAST_CHARGING);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t pm8058_chg_batttemp_handler(int irq, void *dev_id)
+{
+	int ret;
+
+	/* we could get temperature
+	 * interrupt when the battery is plugged out
+	 */
+	ret = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[BATTCONNECT_IRQ]);
+	if (ret) {
+		msm_charger_notify_event(&usb_hw_chg, CHG_BATT_REMOVED);
+	} else {
+		/* read status to determine we are inrange or outofrange */
+		ret =
+		    pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[BATTTEMP_IRQ]);
+		if (ret)
+			msm_charger_notify_event(&usb_hw_chg,
+						 CHG_BATT_TEMP_OUTOFRANGE);
+		else
+			msm_charger_notify_event(&usb_hw_chg,
+						 CHG_BATT_TEMP_INRANGE);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t pm8058_chg_vbatdet_handler(int irq, void *dev_id)
+{
+	int ret;
+
+	/* settling time */
+	msleep(20);
+	ret = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[VBATDET_IRQ]);
+
+	if (ret) {
+		if (pm8058_chg.vbatdet == AUTO_CHARGING_VBATDET
+			&& !delayed_work_pending(&pm8058_chg.veoc_begin_work)) {
+			/*
+			 * we are in constant voltage phase of charging
+			 * IEOC should happen withing 90 mins of this instant
+			 * else we enable VEOC
+			 */
+			dev_info(pm8058_chg.dev, "%s entered constant voltage"
+				"begin veoc timer\n", __func__);
+			schedule_delayed_work(&pm8058_chg.veoc_begin_work,
+				      round_jiffies_relative
+				      (msecs_to_jiffies
+				      (AUTO_CHARGING_VEOC_BEGIN_TIME_MS)));
+		}
+	} else {
+		if (pm8058_chg.vbatdet == AUTO_CHARGING_VEOC_VBATDET) {
+			cancel_delayed_work_sync(
+				&pm8058_chg.check_vbat_low_work);
+
+			if (pm8058_chg.waiting_for_topoff
+			    || pm8058_chg.waiting_for_veoc) {
+				/*
+				 * the battery dropped its voltage below 4100
+				 * around a minute charge the battery for 16
+				 * mins and check vbat again for a minute
+				 */
+				dev_info(pm8058_chg.dev, "%s batt dropped vlt"
+					"within a minute\n", __func__);
+				pm8058_chg.waiting_for_topoff = 0;
+				pm8058_chg.waiting_for_veoc = 1;
+				pm8058_chg_disable_irq(VBATDET_IRQ);
+				__pm8058_start_charging(pm8058_chg.
+						current_charger_current,
+						AUTO_CHARGING_VEOC_ITERM,
+						AUTO_CHARGING_VEOC_TCHG);
+			}
+		}
+	}
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t pm8058_chg_batt_replace_handler(int irq, void *dev_id)
+{
+	int ret;
+
+	pm8058_chg_disable_irq(BATT_REPLACE_IRQ);
+	ret = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[BATT_REPLACE_IRQ]);
+	if (ret) {
+		msm_charger_notify_event(&usb_hw_chg, CHG_BATT_INSERTED);
+		/*
+		 * battery is present enable batt removal
+		 * and batt temperatture interrupt
+		 */
+		pm8058_chg_enable_irq(BATTCONNECT_IRQ);
+	}
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t pm8058_chg_battconnect_handler(int irq, void *dev_id)
+{
+	int ret;
+
+	ret = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[BATTCONNECT_IRQ]);
+	if (ret) {
+		msm_charger_notify_event(&usb_hw_chg, CHG_BATT_REMOVED);
+	} else {
+		msm_charger_notify_event(&usb_hw_chg, CHG_BATT_INSERTED);
+		pm8058_chg_enable_irq(BATTTEMP_IRQ);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int get_rt_status(void *data, u64 * val)
+{
+	int i = (int)data;
+	int ret;
+
+	ret = pm_chg_get_rt_status(i);
+	*val = ret;
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(rt_fops, get_rt_status, NULL, "%llu\n");
+DEFINE_SIMPLE_ATTRIBUTE(fsm_fops, get_fsm_status, NULL, "%llu\n");
+
+static void free_irqs(void)
+{
+	int i;
+
+	for (i = 0; i < PMIC_CHG_MAX_INTS; i++)
+		if (pm8058_chg.pmic_chg_irq[i]) {
+			free_irq(pm8058_chg.pmic_chg_irq[i], NULL);
+			pm8058_chg.pmic_chg_irq[i] = 0;
+		}
+}
+
+static int __devinit request_irqs(struct platform_device *pdev)
+{
+	struct resource *res;
+	int ret;
+
+	ret = 0;
+	bitmap_fill(pm8058_chg.enabled_irqs, PMIC_CHG_MAX_INTS);
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "CHGVAL");
+	if (res == NULL) {
+		dev_err(pm8058_chg.dev,
+			"%s:couldnt find resource CHGVAL\n", __func__);
+		goto err_out;
+	} else {
+		ret = request_threaded_irq(res->start, NULL,
+				  pm8058_chg_chgval_handler,
+				  IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+				  res->name, NULL);
+		if (ret < 0) {
+			dev_err(pm8058_chg.dev, "%s:couldnt request %d %d\n",
+				__func__, res->start, ret);
+			goto err_out;
+		} else {
+			pm8058_chg.pmic_chg_irq[CHGVAL_IRQ] = res->start;
+			pm8058_chg_disable_irq(CHGVAL_IRQ);
+			enable_irq_wake(pm8058_chg.pmic_chg_irq[CHGVAL_IRQ]);
+		}
+	}
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "CHGINVAL");
+	if (res == NULL) {
+		dev_err(pm8058_chg.dev,
+			"%s:couldnt find resource CHGINVAL\n", __func__);
+		goto err_out;
+	} else {
+		ret = request_threaded_irq(res->start, NULL,
+				  pm8058_chg_chginval_handler,
+				  IRQF_TRIGGER_RISING, res->name, NULL);
+		if (ret < 0) {
+			dev_err(pm8058_chg.dev, "%s:couldnt request %d %d\n",
+				__func__, res->start, ret);
+			goto err_out;
+		} else {
+			pm8058_chg.pmic_chg_irq[CHGINVAL_IRQ] = res->start;
+			pm8058_chg_disable_irq(CHGINVAL_IRQ);
+		}
+	}
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+					   "AUTO_CHGDONE");
+	if (res == NULL) {
+		dev_err(pm8058_chg.dev,
+			"%s:couldnt find resource AUTO_CHGDONE\n", __func__);
+		goto err_out;
+	} else {
+		ret = request_threaded_irq(res->start, NULL,
+				  pm8058_chg_auto_chgdone_handler,
+				  IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+				  res->name, NULL);
+		if (ret < 0) {
+			dev_err(pm8058_chg.dev, "%s:couldnt request %d %d\n",
+				__func__, res->start, ret);
+			goto err_out;
+		} else {
+			pm8058_chg.pmic_chg_irq[AUTO_CHGDONE_IRQ] = res->start;
+			pm8058_chg_disable_irq(AUTO_CHGDONE_IRQ);
+		}
+	}
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+					   "AUTO_CHGFAIL");
+	if (res == NULL) {
+		dev_err(pm8058_chg.dev,
+			"%s:couldnt find resource AUTO_CHGFAIL\n", __func__);
+		goto err_out;
+	} else {
+		ret = request_threaded_irq(res->start, NULL,
+				  pm8058_chg_auto_chgfail_handler,
+				  IRQF_TRIGGER_RISING, res->name, NULL);
+		if (ret < 0) {
+			dev_err(pm8058_chg.dev, "%s:couldnt request %d %d\n",
+				__func__, res->start, ret);
+			goto err_out;
+		} else {
+			pm8058_chg.pmic_chg_irq[AUTO_CHGFAIL_IRQ] = res->start;
+			pm8058_chg_disable_irq(AUTO_CHGFAIL_IRQ);
+		}
+	}
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "CHGSTATE");
+	if (res == NULL) {
+		dev_err(pm8058_chg.dev,
+			"%s:couldnt find resource CHGSTATE\n", __func__);
+		goto err_out;
+	} else {
+		ret = request_threaded_irq(res->start, NULL,
+				  pm8058_chg_chgstate_handler,
+				  IRQF_TRIGGER_RISING, res->name, NULL);
+		if (ret < 0) {
+			dev_err(pm8058_chg.dev, "%s:couldnt request %d %d\n",
+				__func__, res->start, ret);
+			goto err_out;
+		} else {
+			pm8058_chg.pmic_chg_irq[CHGSTATE_IRQ] = res->start;
+			pm8058_chg_disable_irq(CHGSTATE_IRQ);
+		}
+	}
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "FASTCHG");
+	if (res == NULL) {
+		dev_err(pm8058_chg.dev,
+			"%s:couldnt find resource FASTCHG\n", __func__);
+		goto err_out;
+	} else {
+		ret = request_threaded_irq(res->start, NULL,
+				  pm8058_chg_fastchg_handler,
+				  IRQF_TRIGGER_RISING, res->name, NULL);
+		if (ret < 0) {
+			dev_err(pm8058_chg.dev, "%s:couldnt request %d %d\n",
+				__func__, res->start, ret);
+			goto err_out;
+		} else {
+			pm8058_chg.pmic_chg_irq[FASTCHG_IRQ] = res->start;
+			pm8058_chg_disable_irq(FASTCHG_IRQ);
+		}
+	}
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "BATTTEMP");
+	if (res == NULL) {
+		dev_err(pm8058_chg.dev,
+			"%s:couldnt find resource CHG_END\n", __func__);
+		goto err_out;
+	} else {
+		ret = request_threaded_irq(res->start, NULL,
+				  pm8058_chg_batttemp_handler,
+				  IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+				  res->name, NULL);
+		if (ret < 0) {
+			dev_err(pm8058_chg.dev, "%s:couldnt request %d %d\n",
+				__func__, res->start, ret);
+			goto err_out;
+		} else {
+			pm8058_chg.pmic_chg_irq[BATTTEMP_IRQ] = res->start;
+			pm8058_chg_disable_irq(BATTTEMP_IRQ);
+		}
+	}
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+					   "BATT_REPLACE");
+	if (res == NULL) {
+		dev_err(pm8058_chg.dev,
+			"%s:couldnt find resource BATT_REPLACE\n", __func__);
+		goto err_out;
+	} else {
+		ret = request_threaded_irq(res->start, NULL,
+				  pm8058_chg_batt_replace_handler,
+				  IRQF_TRIGGER_RISING, res->name, NULL);
+		if (ret < 0) {
+			dev_err(pm8058_chg.dev, "%s:couldnt request %d %d\n",
+				__func__, res->start, ret);
+			goto err_out;
+		} else {
+			pm8058_chg.pmic_chg_irq[BATT_REPLACE_IRQ] = res->start;
+			pm8058_chg_disable_irq(BATT_REPLACE_IRQ);
+		}
+	}
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "BATTCONNECT");
+	if (res == NULL) {
+		dev_err(pm8058_chg.dev,
+			"%s:couldnt find resource BATTCONNECT\n", __func__);
+		goto err_out;
+	} else {
+		ret = request_threaded_irq(res->start, NULL,
+				  pm8058_chg_battconnect_handler,
+				  IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+				  res->name, NULL);
+		if (ret < 0) {
+			dev_err(pm8058_chg.dev, "%s:couldnt request %d %d\n",
+				__func__, res->start, ret);
+			goto err_out;
+		} else {
+			pm8058_chg.pmic_chg_irq[BATTCONNECT_IRQ] = res->start;
+			pm8058_chg_disable_irq(BATTCONNECT_IRQ);
+		}
+	}
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "VBATDET");
+	if (res == NULL) {
+		dev_err(pm8058_chg.dev,
+			"%s:couldnt find resource VBATDET\n", __func__);
+		goto err_out;
+	} else {
+		ret = request_threaded_irq(res->start, NULL,
+				  pm8058_chg_vbatdet_handler,
+				  IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+				  res->name, NULL);
+		if (ret < 0) {
+			dev_err(pm8058_chg.dev, "%s:couldnt request %d %d\n",
+				__func__, res->start, ret);
+			goto err_out;
+		} else {
+			pm8058_chg.pmic_chg_irq[VBATDET_IRQ] = res->start;
+			pm8058_chg_disable_irq(VBATDET_IRQ);
+		}
+	}
+
+	return 0;
+
+err_out:
+	free_irqs();
+	return -EINVAL;
+}
+
+static int pm8058_get_charge_batt(void)
+{
+	u8 temp;
+	int rc;
+
+	rc = pm8058_read(pm8058_chg.pm_chip, PM8058_CHG_CNTRL, &temp, 1);
+	if (rc)
+		return rc;
+
+	temp &= BIT(CHG_CHARGE_BAT);
+	if (temp)
+		temp = 1;
+	return temp;
+}
+EXPORT_SYMBOL(pm8058_get_charge_batt);
+
+static int pm8058_set_charge_batt(int on)
+{
+	u8 temp;
+	int rc;
+
+	rc = pm8058_read(pm8058_chg.pm_chip, PM8058_CHG_CNTRL, &temp, 1);
+	if (rc)
+		return rc;
+	if (on)
+		temp |= BIT(CHG_CHARGE_BAT);
+	else
+		temp &= ~BIT(CHG_CHARGE_BAT);
+
+	return pm8058_write(pm8058_chg.pm_chip, PM8058_CHG_CNTRL, &temp, 1);
+
+}
+EXPORT_SYMBOL(pm8058_set_charge_batt);
+
+static int get_charge_batt(void *data, u64 * val)
+{
+	int ret;
+
+	ret = pm8058_get_charge_batt();
+	if (ret < 0)
+		return ret;
+
+	*val = ret;
+	return 0;
+}
+
+static int set_charge_batt(void *data, u64 val)
+{
+	return pm8058_set_charge_batt(val);
+}
+DEFINE_SIMPLE_ATTRIBUTE(fet_fops, get_charge_batt, set_charge_batt, "%llu\n");
+
+static void pm8058_chg_determine_initial_state(void)
+{
+	if (is_chg_plugged_in()) {
+		pm8058_chg.present = 1;
+		msm_charger_notify_event(&usb_hw_chg, CHG_INSERTED_EVENT);
+		dev_info(pm8058_chg.dev, "%s charger present\n", __func__);
+	} else {
+		pm8058_chg.present = 0;
+		dev_info(pm8058_chg.dev, "%s charger absent\n", __func__);
+	}
+	pm8058_chg_enable_irq(CHGVAL_IRQ);
+}
+
+static int pm8058_stop_charging(struct msm_hardware_charger *hw_chg)
+{
+	int ret;
+
+	dev_info(pm8058_chg.dev, "%s stopping charging\n", __func__);
+	cancel_delayed_work_sync(&pm8058_chg.veoc_begin_work);
+	cancel_delayed_work_sync(&pm8058_chg.check_vbat_low_work);
+	cancel_delayed_work_sync(&pm8058_chg.chg_done_check_work);
+	cancel_delayed_work_sync(&pm8058_chg.charging_check_work);
+
+	ret = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[FASTCHG_IRQ]);
+	if (ret == 1)
+		pm_chg_suspend(1);
+	else
+		dev_err(pm8058_chg.dev,
+			"%s called when not fast-charging\n", __func__);
+
+	pm_chg_failed_clear(1);
+
+	pm8058_chg.waiting_for_veoc = 0;
+	pm8058_chg.waiting_for_topoff = 0;
+
+	/* disable the irqs enabled while charging */
+	pm8058_chg_disable_irq(AUTO_CHGFAIL_IRQ);
+	pm8058_chg_disable_irq(CHGHOT_IRQ);
+	pm8058_chg_disable_irq(AUTO_CHGDONE_IRQ);
+	pm8058_chg_disable_irq(FASTCHG_IRQ);
+	pm8058_chg_disable_irq(CHG_END_IRQ);
+	pm8058_chg_disable_irq(VBATDET_IRQ);
+	pm8058_chg_disable_irq(VBATDET_LOW_IRQ);
+	if (pm8058_chg.voter)
+		msm_xo_mode_vote(pm8058_chg.voter, MSM_XO_MODE_OFF);
+
+	return 0;
+}
+
+static int get_status(void *data, u64 * val)
+{
+	*val = pm8058_chg.current_charger_current;
+	return 0;
+}
+
+static int set_status(void *data, u64 val)
+{
+
+	pm8058_chg.current_charger_current = val;
+	if (pm8058_chg.current_charger_current)
+		pm8058_start_charging(NULL,
+			AUTO_CHARGING_VMAXSEL,
+			pm8058_chg.current_charger_current);
+	else
+		pm8058_stop_charging(NULL);
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(chg_fops, get_status, set_status, "%llu\n");
+
+static int set_disable_status_param(const char *val, struct kernel_param *kp)
+{
+	int ret;
+
+	ret = param_set_int(val, kp);
+	if (ret)
+		return ret;
+
+	if (pm8058_chg.inited && pm8058_chg.disabled) {
+		/*
+		 * stop_charging is called during usb suspend
+		 * act as the usb is removed by disabling auto and enum
+		 */
+		pm_chg_enum_done_enable(0);
+		pm_chg_auto_disable(1);
+		pm8058_stop_charging(NULL);
+	}
+	return 0;
+}
+module_param_call(disabled, set_disable_status_param, param_get_uint,
+					&(pm8058_chg.disabled), 0644);
+
+static int pm8058_charging_switched(struct msm_hardware_charger *hw_chg)
+{
+	u8 temp;
+
+	temp = 0xA3;
+	pm8058_write(pm8058_chg.pm_chip, PM8058_CHG_TEST_2, &temp, 1);
+	temp = 0x84;
+	pm8058_write(pm8058_chg.pm_chip, PM8058_CHG_TEST_2, &temp, 1);
+	msleep(2);
+	temp = 0x80;
+	pm8058_write(pm8058_chg.pm_chip, PM8058_CHG_TEST_2, &temp, 1);
+	return 0;
+}
+
+static int get_reg(void *data, u64 * val)
+{
+	int i = (int)data;
+	int ret;
+	u8 temp;
+
+	ret = pm8058_read(pm8058_chg.pm_chip, i, &temp, 1);
+	if (ret)
+		return -EAGAIN;
+	*val = temp;
+	return 0;
+}
+
+static int set_reg(void *data, u64 val)
+{
+	int i = (int)data;
+	int ret;
+	u8 temp;
+
+	temp = (u8) val;
+	ret = pm8058_write(pm8058_chg.pm_chip, i, &temp, 1);
+	mb();
+	if (ret)
+		return -EAGAIN;
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(reg_fops, get_reg, set_reg, "%llu\n");
+
+#ifdef CONFIG_DEBUG_FS
+static void create_debugfs_entries(void)
+{
+	pm8058_chg.dent = debugfs_create_dir("pm8058_usb_chg", NULL);
+
+	if (IS_ERR(pm8058_chg.dent)) {
+		pr_err("pmic charger couldnt create debugfs dir\n");
+		return;
+	}
+
+	debugfs_create_file("CHG_CNTRL", 0644, pm8058_chg.dent,
+			    (void *)PM8058_CHG_CNTRL, &reg_fops);
+	debugfs_create_file("CHG_CNTRL_2", 0644, pm8058_chg.dent,
+			    (void *)PM8058_CHG_CNTRL_2, &reg_fops);
+	debugfs_create_file("CHG_VMAX_SEL", 0644, pm8058_chg.dent,
+			    (void *)PM8058_CHG_VMAX_SEL, &reg_fops);
+	debugfs_create_file("CHG_VBAT_DET", 0644, pm8058_chg.dent,
+			    (void *)PM8058_CHG_VBAT_DET, &reg_fops);
+	debugfs_create_file("CHG_IMAX", 0644, pm8058_chg.dent,
+			    (void *)PM8058_CHG_IMAX, &reg_fops);
+	debugfs_create_file("CHG_TRICKLE", 0644, pm8058_chg.dent,
+			    (void *)PM8058_CHG_TRICKLE, &reg_fops);
+	debugfs_create_file("CHG_ITERM", 0644, pm8058_chg.dent,
+			    (void *)PM8058_CHG_ITERM, &reg_fops);
+	debugfs_create_file("CHG_TTRKL_MAX", 0644, pm8058_chg.dent,
+			    (void *)PM8058_CHG_TTRKL_MAX, &reg_fops);
+	debugfs_create_file("CHG_TCHG_MAX", 0644, pm8058_chg.dent,
+			    (void *)PM8058_CHG_TCHG_MAX, &reg_fops);
+	debugfs_create_file("CHG_TEMP_THRESH", 0644, pm8058_chg.dent,
+			    (void *)PM8058_CHG_TEMP_THRESH, &reg_fops);
+	debugfs_create_file("CHG_TEMP_REG", 0644, pm8058_chg.dent,
+			    (void *)PM8058_CHG_TEMP_REG, &reg_fops);
+
+	debugfs_create_file("FSM_STATE", 0644, pm8058_chg.dent, NULL,
+			    &fsm_fops);
+
+	debugfs_create_file("stop", 0644, pm8058_chg.dent, NULL,
+			    &chg_fops);
+
+	if (pm8058_chg.pmic_chg_irq[CHGVAL_IRQ])
+		debugfs_create_file("CHGVAL", 0444, pm8058_chg.dent,
+				    (void *)pm8058_chg.pmic_chg_irq[CHGVAL_IRQ],
+				    &rt_fops);
+
+	if (pm8058_chg.pmic_chg_irq[CHGINVAL_IRQ])
+		debugfs_create_file("CHGINVAL", 0444, pm8058_chg.dent, (void *)
+				    pm8058_chg.pmic_chg_irq[CHGINVAL_IRQ],
+				    &rt_fops);
+	if (pm8058_chg.pmic_chg_irq[CHGILIM_IRQ])
+		debugfs_create_file("CHGILIM", 0444, pm8058_chg.dent, (void *)
+				    pm8058_chg.pmic_chg_irq[CHGILIM_IRQ],
+				    &rt_fops);
+	if (pm8058_chg.pmic_chg_irq[VCP_IRQ])
+		debugfs_create_file("VCP", 0444, pm8058_chg.dent,
+				    (void *)pm8058_chg.pmic_chg_irq[VCP_IRQ],
+				    &rt_fops);
+	if (pm8058_chg.pmic_chg_irq[ATC_DONE_IRQ])
+		debugfs_create_file("ATC_DONE", 0444, pm8058_chg.dent, (void *)
+				    pm8058_chg.pmic_chg_irq[ATC_DONE_IRQ],
+				    &rt_fops);
+	if (pm8058_chg.pmic_chg_irq[ATCFAIL_IRQ])
+		debugfs_create_file("ATCFAIL", 0444, pm8058_chg.dent, (void *)
+				    pm8058_chg.pmic_chg_irq[ATCFAIL_IRQ],
+				    &rt_fops);
+	if (pm8058_chg.pmic_chg_irq[AUTO_CHGDONE_IRQ])
+		debugfs_create_file("AUTO_CHGDONE", 0444, pm8058_chg.dent,
+				    (void *)
+				    pm8058_chg.pmic_chg_irq[AUTO_CHGDONE_IRQ],
+				    &rt_fops);
+	if (pm8058_chg.pmic_chg_irq[AUTO_CHGFAIL_IRQ])
+		debugfs_create_file("AUTO_CHGFAIL", 0444, pm8058_chg.dent,
+				    (void *)
+				    pm8058_chg.pmic_chg_irq[AUTO_CHGFAIL_IRQ],
+				    &rt_fops);
+	if (pm8058_chg.pmic_chg_irq[CHGSTATE_IRQ])
+		debugfs_create_file("CHGSTATE", 0444, pm8058_chg.dent, (void *)
+				    pm8058_chg.pmic_chg_irq[CHGSTATE_IRQ],
+				    &rt_fops);
+	if (pm8058_chg.pmic_chg_irq[FASTCHG_IRQ])
+		debugfs_create_file("FASTCHG", 0444, pm8058_chg.dent, (void *)
+				    pm8058_chg.pmic_chg_irq[FASTCHG_IRQ],
+				    &rt_fops);
+	if (pm8058_chg.pmic_chg_irq[CHG_END_IRQ])
+		debugfs_create_file("CHG_END", 0444, pm8058_chg.dent, (void *)
+				    pm8058_chg.pmic_chg_irq[CHG_END_IRQ],
+				    &rt_fops);
+	if (pm8058_chg.pmic_chg_irq[BATTTEMP_IRQ])
+		debugfs_create_file("BATTTEMP", 0444, pm8058_chg.dent, (void *)
+				    pm8058_chg.pmic_chg_irq[BATTTEMP_IRQ],
+				    &rt_fops);
+	if (pm8058_chg.pmic_chg_irq[CHGHOT_IRQ])
+		debugfs_create_file("CHGHOT", 0444, pm8058_chg.dent,
+				    (void *)pm8058_chg.pmic_chg_irq[CHGHOT_IRQ],
+				    &rt_fops);
+	if (pm8058_chg.pmic_chg_irq[CHGTLIMIT_IRQ])
+		debugfs_create_file("CHGTLIMIT", 0444, pm8058_chg.dent, (void *)
+				    pm8058_chg.pmic_chg_irq[CHGTLIMIT_IRQ],
+				    &rt_fops);
+	if (pm8058_chg.pmic_chg_irq[CHG_GONE_IRQ])
+		debugfs_create_file("CHG_GONE", 0444, pm8058_chg.dent, (void *)
+				    pm8058_chg.pmic_chg_irq[CHG_GONE_IRQ],
+				    &rt_fops);
+	if (pm8058_chg.pmic_chg_irq[VCPMAJOR_IRQ])
+		debugfs_create_file("VCPMAJOR", 0444, pm8058_chg.dent, (void *)
+				    pm8058_chg.pmic_chg_irq[VCPMAJOR_IRQ],
+				    &rt_fops);
+	if (pm8058_chg.pmic_chg_irq[BATFET_IRQ])
+		debugfs_create_file("BATFET", 0444, pm8058_chg.dent,
+				    (void *)pm8058_chg.pmic_chg_irq[BATFET_IRQ],
+				    &rt_fops);
+	if (pm8058_chg.pmic_chg_irq[BATT_REPLACE_IRQ])
+		debugfs_create_file("BATT_REPLACE", 0444, pm8058_chg.dent,
+				    (void *)
+				    pm8058_chg.pmic_chg_irq[BATT_REPLACE_IRQ],
+				    &rt_fops);
+	if (pm8058_chg.pmic_chg_irq[BATTCONNECT_IRQ])
+		debugfs_create_file("BATTCONNECT", 0444, pm8058_chg.dent,
+				    (void *)
+				    pm8058_chg.pmic_chg_irq[BATTCONNECT_IRQ],
+				    &rt_fops);
+	if (pm8058_chg.pmic_chg_irq[VBATDET_IRQ])
+		debugfs_create_file("VBATDET", 0444, pm8058_chg.dent, (void *)
+				    pm8058_chg.pmic_chg_irq[VBATDET_IRQ],
+				    &rt_fops);
+	if (pm8058_chg.pmic_chg_irq[VBATDET_LOW_IRQ])
+		debugfs_create_file("VBATDET_LOW", 0444, pm8058_chg.dent,
+				    (void *)
+				    pm8058_chg.pmic_chg_irq[VBATDET_LOW_IRQ],
+				    &rt_fops);
+	debugfs_create_file("CHARGE_BATT", 0444, pm8058_chg.dent,
+				    NULL,
+				    &fet_fops);
+}
+#else
+static inline void create_debugfs_entries(void)
+{
+}
+#endif
+
+static void remove_debugfs_entries(void)
+{
+	debugfs_remove_recursive(pm8058_chg.dent);
+}
+
+static struct msm_hardware_charger usb_hw_chg = {
+	.type = CHG_TYPE_USB,
+	.rating = 1,
+	.name = "pm8058-usb",
+	.start_charging = pm8058_start_charging,
+	.stop_charging = pm8058_stop_charging,
+	.charging_switched = pm8058_charging_switched,
+};
+
+static int batt_read_adc(int channel, int *mv_reading)
+{
+	int ret;
+	void *h;
+	struct adc_chan_result adc_chan_result;
+	struct completion  conv_complete_evt;
+
+	pr_debug("%s: called for %d\n", __func__, channel);
+	ret = adc_channel_open(channel, &h);
+	if (ret) {
+		pr_err("%s: couldnt open channel %d ret=%d\n",
+					__func__, channel, ret);
+		goto out;
+	}
+	init_completion(&conv_complete_evt);
+	ret = adc_channel_request_conv(h, &conv_complete_evt);
+	if (ret) {
+		pr_err("%s: couldnt request conv channel %d ret=%d\n",
+						__func__, channel, ret);
+		goto out;
+	}
+	wait_for_completion(&conv_complete_evt);
+	ret = adc_channel_read_result(h, &adc_chan_result);
+	if (ret) {
+		pr_err("%s: couldnt read result channel %d ret=%d\n",
+						__func__, channel, ret);
+		goto out;
+	}
+	ret = adc_channel_close(h);
+	if (ret) {
+		pr_err("%s: couldnt close channel %d ret=%d\n",
+						__func__, channel, ret);
+	}
+	if (mv_reading)
+		*mv_reading = adc_chan_result.measurement;
+
+	pr_debug("%s: done for %d\n", __func__, channel);
+	return adc_chan_result.physical;
+out:
+	pr_debug("%s: done for %d\n", __func__, channel);
+	return -EINVAL;
+
+}
+
+#define BATT_THERM_OPEN_MV  2000
+static int pm8058_is_battery_present(void)
+{
+	int mv_reading;
+
+	mv_reading = 0;
+	batt_read_adc(CHANNEL_ADC_BATT_THERM, &mv_reading);
+	pr_debug("%s: therm_raw is %d\n", __func__, mv_reading);
+	if (mv_reading > 0 && mv_reading < BATT_THERM_OPEN_MV)
+		return 1;
+
+	return 0;
+}
+
+static int pm8058_get_battery_temperature(void)
+{
+	return batt_read_adc(CHANNEL_ADC_BATT_THERM, NULL);
+}
+
+#define BATT_THERM_OPERATIONAL_MAX_CELCIUS 40
+#define BATT_THERM_OPERATIONAL_MIN_CELCIUS 0
+static int pm8058_is_battery_temp_within_range(void)
+{
+	int therm_celcius;
+
+	therm_celcius = pm8058_get_battery_temperature();
+	pr_debug("%s: therm_celcius is %d\n", __func__, therm_celcius);
+	if (therm_celcius > 0
+		&& therm_celcius > BATT_THERM_OPERATIONAL_MIN_CELCIUS
+		&& therm_celcius < BATT_THERM_OPERATIONAL_MAX_CELCIUS)
+		return 1;
+
+	return 0;
+}
+
+#define BATT_ID_MAX_MV  800
+#define BATT_ID_MIN_MV  600
+static int pm8058_is_battery_id_valid(void)
+{
+	int batt_id_mv;
+
+	batt_id_mv = batt_read_adc(CHANNEL_ADC_BATT_ID, NULL);
+	pr_debug("%s: batt_id_mv is %d\n", __func__, batt_id_mv);
+
+	/*
+	 * The readings are not in range
+	 * assume battery is present for now
+	 */
+	return 1;
+
+	if (batt_id_mv > 0
+		&& batt_id_mv > BATT_ID_MIN_MV
+		&& batt_id_mv < BATT_ID_MAX_MV)
+		return 1;
+
+	return 0;
+}
+
+/* returns voltage in mV */
+static int pm8058_get_battery_mvolts(void)
+{
+	int vbatt_mv;
+
+	vbatt_mv = batt_read_adc(CHANNEL_ADC_VBATT, NULL);
+	pr_debug("%s: vbatt_mv is %d\n", __func__, vbatt_mv);
+	if (vbatt_mv > 0)
+		return vbatt_mv;
+	/*
+	 * return 0 to tell the upper layers
+	 * we couldnt read the battery voltage
+	 */
+	return 0;
+}
+
+static int msm_battery_gauge_alarm_notify(struct notifier_block *nb,
+		unsigned long status, void *unused)
+{
+	int rc;
+
+	pr_info("%s: status: %lu\n", __func__, status);
+
+	switch (status) {
+	case 0:
+		dev_err(pm8058_chg.dev,
+			"%s: spurious interrupt\n", __func__);
+		break;
+	/* expected case - trip of low threshold */
+	case 1:
+		rc = pm8058_batt_alarm_state_set(0, 0);
+		if (rc)
+			dev_err(pm8058_chg.dev,
+				"%s: unable to set alarm state\n", __func__);
+		msm_charger_notify_event(NULL, CHG_BATT_NEEDS_RECHARGING);
+		break;
+	case 2:
+		dev_err(pm8058_chg.dev,
+			"%s: trip of high threshold\n", __func__);
+		break;
+	default:
+		dev_err(pm8058_chg.dev,
+			"%s: error received\n", __func__);
+	};
+
+	return 0;
+}
+
+static int pm8058_monitor_for_recharging(void)
+{
+	/* enable low comparator */
+	return pm8058_batt_alarm_state_set(1, 0);
+}
+
+static struct msm_battery_gauge pm8058_batt_gauge = {
+	.get_battery_mvolts = pm8058_get_battery_mvolts,
+	.get_battery_temperature = pm8058_get_battery_temperature,
+	.is_battery_present = pm8058_is_battery_present,
+	.is_battery_temp_within_range = pm8058_is_battery_temp_within_range,
+	.is_battery_id_valid = pm8058_is_battery_id_valid,
+	.monitor_for_recharging = pm8058_monitor_for_recharging,
+};
+
+static int pm8058_usb_voltage_lower_limit(void)
+{
+	u8 temp, old;
+	int ret = 0;
+
+	temp = 0x10;
+	ret |= pm8058_write(pm8058_chg.pm_chip, PM8058_CHG_TEST, &temp, 1);
+	ret |= pm8058_read(pm8058_chg.pm_chip, PM8058_CHG_TEST, &old, 1);
+	old = old & ~BIT(IGNORE_LL);
+	temp = 0x90  | (0xF & old);
+	ret |= pm8058_write(pm8058_chg.pm_chip, PM8058_CHG_TEST, &temp, 1);
+
+	return ret;
+}
+
+static int __devinit pm8058_charger_probe(struct platform_device *pdev)
+{
+	struct pm8058_chip *pm_chip;
+	int rc = 0;
+
+	pm_chip = dev_get_drvdata(pdev->dev.parent);
+	if (pm_chip == NULL) {
+		pr_err("%s:no parent data passed in.\n", __func__);
+		return -EFAULT;
+	}
+
+	pm8058_chg.pm_chip = pm_chip;
+	pm8058_chg.pdata = pdev->dev.platform_data;
+	pm8058_chg.dev = &pdev->dev;
+
+	rc = request_irqs(pdev);
+	if (rc) {
+		pr_err("%s: couldnt register interrupts\n", __func__);
+		goto out;
+	}
+
+	rc = pm8058_usb_voltage_lower_limit();
+	if (rc) {
+		pr_err("%s: couldnt set ignore lower limit bit to 0\n",
+								__func__);
+		goto free_irq;
+	}
+
+	rc = msm_charger_register(&usb_hw_chg);
+	if (rc) {
+		pr_err("%s: msm_charger_register failed ret=%d\n",
+							__func__, rc);
+		goto free_irq;
+	}
+
+	pm_chg_batt_temp_disable(0);
+	msm_battery_gauge_register(&pm8058_batt_gauge);
+	__dump_chg_regs();
+
+	create_debugfs_entries();
+	INIT_DELAYED_WORK(&pm8058_chg.veoc_begin_work, veoc_begin_work);
+	INIT_DELAYED_WORK(&pm8058_chg.check_vbat_low_work, vbat_low_work);
+	INIT_DELAYED_WORK(&pm8058_chg.chg_done_check_work, chg_done_check_work);
+	INIT_DELAYED_WORK(&pm8058_chg.charging_check_work, charging_check_work);
+
+	/* determine what state the charger is in */
+	pm8058_chg_determine_initial_state();
+
+	pm8058_chg_enable_irq(BATTTEMP_IRQ);
+	pm8058_chg_enable_irq(BATTCONNECT_IRQ);
+
+	rc = pm8058_batt_alarm_state_set(0, 0);
+	if (rc) {
+		pr_err("%s: unable to set batt alarm state\n", __func__);
+		goto free_irq;
+	}
+
+	/*
+	 * The batt-alarm driver requires sane values for both min / max,
+	 * regardless of whether they're both activated.
+	 */
+	rc = pm8058_batt_alarm_threshold_set(resume_mv, 4300);
+	if (rc) {
+		pr_err("%s: unable to set batt alarm threshold\n", __func__);
+		goto free_irq;
+	}
+
+	rc = pm8058_batt_alarm_hold_time_set(PM8058_BATT_ALARM_HOLD_TIME_16_MS);
+	if (rc) {
+		pr_err("%s: unable to set batt alarm hold time\n", __func__);
+		goto free_irq;
+	}
+
+	/* PWM enabled at 2Hz */
+	rc = pm8058_batt_alarm_pwm_rate_set(1, 7, 4);
+	if (rc) {
+		pr_err("%s: unable to set batt alarm pwm rate\n", __func__);
+		goto free_irq;
+	}
+
+	rc = pm8058_batt_alarm_register_notifier(&alarm_notifier);
+	if (rc) {
+		pr_err("%s: unable to register alarm notifier\n", __func__);
+		goto free_irq;
+	}
+
+	pm8058_chg.inited = 1;
+
+	return 0;
+
+free_irq:
+	free_irqs();
+out:
+	return rc;
+}
+
+static int __devexit pm8058_charger_remove(struct platform_device *pdev)
+{
+	struct pm8058_charger_chip *chip = platform_get_drvdata(pdev);
+	int rc;
+
+	msm_charger_notify_event(&usb_hw_chg, CHG_REMOVED_EVENT);
+	msm_charger_unregister(&usb_hw_chg);
+	cancel_delayed_work_sync(&pm8058_chg.veoc_begin_work);
+	cancel_delayed_work_sync(&pm8058_chg.check_vbat_low_work);
+	cancel_delayed_work_sync(&pm8058_chg.charging_check_work);
+	free_irqs();
+	remove_debugfs_entries();
+	kfree(chip);
+
+	rc = pm8058_batt_alarm_state_set(0, 0);
+	if (rc)
+		pr_err("%s: unable to set batt alarm state\n", __func__);
+
+	rc |= pm8058_batt_alarm_unregister_notifier(&alarm_notifier);
+	if (rc)
+		pr_err("%s: unable to register alarm notifier\n", __func__);
+	return rc;
+}
+
+static struct platform_driver pm8058_charger_driver = {
+	.probe = pm8058_charger_probe,
+	.remove = __devexit_p(pm8058_charger_remove),
+	.driver = {
+		   .name = "pm8058-charger",
+		   .owner = THIS_MODULE,
+	},
+};
+
+static int __init pm8058_charger_init(void)
+{
+	return platform_driver_register(&pm8058_charger_driver);
+}
+
+static void __exit pm8058_charger_exit(void)
+{
+	platform_driver_unregister(&pm8058_charger_driver);
+}
+
+late_initcall(pm8058_charger_init);
+module_exit(pm8058_charger_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PMIC8058 BATTERY driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:pm8058_charger");
diff --git a/drivers/power/qci_battery.c b/drivers/power/qci_battery.c
new file mode 100644
index 0000000..724bcba
--- /dev/null
+++ b/drivers/power/qci_battery.c
@@ -0,0 +1,662 @@
+/* Quanta I2C Battery Driver
+ *
+ * Copyright (C) 2009 Quanta Computer Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/*
+ *
+ *  The Driver with I/O communications via the I2C Interface for ST15 platform.
+ *  And it is only working on the nuvoTon WPCE775x Embedded Controller.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/sched.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/wpce775x.h>
+#include <linux/delay.h>
+
+#include "qci_battery.h"
+
+#define QCIBAT_DEFAULT_CHARGE_FULL_CAPACITY 2200 /* 2200 mAh */
+#define QCIBAT_DEFAULT_CHARGE_FULL_DESIGN   2200
+#define QCIBAT_DEFAULT_VOLTAGE_DESIGN      10800 /* 10.8 V */
+#define QCIBAT_STRING_SIZE 16
+
+/* General structure to hold the driver data */
+struct i2cbat_drv_data {
+	struct i2c_client *bi2c_client;
+	struct work_struct work;
+	unsigned int qcibat_irq;
+	unsigned int qcibat_gpio;
+	u8 battery_state;
+	u8 battery_dev_name[QCIBAT_STRING_SIZE];
+	u8 serial_number[QCIBAT_STRING_SIZE];
+	u8 manufacturer_name[QCIBAT_STRING_SIZE];
+	unsigned int charge_full;
+	unsigned int charge_full_design;
+	unsigned int voltage_full_design;
+	unsigned int energy_full;
+};
+
+static struct i2cbat_drv_data context;
+static struct mutex qci_i2c_lock;
+static struct mutex qci_transaction_lock;
+/*********************************************************************
+ *		Power
+ *********************************************************************/
+
+static int get_bat_info(u8 ec_data)
+{
+	u8 byte_read;
+
+	mutex_lock(&qci_i2c_lock);
+	i2c_smbus_write_byte(context.bi2c_client, ec_data);
+	byte_read = i2c_smbus_read_byte(context.bi2c_client);
+	mutex_unlock(&qci_i2c_lock);
+	return byte_read;
+}
+
+static int qci_ac_get_prop(struct power_supply *psy,
+			    enum power_supply_property psp,
+			    union power_supply_propval *val)
+{
+	int ret = 0;
+	switch (psp) {
+	case POWER_SUPPLY_PROP_ONLINE:
+		if (get_bat_info(ECRAM_POWER_SOURCE) & EC_FLAG_ADAPTER_IN)
+			val->intval =  EC_ADAPTER_PRESENT;
+		else
+			val->intval =  EC_ADAPTER_NOT_PRESENT;
+	break;
+	default:
+		ret = -EINVAL;
+	break;
+	}
+	return ret;
+}
+
+static enum power_supply_property qci_ac_props[] = {
+	POWER_SUPPLY_PROP_ONLINE,
+};
+
+static enum power_supply_property qci_bat_props[] = {
+	POWER_SUPPLY_PROP_STATUS,
+	POWER_SUPPLY_PROP_PRESENT,
+	POWER_SUPPLY_PROP_HEALTH,
+	POWER_SUPPLY_PROP_TECHNOLOGY,
+	POWER_SUPPLY_PROP_VOLTAGE_AVG,
+	POWER_SUPPLY_PROP_CURRENT_AVG,
+	POWER_SUPPLY_PROP_CAPACITY,
+	POWER_SUPPLY_PROP_TEMP,
+	POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+	POWER_SUPPLY_PROP_CHARGE_FULL,
+	POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
+	POWER_SUPPLY_PROP_TIME_TO_FULL_AVG,
+	POWER_SUPPLY_PROP_MODEL_NAME,
+	POWER_SUPPLY_PROP_MANUFACTURER,
+	POWER_SUPPLY_PROP_SERIAL_NUMBER,
+	POWER_SUPPLY_PROP_CHARGE_COUNTER,
+	POWER_SUPPLY_PROP_ENERGY_NOW,
+	POWER_SUPPLY_PROP_ENERGY_FULL,
+	POWER_SUPPLY_PROP_ENERGY_EMPTY,
+};
+
+static int read_data_from_battery(u8 smb_cmd, u8 smb_prtcl)
+{
+	if (context.battery_state & MAIN_BATTERY_STATUS_BAT_IN)	{
+		mutex_lock(&qci_i2c_lock);
+		i2c_smbus_write_byte_data(context.bi2c_client,
+					  ECRAM_SMB_STS, 0);
+		i2c_smbus_write_byte_data(context.bi2c_client, ECRAM_SMB_ADDR,
+					  BATTERY_SLAVE_ADDRESS);
+		i2c_smbus_write_byte_data(context.bi2c_client,
+					  ECRAM_SMB_CMD, smb_cmd);
+		i2c_smbus_write_byte_data(context.bi2c_client,
+					  ECRAM_SMB_PRTCL, smb_prtcl);
+		mutex_unlock(&qci_i2c_lock);
+		msleep(100);
+		return get_bat_info(ECRAM_SMB_STS);
+	} else
+		return SMBUS_DEVICE_NOACK;
+}
+
+static int qbat_get_status(union power_supply_propval *val)
+{
+	int status;
+
+	status = get_bat_info(ECRAM_BATTERY_STATUS);
+
+	if ((status & MAIN_BATTERY_STATUS_BAT_IN) == 0x0)
+		val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
+	else if (status & MAIN_BATTERY_STATUS_BAT_CHARGING)
+		val->intval = POWER_SUPPLY_STATUS_CHARGING;
+	else if (status & MAIN_BATTERY_STATUS_BAT_FULL)
+		val->intval = POWER_SUPPLY_STATUS_FULL;
+	else if (status & MAIN_BATTERY_STATUS_BAT_DISCHRG)
+		val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+	else
+		val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
+
+	return 0;
+}
+
+static int qbat_get_present(union power_supply_propval *val)
+{
+	if (context.battery_state & MAIN_BATTERY_STATUS_BAT_IN)
+		val->intval = EC_BAT_PRESENT;
+	else
+		val->intval = EC_BAT_NOT_PRESENT;
+	return 0;
+}
+
+static int qbat_get_health(union power_supply_propval *val)
+{
+	u8 health;
+
+	health = get_bat_info(ECRAM_CHARGER_ALARM);
+	if (!(context.battery_state & MAIN_BATTERY_STATUS_BAT_IN))
+		val->intval = POWER_SUPPLY_HEALTH_UNKNOWN;
+	else if (health & ALARM_OVER_TEMP)
+		val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
+	else if (health & ALARM_REMAIN_CAPACITY)
+		val->intval = POWER_SUPPLY_HEALTH_DEAD;
+	else if (health & ALARM_OVER_CHARGE)
+		val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+	else
+		val->intval = POWER_SUPPLY_HEALTH_GOOD;
+	return 0;
+}
+
+static int qbat_get_voltage_avg(union power_supply_propval *val)
+{
+	val->intval = (get_bat_info(ECRAM_BATTERY_VOLTAGE_MSB) << 8 |
+		       get_bat_info(ECRAM_BATTERY_VOLTAGE_LSB)) * 1000;
+	return 0;
+}
+
+static int qbat_get_current_avg(union power_supply_propval *val)
+{
+	val->intval = (get_bat_info(ECRAM_BATTERY_CURRENT_MSB) << 8 |
+		       get_bat_info(ECRAM_BATTERY_CURRENT_LSB));
+	return 0;
+}
+
+static int qbat_get_capacity(union power_supply_propval *val)
+{
+	if (!(context.battery_state & MAIN_BATTERY_STATUS_BAT_IN))
+		val->intval = 0xFF;
+	else
+		val->intval = get_bat_info(ECRAM_BATTERY_CAPACITY);
+	return 0;
+}
+
+static int qbat_get_temp_avg(union power_supply_propval *val)
+{
+	int temp;
+	int rc = 0;
+
+	if (!(context.battery_state & MAIN_BATTERY_STATUS_BAT_IN)) {
+		val->intval = 0xFFFF;
+		rc = -ENODATA;
+	} else {
+		temp = (get_bat_info(ECRAM_BATTERY_TEMP_MSB) << 8) |
+			get_bat_info(ECRAM_BATTERY_TEMP_LSB);
+		val->intval = (temp - 2730) / 10;
+	}
+	return rc;
+}
+
+static int qbat_get_charge_full_design(union power_supply_propval *val)
+{
+	val->intval = context.charge_full_design;
+	return 0;
+}
+
+static int qbat_get_charge_full(union power_supply_propval *val)
+{
+	val->intval = context.charge_full;
+	return 0;
+}
+
+static int qbat_get_charge_counter(union power_supply_propval *val)
+{
+	u16 charge = 0;
+	int rc = 0;
+
+	mutex_lock(&qci_transaction_lock);
+	if (read_data_from_battery(BATTERY_CYCLE_COUNT,
+				   SMBUS_READ_WORD_PRTCL) == SMBUS_DONE) {
+		charge = get_bat_info(ECRAM_SMB_DATA1);
+		charge = charge << 8;
+		charge |= get_bat_info(ECRAM_SMB_DATA0);
+	} else
+		rc = -ENODATA;
+	mutex_unlock(&qci_transaction_lock);
+	val->intval = charge;
+	return rc;
+}
+
+static int qbat_get_time_empty_avg(union power_supply_propval *val)
+{
+	u16 avg = 0;
+	int rc = 0;
+
+	mutex_lock(&qci_transaction_lock);
+	if (read_data_from_battery(BATTERY_AVERAGE_TIME_TO_EMPTY,
+				   SMBUS_READ_WORD_PRTCL) == SMBUS_DONE) {
+		avg = get_bat_info(ECRAM_SMB_DATA1);
+		avg = avg << 8;
+		avg |= get_bat_info(ECRAM_SMB_DATA0);
+	} else
+		rc = -ENODATA;
+	mutex_unlock(&qci_transaction_lock);
+	val->intval = avg;
+	return rc;
+}
+
+static int qbat_get_time_full_avg(union power_supply_propval *val)
+{
+	u16 avg = 0;
+	int rc = 0;
+
+	mutex_lock(&qci_transaction_lock);
+	if (read_data_from_battery(BATTERY_AVERAGE_TIME_TO_FULL,
+				   SMBUS_READ_WORD_PRTCL) == SMBUS_DONE) {
+		avg = get_bat_info(ECRAM_SMB_DATA1);
+		avg = avg << 8;
+		avg |= get_bat_info(ECRAM_SMB_DATA0);
+	} else
+		rc = -ENODATA;
+	mutex_unlock(&qci_transaction_lock);
+	val->intval = avg;
+	return rc;
+}
+
+static int qbat_get_model_name(union power_supply_propval *val)
+{
+	unsigned char i, size;
+
+	mutex_lock(&qci_transaction_lock);
+	if (read_data_from_battery(BATTERY_DEVICE_NAME,
+				   SMBUS_READ_BLOCK_PRTCL) == SMBUS_DONE) {
+		size = min(get_bat_info(ECRAM_SMB_BCNT), QCIBAT_STRING_SIZE);
+		for (i = 0; i < size; i++) {
+			context.battery_dev_name[i] =
+				get_bat_info(ECRAM_SMB_DATA_START + i);
+		}
+		val->strval = context.battery_dev_name;
+	} else
+		val->strval = "Unknown";
+	mutex_unlock(&qci_transaction_lock);
+	return 0;
+}
+
+static int qbat_get_manufacturer_name(union power_supply_propval *val)
+{
+	val->strval = context.manufacturer_name;
+	return 0;
+}
+
+static int qbat_get_serial_number(union power_supply_propval *val)
+{
+	val->strval = context.serial_number;
+	return 0;
+}
+
+static int qbat_get_technology(union power_supply_propval *val)
+{
+	val->intval = POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
+	return 0;
+}
+
+static int qbat_get_energy_now(union power_supply_propval *val)
+{
+	if (!(get_bat_info(ECRAM_BATTERY_STATUS) & MAIN_BATTERY_STATUS_BAT_IN))
+		val->intval = 0;
+	else
+		val->intval = (get_bat_info(ECRAM_BATTERY_CAPACITY) *
+			       context.energy_full) / 100;
+	return 0;
+}
+
+static int qbat_get_energy_full(union power_supply_propval *val)
+{
+	val->intval = context.energy_full;
+	return 0;
+}
+
+static int qbat_get_energy_empty(union power_supply_propval *val)
+{
+	val->intval = 0;
+	return 0;
+}
+
+static void qbat_init_get_charge_full(void)
+{
+	u16 charge = QCIBAT_DEFAULT_CHARGE_FULL_CAPACITY;
+
+	mutex_lock(&qci_transaction_lock);
+	if (read_data_from_battery(BATTERY_FULL_CAPACITY,
+				   SMBUS_READ_WORD_PRTCL) == SMBUS_DONE) {
+		charge = get_bat_info(ECRAM_SMB_DATA1);
+		charge = charge << 8;
+		charge |= get_bat_info(ECRAM_SMB_DATA0);
+	}
+	mutex_unlock(&qci_transaction_lock);
+	context.charge_full = charge;
+}
+
+static void qbat_init_get_charge_full_design(void)
+{
+	u16 charge = QCIBAT_DEFAULT_CHARGE_FULL_DESIGN;
+
+	mutex_lock(&qci_transaction_lock);
+	if (read_data_from_battery(BATTERY_DESIGN_CAPACITY,
+				   SMBUS_READ_WORD_PRTCL) == SMBUS_DONE) {
+		charge = get_bat_info(ECRAM_SMB_DATA1);
+		charge = charge << 8;
+		charge |= get_bat_info(ECRAM_SMB_DATA0);
+	}
+	mutex_unlock(&qci_transaction_lock);
+	context.charge_full_design = charge;
+}
+
+static void qbat_init_get_voltage_full_design(void)
+{
+	u16 voltage = QCIBAT_DEFAULT_VOLTAGE_DESIGN;
+
+	mutex_lock(&qci_transaction_lock);
+	if (read_data_from_battery(BATTERY_DESIGN_VOLTAGE,
+				   SMBUS_READ_WORD_PRTCL) == SMBUS_DONE) {
+		voltage = get_bat_info(ECRAM_SMB_DATA1);
+		voltage = voltage << 8;
+		voltage |= get_bat_info(ECRAM_SMB_DATA0);
+	}
+	mutex_unlock(&qci_transaction_lock);
+	context.voltage_full_design = voltage;
+}
+
+static void qbat_init_get_manufacturer_name(void)
+{
+	u8 size;
+	u8 i;
+	int rc;
+
+	mutex_lock(&qci_transaction_lock);
+	rc = read_data_from_battery(BATTERY_MANUFACTURE_NAME,
+				    SMBUS_READ_BLOCK_PRTCL);
+	if (rc == SMBUS_DONE) {
+		size = min(get_bat_info(ECRAM_SMB_BCNT), QCIBAT_STRING_SIZE);
+		for (i = 0; i < size; i++) {
+			context.manufacturer_name[i] =
+				get_bat_info(ECRAM_SMB_DATA_START + i);
+		}
+	} else
+		strcpy(context.manufacturer_name, "Unknown");
+	mutex_unlock(&qci_transaction_lock);
+}
+
+static void qbat_init_get_serial_number(void)
+{
+	u8 size;
+	u8 i;
+	int rc;
+
+	mutex_lock(&qci_transaction_lock);
+	rc = read_data_from_battery(BATTERY_SERIAL_NUMBER,
+				    SMBUS_READ_BLOCK_PRTCL);
+	if (rc == SMBUS_DONE) {
+		size = min(get_bat_info(ECRAM_SMB_BCNT), QCIBAT_STRING_SIZE);
+		for (i = 0; i < size; i++) {
+			context.serial_number[i] =
+				get_bat_info(ECRAM_SMB_DATA_START + i);
+		}
+	} else
+		strcpy(context.serial_number, "Unknown");
+	mutex_unlock(&qci_transaction_lock);
+}
+
+static void init_battery_stats(void)
+{
+	int i;
+
+	context.battery_state = get_bat_info(ECRAM_BATTERY_STATUS);
+	if (!(context.battery_state & MAIN_BATTERY_STATUS_BAT_IN))
+		return;
+	/* EC bug? needs some initial priming */
+	for (i = 0; i < 5; i++) {
+		read_data_from_battery(BATTERY_DESIGN_CAPACITY,
+				       SMBUS_READ_WORD_PRTCL);
+	}
+
+	qbat_init_get_charge_full_design();
+	qbat_init_get_charge_full();
+	qbat_init_get_voltage_full_design();
+
+	context.energy_full = context.voltage_full_design *
+		context.charge_full;
+
+	qbat_init_get_serial_number();
+	qbat_init_get_manufacturer_name();
+}
+
+/*********************************************************************
+ *		Battery properties
+ *********************************************************************/
+static int qbat_get_property(struct power_supply *psy,
+			     enum power_supply_property psp,
+			     union power_supply_propval *val)
+{
+	int ret = 0;
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_STATUS:
+		ret = qbat_get_status(val);
+		break;
+	case POWER_SUPPLY_PROP_PRESENT:
+		ret = qbat_get_present(val);
+		break;
+	case POWER_SUPPLY_PROP_HEALTH:
+		ret = qbat_get_health(val);
+		break;
+	case POWER_SUPPLY_PROP_MANUFACTURER:
+		ret = qbat_get_manufacturer_name(val);
+		break;
+	case POWER_SUPPLY_PROP_TECHNOLOGY:
+		ret = qbat_get_technology(val);
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_AVG:
+		ret = qbat_get_voltage_avg(val);
+		break;
+	case POWER_SUPPLY_PROP_CURRENT_AVG:
+		ret = qbat_get_current_avg(val);
+		break;
+	case POWER_SUPPLY_PROP_CAPACITY:
+		ret = qbat_get_capacity(val);
+		break;
+	case POWER_SUPPLY_PROP_TEMP:
+		ret = qbat_get_temp_avg(val);
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
+		ret = qbat_get_charge_full_design(val);
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_FULL:
+		ret = qbat_get_charge_full(val);
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_COUNTER:
+		ret = qbat_get_charge_counter(val);
+		break;
+	case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG:
+		ret = qbat_get_time_empty_avg(val);
+		break;
+	case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG:
+		ret = qbat_get_time_full_avg(val);
+		break;
+	case POWER_SUPPLY_PROP_MODEL_NAME:
+		ret = qbat_get_model_name(val);
+		break;
+	case POWER_SUPPLY_PROP_SERIAL_NUMBER:
+		ret = qbat_get_serial_number(val);
+		break;
+	case POWER_SUPPLY_PROP_ENERGY_NOW:
+		ret = qbat_get_energy_now(val);
+		break;
+	case POWER_SUPPLY_PROP_ENERGY_FULL:
+		ret = qbat_get_energy_full(val);
+		break;
+	case POWER_SUPPLY_PROP_ENERGY_EMPTY:
+		ret = qbat_get_energy_empty(val);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+/*********************************************************************
+ *		Initialisation
+ *********************************************************************/
+
+static struct power_supply qci_ac = {
+	.name = "ac",
+	.type = POWER_SUPPLY_TYPE_MAINS,
+	.properties = qci_ac_props,
+	.num_properties = ARRAY_SIZE(qci_ac_props),
+	.get_property = qci_ac_get_prop,
+};
+
+static struct power_supply qci_bat = {
+	.name = "battery",
+	.type = POWER_SUPPLY_TYPE_BATTERY,
+	.properties = qci_bat_props,
+	.num_properties = ARRAY_SIZE(qci_bat_props),
+	.get_property = qbat_get_property,
+	.use_for_apm = 1,
+};
+
+static irqreturn_t qbat_interrupt(int irq, void *dev_id)
+{
+	struct i2cbat_drv_data *ibat_drv_data = dev_id;
+	schedule_work(&ibat_drv_data->work);
+	return IRQ_HANDLED;
+}
+
+static void qbat_work(struct work_struct *_work)
+{
+	u8 status;
+
+	status = get_bat_info(ECRAM_BATTERY_EVENTS);
+	if (status & EC_EVENT_AC) {
+		context.battery_state = get_bat_info(ECRAM_BATTERY_STATUS);
+		power_supply_changed(&qci_ac);
+	}
+
+	if (status & (EC_EVENT_BATTERY | EC_EVENT_CHARGER | EC_EVENT_TIMER)) {
+		context.battery_state = get_bat_info(ECRAM_BATTERY_STATUS);
+		power_supply_changed(&qci_bat);
+		if (status & EC_EVENT_BATTERY)
+			init_battery_stats();
+	}
+}
+
+static struct platform_device *bat_pdev;
+
+static int __init qbat_init(void)
+{
+	int err = 0;
+
+	mutex_init(&qci_i2c_lock);
+	mutex_init(&qci_transaction_lock);
+
+	context.bi2c_client = wpce_get_i2c_client();
+	if (context.bi2c_client == NULL)
+		return -1;
+
+	i2c_set_clientdata(context.bi2c_client, &context);
+	context.qcibat_gpio = context.bi2c_client->irq;
+
+	/*battery device register*/
+	bat_pdev = platform_device_register_simple("battery", 0, NULL, 0);
+	if (IS_ERR(bat_pdev))
+		return PTR_ERR(bat_pdev);
+
+	err = power_supply_register(&bat_pdev->dev, &qci_ac);
+	if (err)
+		goto ac_failed;
+
+	qci_bat.name = bat_pdev->name;
+	err = power_supply_register(&bat_pdev->dev, &qci_bat);
+	if (err)
+		goto battery_failed;
+
+	/*battery irq configure*/
+	INIT_WORK(&context.work, qbat_work);
+	err = gpio_request(context.qcibat_gpio, "qci-bat");
+	if (err) {
+		dev_err(&context.bi2c_client->dev,
+			"[BAT] err gpio request\n");
+		goto gpio_request_fail;
+	}
+	context.qcibat_irq = gpio_to_irq(context.qcibat_gpio);
+	err = request_irq(context.qcibat_irq, qbat_interrupt,
+		IRQF_TRIGGER_FALLING, BATTERY_ID_NAME, &context);
+	if (err) {
+		dev_err(&context.bi2c_client->dev,
+			"[BAT] unable to get IRQ\n");
+		goto request_irq_fail;
+	}
+
+	init_battery_stats();
+	goto success;
+
+request_irq_fail:
+	gpio_free(context.qcibat_gpio);
+
+gpio_request_fail:
+	power_supply_unregister(&qci_bat);
+
+battery_failed:
+	power_supply_unregister(&qci_ac);
+
+ac_failed:
+	platform_device_unregister(bat_pdev);
+
+	i2c_set_clientdata(context.bi2c_client, NULL);
+success:
+	return err;
+}
+
+static void __exit qbat_exit(void)
+{
+	free_irq(context.qcibat_irq, &context);
+	gpio_free(context.qcibat_gpio);
+	power_supply_unregister(&qci_bat);
+	power_supply_unregister(&qci_ac);
+	platform_device_unregister(bat_pdev);
+	i2c_set_clientdata(context.bi2c_client, NULL);
+}
+
+late_initcall(qbat_init);
+module_exit(qbat_exit);
+
+MODULE_AUTHOR("Quanta Computer Inc.");
+MODULE_DESCRIPTION("Quanta Embedded Controller I2C Battery Driver");
+MODULE_LICENSE("GPL v2");
+
diff --git a/drivers/power/qci_battery.h b/drivers/power/qci_battery.h
new file mode 100644
index 0000000..dcbb62b
--- /dev/null
+++ b/drivers/power/qci_battery.h
@@ -0,0 +1,121 @@
+/* Header file for Quanta I2C Battery Driver
+ *
+ * Copyright (C) 2009 Quanta Computer Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+ /*
+ *
+ *  The Driver with I/O communications via the I2C Interface for ON2 of AP BU.
+ *  And it is only working on the nuvoTon WPCE775x Embedded Controller.
+ *
+ */
+
+#ifndef __QCI_BATTERY_H__
+#define __QCI_BATTERY_H__
+
+#define BAT_I2C_ADDRESS 0x1A
+#define BATTERY_ID_NAME          "qci-i2cbat"
+#define EC_FLAG_ADAPTER_IN		0x01
+#define EC_FLAG_POWER_ON		0x02
+#define EC_FLAG_ENTER_S3		0x04
+#define EC_FLAG_ENTER_S4		0x08
+#define EC_FLAG_IN_STANDBY		0x10
+#define EC_FLAG_SYSTEM_ON		0x20
+#define EC_FLAG_WAIT_HWPG		0x40
+#define EC_FLAG_S5_POWER_ON	0x80
+
+#define MAIN_BATTERY_STATUS_BAT_DISCHRG		0x01
+#define MAIN_BATTERY_STATUS_BAT_CHARGING	0x02
+#define MAIN_BATTERY_STATUS_BAT_ABNORMAL	0x04
+#define MAIN_BATTERY_STATUS_BAT_IN		0x08
+#define MAIN_BATTERY_STATUS_BAT_FULL		0x10
+#define MAIN_BATTERY_STATUS_BAT_LOW		0x20
+#define MAIN_BATTERY_STATUS_BAT_SMB_VALID	0x80
+
+#define CHG_STATUS_BAT_CHARGE			0x01
+#define CHG_STATUS_BAT_PRECHG			0x02
+#define CHG_STATUS_BAT_OVERTEMP			0x04
+#define CHG_STATUS_BAT_TYPE			0x08
+#define CHG_STATUS_BAT_GWROK			0x10
+#define CHG_STATUS_BAT_INCHARGE			0x20
+#define CHG_STATUS_BAT_WAKECHRG			0x40
+#define CHG_STATUS_BAT_CHGTIMEOUT		0x80
+
+#define EC_ADAPTER_PRESENT		0x1
+#define EC_BAT_PRESENT		        0x1
+#define EC_ADAPTER_NOT_PRESENT		0x0
+#define EC_BAT_NOT_PRESENT		0x0
+
+#define ECRAM_POWER_SOURCE              0x40
+#define ECRAM_CHARGER_ALARM		0x42
+#define ECRAM_BATTERY_STATUS            0x82
+#define ECRAM_BATTERY_CURRENT_LSB       0x83
+#define ECRAM_BATTERY_CURRENT_MSB       0x84
+#define ECRAM_BATTERY_VOLTAGE_LSB       0x87
+#define ECRAM_BATTERY_VOLTAGE_MSB       0x88
+#define ECRAM_BATTERY_CAPACITY          0x89
+#define ECRAM_BATTERY_TEMP_LSB          0x8C
+#define ECRAM_BATTERY_TEMP_MSB          0x8D
+#define ECRAM_BATTERY_EVENTS            0x99
+
+#define EC_EVENT_BATTERY                0x01
+#define EC_EVENT_CHARGER                0x02
+#define EC_EVENT_AC                     0x10
+#define EC_EVENT_TIMER                  0x40
+
+/* smbus access */
+#define SMBUS_READ_BYTE_PRTCL		0x07
+#define SMBUS_READ_WORD_PRTCL		0x09
+#define SMBUS_READ_BLOCK_PRTCL		0x0B
+
+/* smbus status code */
+#define SMBUS_OK			0x00
+#define SMBUS_DONE			0x80
+#define SMBUS_ALARM			0x40
+#define SMBUS_UNKNOW_FAILURE		0x07
+#define SMBUS_DEVICE_NOACK		0x10
+#define SMBUS_DEVICE_ERROR		0x11
+#define SMBUS_UNKNOW_ERROR		0x13
+#define SMBUS_TIME_OUT			0x18
+#define SMBUS_BUSY			0x1A
+
+/* ec ram mapping */
+#define ECRAM_SMB_PRTCL			0
+#define ECRAM_SMB_STS			1
+#define ECRAM_SMB_ADDR			2
+#define ECRAM_SMB_CMD			3
+#define ECRAM_SMB_DATA_START		4
+#define ECRAM_SMB_DATA0			4
+#define ECRAM_SMB_DATA1			5
+#define ECRAM_SMB_BCNT			36
+#define ECRAM_SMB_ALARM_ADDR		37
+#define ECRAM_SMB_ALARM_DATA0		38
+#define ECRAM_SMB_ALARM_DATA1		39
+
+/* smart battery commands */
+#define BATTERY_SLAVE_ADDRESS		0x16
+#define BATTERY_FULL_CAPACITY		0x10
+#define BATTERY_AVERAGE_TIME_TO_EMPTY	0x12
+#define BATTERY_AVERAGE_TIME_TO_FULL	0x13
+#define BATTERY_CYCLE_COUNT		0x17
+#define BATTERY_DESIGN_CAPACITY		0x18
+#define BATTERY_DESIGN_VOLTAGE		0x19
+#define BATTERY_SERIAL_NUMBER		0x1C
+#define BATTERY_MANUFACTURE_NAME        0x20
+#define BATTERY_DEVICE_NAME		0x21
+
+/* alarm bit */
+#define ALARM_REMAIN_CAPACITY           0x02
+#define ALARM_OVER_TEMP                 0x10
+#define ALARM_OVER_CHARGE               0x80
+#endif
diff --git a/drivers/power/smb137b.c b/drivers/power/smb137b.c
new file mode 100644
index 0000000..7ff8e28
--- /dev/null
+++ b/drivers/power/smb137b.c
@@ -0,0 +1,857 @@
+/* Copyright (c) 2010-2011 Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include <linux/workqueue.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/i2c/smb137b.h>
+#include <linux/power_supply.h>
+#include <linux/msm-charger.h>
+
+#define SMB137B_MASK(BITS, POS)  ((unsigned char)(((1 << BITS) - 1) << POS))
+
+#define CHG_CURRENT_REG		0x00
+#define FAST_CHG_CURRENT_MASK		SMB137B_MASK(3, 5)
+#define PRE_CHG_CURRENT_MASK		SMB137B_MASK(2, 3)
+#define TERM_CHG_CURRENT_MASK		SMB137B_MASK(2, 1)
+
+#define INPUT_CURRENT_LIMIT_REG	0x01
+#define IN_CURRENT_MASK			SMB137B_MASK(3, 5)
+#define IN_CURRENT_LIMIT_EN_BIT		BIT(2)
+#define IN_CURRENT_DET_THRESH_MASK	SMB137B_MASK(2, 0)
+
+#define FLOAT_VOLTAGE_REG	0x02
+#define STAT_OUT_POLARITY_BIT		BIT(7)
+#define FLOAT_VOLTAGE_MASK		SMB137B_MASK(7, 0)
+
+#define CONTROL_A_REG		0x03
+#define AUTO_RECHARGE_DIS_BIT		BIT(7)
+#define CURR_CYCLE_TERM_BIT		BIT(6)
+#define PRE_TO_FAST_V_MASK		SMB137B_MASK(3, 3)
+#define TEMP_BEHAV_BIT			BIT(2)
+#define THERM_NTC_CURR_MODE_BIT		BIT(1)
+#define THERM_NTC_47KOHM_BIT		BIT(0)
+
+#define CONTROL_B_REG		0x04
+#define STAT_OUTPUT_MODE_MASK		SMB137B_MASK(2, 6)
+#define BATT_OV_ENDS_CYCLE_BIT		BIT(5)
+#define AUTO_PRE_TO_FAST_DIS_BIT	BIT(4)
+#define SAFETY_TIMER_EN_BIT		BIT(3)
+#define OTG_LBR_WD_EN_BIT		BIT(2)
+#define CHG_WD_TIMER_EN_BIT		BIT(1)
+#define IRQ_OP_MASK			BIT(0)
+
+#define PIN_CTRL_REG		0x05
+#define AUTO_CHG_EN_BIT			BIT(7)
+#define AUTO_LBR_EN_BIT			BIT(6)
+#define OTG_LBR_BIT			BIT(5)
+#define I2C_PIN_BIT			BIT(4)
+#define PIN_EN_CTRL_MASK		SMB137B_MASK(2, 2)
+#define OTG_LBR_PIN_CTRL_MASK		SMB137B_MASK(2, 0)
+
+#define OTG_LBR_CTRL_REG	0x06
+#define BATT_MISSING_DET_EN_BIT		BIT(7)
+#define AUTO_RECHARGE_THRESH_MASK	BIT(6)
+#define USB_DP_DN_DET_EN_MASK		BIT(5)
+#define OTG_LBR_BATT_CURRENT_LIMIT_MASK	SMB137B_MASK(2, 3)
+#define OTG_LBR_UVLO_THRESH_MASK	SMB137B_MASK(3, 0)
+
+#define FAULT_INTR_REG		0x07
+#define SAFETY_TIMER_EXP_MASK		SMB137B_MASK(1, 7)
+#define BATT_TEMP_UNSAFE_MASK		SMB137B_MASK(1, 6)
+#define INPUT_OVLO_IVLO_MASK		SMB137B_MASK(1, 5)
+#define BATT_OVLO_MASK			SMB137B_MASK(1, 4)
+#define INTERNAL_OVER_TEMP_MASK		SMB137B_MASK(1, 2)
+#define ENTER_TAPER_CHG_MASK		SMB137B_MASK(1, 1)
+#define CHG_MASK			SMB137B_MASK(1, 0)
+
+#define CELL_TEMP_MON_REG	0x08
+#define THERMISTOR_CURR_MASK		SMB137B_MASK(2, 6)
+#define LOW_TEMP_CHG_INHIBIT_MASK	SMB137B_MASK(3, 3)
+#define HIGH_TEMP_CHG_INHIBIT_MASK	SMB137B_MASK(3, 0)
+
+#define	SAFETY_TIMER_THERMAL_SHUTDOWN_REG	0x09
+#define DCIN_OVLO_SEL_MASK		SMB137B_MASK(2, 7)
+#define RELOAD_EN_INPUT_VOLTAGE_MASK	SMB137B_MASK(1, 6)
+#define THERM_SHUTDN_EN_MASK		SMB137B_MASK(1, 5)
+#define STANDBY_WD_TIMER_EN_MASK		SMB137B_MASK(1, 4)
+#define COMPLETE_CHG_TMOUT_MASK		SMB137B_MASK(2, 2)
+#define PRE_CHG_TMOUT_MASK		SMB137B_MASK(2, 0)
+
+#define	VSYS_REG	0x0A
+#define	VSYS_MASK			SMB137B_MASK(3, 4)
+
+#define IRQ_RESET_REG	0x30
+
+#define COMMAND_A_REG	0x31
+#define	VOLATILE_REGS_WRITE_PERM_BIT	BIT(7)
+#define	POR_BIT				BIT(6)
+#define	FAST_CHG_SETTINGS_BIT		BIT(5)
+#define	BATT_CHG_EN_BIT			BIT(4)
+#define	USBIN_MODE_500_BIT		BIT(3)
+#define	USBIN_MODE_HCMODE_BIT		BIT(2)
+#define	OTG_LBR_EN_BIT			BIT(1)
+#define	STAT_OE_BIT			BIT(0)
+
+#define STATUS_A_REG	0x32
+#define INTERNAL_TEMP_IRQ_STAT		BIT(4)
+#define DCIN_OV_IRQ_STAT		BIT(3)
+#define DCIN_UV_IRQ_STAT		BIT(2)
+#define USBIN_OV_IRQ_STAT		BIT(1)
+#define USBIN_UV_IRQ_STAT		BIT(0)
+
+#define STATUS_B_REG	0x33
+#define USB_PIN_STAT			BIT(7)
+#define USB51_MODE_STAT			BIT(6)
+#define USB51_HC_MODE_STAT		BIT(5)
+#define INTERNAL_TEMP_LIMIT_B_STAT	BIT(4)
+#define DC_IN_OV_STAT			BIT(3)
+#define DC_IN_UV_STAT			BIT(2)
+#define USB_IN_OV_STAT			BIT(1)
+#define USB_IN_UV_STAT			BIT(0)
+
+#define	STATUS_C_REG	0x34
+#define AUTO_IN_CURR_LIMIT_MASK		SMB137B_MASK(4, 4)
+#define AUTO_IN_CURR_LIMIT_STAT		BIT(3)
+#define AUTO_SOURCE_DET_COMP_STAT_MASK	SMB137B_MASK(2, 1)
+#define AUTO_SOURCE_DET_RESULT_STAT	BIT(0)
+
+#define	STATUS_D_REG	0x35
+#define	VBATT_LESS_THAN_VSYS_STAT	BIT(7)
+#define	USB_FAIL_STAT			BIT(6)
+#define	BATT_TEMP_STAT_MASK		SMB137B_MASK(2, 4)
+#define	INTERNAL_TEMP_LIMIT_STAT	BIT(2)
+#define	OTG_LBR_MODE_EN_STAT		BIT(1)
+#define	OTG_LBR_VBATT_UVLO_STAT		BIT(0)
+
+#define	STATUS_E_REG	0x36
+#define	CHARGE_CYCLE_COUNT_STAT		BIT(7)
+#define	CHARGER_TERM_STAT		BIT(6)
+#define	SAFETY_TIMER_STAT_MASK		SMB137B_MASK(2, 4)
+#define	CHARGER_ERROR_STAT		BIT(3)
+#define	CHARGING_STAT_E			SMB137B_MASK(2, 1)
+#define	CHARGING_EN			BIT(0)
+
+#define	STATUS_F_REG	0x37
+#define	WD_IRQ_ACTIVE_STAT		BIT(7)
+#define	OTG_OVERCURRENT_STAT		BIT(6)
+#define	BATT_PRESENT_STAT		BIT(4)
+#define	BATT_OV_LATCHED_STAT		BIT(3)
+#define	CHARGER_OVLO_STAT		BIT(2)
+#define	CHARGER_UVLO_STAT		BIT(1)
+#define	BATT_LOW_STAT			BIT(0)
+
+#define	STATUS_G_REG	0x38
+#define	CHARGE_TIMEOUT_IRQ_STAT		BIT(7)
+#define	PRECHARGE_TIMEOUT_IRQ_STAT	BIT(6)
+#define	BATT_HOT_IRQ_STAT		BIT(5)
+#define	BATT_COLD_IRQ_STAT		BIT(4)
+#define	BATT_OV_IRQ_STAT		BIT(3)
+#define	TAPER_CHG_IRQ_STAT		BIT(2)
+#define	FAST_CHG_IRQ_STAT		BIT(1)
+#define	CHARGING_IRQ_STAT		BIT(0)
+
+#define	STATUS_H_REG	0x39
+#define	CHARGE_TIMEOUT_STAT		BIT(7)
+#define	PRECHARGE_TIMEOUT_STAT		BIT(6)
+#define	BATT_HOT_STAT			BIT(5)
+#define	BATT_COLD_STAT			BIT(4)
+#define	BATT_OV_STAT			BIT(3)
+#define	TAPER_CHG_STAT			BIT(2)
+#define	FAST_CHG_STAT			BIT(1)
+#define	CHARGING_STAT_H			BIT(0)
+
+#define DEV_ID_REG	0x3B
+
+#define COMMAND_B_REG	0x3C
+#define	THERM_NTC_CURR_VERRIDE		BIT(7)
+
+#define SMB137B_CHG_PERIOD	((HZ) * 150)
+
+#define INPUT_CURRENT_REG_DEFAULT	0xE1
+#define INPUT_CURRENT_REG_MIN		0x01
+#define	COMMAND_A_REG_DEFAULT		0xA0
+#define	COMMAND_A_REG_OTG_MODE		0xA2
+
+#define	PIN_CTRL_REG_DEFAULT		0x00
+#define	PIN_CTRL_REG_CHG_OFF		0x04
+
+#define	FAST_CHG_E_STATUS 0x2
+
+#define SMB137B_DEFAULT_BATT_RATING   950
+struct smb137b_data {
+	struct i2c_client *client;
+	struct delayed_work charge_work;
+
+	bool charging;
+	int chgcurrent;
+	int cur_charging_mode;
+	int max_system_voltage;
+	int min_system_voltage;
+
+	int valid_n_gpio;
+
+	int batt_status;
+	int batt_chg_type;
+	int batt_present;
+	int min_design;
+	int max_design;
+	int batt_mah_rating;
+
+	int usb_status;
+
+	u8 dev_id_reg;
+	struct msm_hardware_charger adapter_hw_chg;
+};
+
+static unsigned int disabled;
+static DEFINE_MUTEX(init_lock);
+static unsigned int init_otg_power;
+
+enum charger_stat {
+	SMB137B_ABSENT,
+	SMB137B_PRESENT,
+	SMB137B_ENUMERATED,
+};
+
+static struct smb137b_data *usb_smb137b_chg;
+
+static int smb137b_read_reg(struct i2c_client *client, int reg,
+	u8 *val)
+{
+	s32 ret;
+	struct smb137b_data *smb137b_chg;
+
+	smb137b_chg = i2c_get_clientdata(client);
+	ret = i2c_smbus_read_byte_data(smb137b_chg->client, reg);
+	if (ret < 0) {
+		dev_err(&smb137b_chg->client->dev,
+			"i2c read fail: can't read from %02x: %d\n", reg, ret);
+		return ret;
+	} else
+		*val = ret;
+
+	return 0;
+}
+
+static int smb137b_write_reg(struct i2c_client *client, int reg,
+	u8 val)
+{
+	s32 ret;
+	struct smb137b_data *smb137b_chg;
+
+	smb137b_chg = i2c_get_clientdata(client);
+	ret = i2c_smbus_write_byte_data(smb137b_chg->client, reg, val);
+	if (ret < 0) {
+		dev_err(&smb137b_chg->client->dev,
+			"i2c write fail: can't write %02x to %02x: %d\n",
+			val, reg, ret);
+		return ret;
+	}
+	return 0;
+}
+
+static ssize_t id_reg_show(struct device *dev, struct device_attribute *attr,
+		char *buf)
+{
+	struct smb137b_data *smb137b_chg;
+
+	smb137b_chg = i2c_get_clientdata(to_i2c_client(dev));
+
+	return sprintf(buf, "%02x\n", smb137b_chg->dev_id_reg);
+}
+static DEVICE_ATTR(id_reg, S_IRUGO | S_IWUSR, id_reg_show, NULL);
+
+#ifdef DEBUG
+static void smb137b_dbg_print_status_regs(struct smb137b_data *smb137b_chg)
+{
+	int ret;
+	u8 temp;
+
+	ret = smb137b_read_reg(smb137b_chg->client, STATUS_A_REG, &temp);
+	dev_dbg(&smb137b_chg->client->dev, "%s A=0x%x\n", __func__, temp);
+	ret = smb137b_read_reg(smb137b_chg->client, STATUS_B_REG, &temp);
+	dev_dbg(&smb137b_chg->client->dev, "%s B=0x%x\n", __func__, temp);
+	ret = smb137b_read_reg(smb137b_chg->client, STATUS_C_REG, &temp);
+	dev_dbg(&smb137b_chg->client->dev, "%s C=0x%x\n", __func__, temp);
+	ret = smb137b_read_reg(smb137b_chg->client, STATUS_D_REG, &temp);
+	dev_dbg(&smb137b_chg->client->dev, "%s D=0x%x\n", __func__, temp);
+	ret = smb137b_read_reg(smb137b_chg->client, STATUS_E_REG, &temp);
+	dev_dbg(&smb137b_chg->client->dev, "%s E=0x%x\n", __func__, temp);
+	ret = smb137b_read_reg(smb137b_chg->client, STATUS_F_REG, &temp);
+	dev_dbg(&smb137b_chg->client->dev, "%s F=0x%x\n", __func__, temp);
+	ret = smb137b_read_reg(smb137b_chg->client, STATUS_G_REG, &temp);
+	dev_dbg(&smb137b_chg->client->dev, "%s G=0x%x\n", __func__, temp);
+	ret = smb137b_read_reg(smb137b_chg->client, STATUS_H_REG, &temp);
+	dev_dbg(&smb137b_chg->client->dev, "%s H=0x%x\n", __func__, temp);
+}
+#else
+static void smb137b_dbg_print_status_regs(struct smb137b_data *smb137b_chg)
+{
+}
+#endif
+
+static int smb137b_start_charging(struct msm_hardware_charger *hw_chg,
+		int chg_voltage, int chg_current)
+{
+	int cmd_val = COMMAND_A_REG_DEFAULT;
+	u8 temp = 0;
+	int ret = 0;
+	struct smb137b_data *smb137b_chg;
+
+	smb137b_chg = container_of(hw_chg, struct smb137b_data, adapter_hw_chg);
+	if (disabled) {
+		dev_err(&smb137b_chg->client->dev,
+			"%s called when disabled\n", __func__);
+		goto out;
+	}
+
+	if (smb137b_chg->charging == true
+		&& smb137b_chg->chgcurrent == chg_current)
+		/* we are already charging with the same current*/
+		 dev_err(&smb137b_chg->client->dev,
+			 "%s charge with same current %d called again\n",
+			  __func__, chg_current);
+
+	dev_dbg(&smb137b_chg->client->dev, "%s\n", __func__);
+	if (chg_current < 500)
+		cmd_val &= ~USBIN_MODE_500_BIT;
+	else if (chg_current == 500)
+		cmd_val |= USBIN_MODE_500_BIT;
+	else
+		cmd_val |= USBIN_MODE_HCMODE_BIT;
+
+	smb137b_chg->chgcurrent = chg_current;
+	smb137b_chg->cur_charging_mode = cmd_val;
+
+	/* Due to non-volatile reload feature,always enable volatile
+	 * mirror writes before modifying any 00h~09h control register.
+	 * Current mode needs to be programmed according to what's detected
+	 * Otherwise default 100mA mode might cause VOUTL drop and fail
+	 * the system in case of dead battery.
+	 */
+	ret = smb137b_write_reg(smb137b_chg->client,
+					COMMAND_A_REG, cmd_val);
+	if (ret) {
+		dev_err(&smb137b_chg->client->dev,
+			"%s couldn't write to command_reg\n", __func__);
+		goto out;
+	}
+	ret = smb137b_write_reg(smb137b_chg->client,
+					PIN_CTRL_REG, PIN_CTRL_REG_DEFAULT);
+	if (ret) {
+		dev_err(&smb137b_chg->client->dev,
+			"%s couldn't write to pin ctrl reg\n", __func__);
+		goto out;
+	}
+	smb137b_chg->charging = true;
+	smb137b_chg->batt_status = POWER_SUPPLY_STATUS_CHARGING;
+	smb137b_chg->batt_chg_type = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
+
+	ret = smb137b_read_reg(smb137b_chg->client, STATUS_E_REG, &temp);
+	if (ret) {
+		dev_err(&smb137b_chg->client->dev,
+			"%s couldn't read status e reg %d\n", __func__, ret);
+	} else {
+		if (temp & CHARGER_ERROR_STAT) {
+			dev_err(&smb137b_chg->client->dev,
+				"%s chg error E=0x%x\n", __func__, temp);
+			smb137b_dbg_print_status_regs(smb137b_chg);
+		}
+		if (((temp & CHARGING_STAT_E) >> 1) >= FAST_CHG_E_STATUS)
+			smb137b_chg->batt_chg_type
+						= POWER_SUPPLY_CHARGE_TYPE_FAST;
+	}
+	/*schedule charge_work to keep track of battery charging state*/
+	schedule_delayed_work(&smb137b_chg->charge_work, SMB137B_CHG_PERIOD);
+
+out:
+	return ret;
+}
+
+static int smb137b_stop_charging(struct msm_hardware_charger *hw_chg)
+{
+	int ret = 0;
+	struct smb137b_data *smb137b_chg;
+
+	smb137b_chg = container_of(hw_chg, struct smb137b_data, adapter_hw_chg);
+
+	dev_dbg(&smb137b_chg->client->dev, "%s\n", __func__);
+	if (smb137b_chg->charging == false)
+		return 0;
+
+	smb137b_chg->charging = false;
+	smb137b_chg->batt_status = POWER_SUPPLY_STATUS_DISCHARGING;
+	smb137b_chg->batt_chg_type = POWER_SUPPLY_CHARGE_TYPE_NONE;
+
+	ret = smb137b_write_reg(smb137b_chg->client, COMMAND_A_REG,
+				smb137b_chg->cur_charging_mode);
+	if (ret) {
+		dev_err(&smb137b_chg->client->dev,
+			"%s couldn't write to command_reg\n", __func__);
+		goto out;
+	}
+
+	ret = smb137b_write_reg(smb137b_chg->client,
+					PIN_CTRL_REG, PIN_CTRL_REG_CHG_OFF);
+	if (ret)
+		dev_err(&smb137b_chg->client->dev,
+			"%s couldn't write to pin ctrl reg\n", __func__);
+
+out:
+	return ret;
+}
+
+static int smb137b_charger_switch(struct msm_hardware_charger *hw_chg)
+{
+	struct smb137b_data *smb137b_chg;
+
+	smb137b_chg = container_of(hw_chg, struct smb137b_data, adapter_hw_chg);
+	dev_dbg(&smb137b_chg->client->dev, "%s\n", __func__);
+	return 0;
+}
+
+static irqreturn_t smb137b_valid_handler(int irq, void *dev_id)
+{
+	int val;
+	struct smb137b_data *smb137b_chg;
+	struct i2c_client *client = dev_id;
+
+	smb137b_chg = i2c_get_clientdata(client);
+
+	pr_debug("%s Cable Detected USB inserted\n", __func__);
+	/*extra delay needed to allow CABLE_DET_N settling down and debounce
+	 before	trying to sample its correct value*/
+	usleep_range(1000, 1200);
+	val = gpio_get_value_cansleep(smb137b_chg->valid_n_gpio);
+	if (val < 0) {
+		dev_err(&smb137b_chg->client->dev,
+			"%s gpio_get_value failed for %d ret=%d\n", __func__,
+			smb137b_chg->valid_n_gpio, val);
+		goto err;
+	}
+	dev_dbg(&smb137b_chg->client->dev, "%s val=%d\n", __func__, val);
+
+	if (val) {
+		if (smb137b_chg->usb_status != SMB137B_ABSENT) {
+			smb137b_chg->usb_status = SMB137B_ABSENT;
+			msm_charger_notify_event(&smb137b_chg->adapter_hw_chg,
+					CHG_REMOVED_EVENT);
+		}
+	} else {
+		if (smb137b_chg->usb_status == SMB137B_ABSENT) {
+			smb137b_chg->usb_status = SMB137B_PRESENT;
+			msm_charger_notify_event(&smb137b_chg->adapter_hw_chg,
+					CHG_INSERTED_EVENT);
+		}
+	}
+err:
+	return IRQ_HANDLED;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static struct dentry *dent;
+static int debug_fs_otg;
+static int otg_get(void *data, u64 *value)
+{
+	*value = debug_fs_otg;
+	return 0;
+}
+static int otg_set(void *data, u64 value)
+{
+	smb137b_otg_power(debug_fs_otg);
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(smb137b_otg_fops, otg_get, otg_set, "%llu\n");
+
+static void smb137b_create_debugfs_entries(struct smb137b_data *smb137b_chg)
+{
+	dent = debugfs_create_dir("smb137b", NULL);
+	if (dent) {
+		debugfs_create_file("otg", 0644, dent, NULL, &smb137b_otg_fops);
+	}
+}
+static void smb137b_destroy_debugfs_entries(void)
+{
+	if (dent)
+		debugfs_remove_recursive(dent);
+}
+#else
+static void smb137b_create_debugfs_entries(struct smb137b_data *smb137b_chg)
+{
+}
+static void smb137b_destroy_debugfs_entries(void)
+{
+}
+#endif
+
+static int set_disable_status_param(const char *val, struct kernel_param *kp)
+{
+	int ret;
+
+	ret = param_set_int(val, kp);
+	if (ret)
+		return ret;
+
+	if (usb_smb137b_chg && disabled)
+		msm_charger_notify_event(&usb_smb137b_chg->adapter_hw_chg,
+				CHG_DONE_EVENT);
+
+	pr_debug("%s disabled =%d\n", __func__, disabled);
+	return 0;
+}
+module_param_call(disabled, set_disable_status_param, param_get_uint,
+					&disabled, 0644);
+static void smb137b_charge_sm(struct work_struct *smb137b_work)
+{
+	int ret;
+	struct smb137b_data *smb137b_chg;
+	u8 temp = 0;
+	int notify_batt_changed = 0;
+
+	smb137b_chg = container_of(smb137b_work, struct smb137b_data,
+			charge_work.work);
+
+	/*if not charging, exit smb137b charging state transition*/
+	if (!smb137b_chg->charging)
+		return;
+
+	dev_dbg(&smb137b_chg->client->dev, "%s\n", __func__);
+
+	ret = smb137b_read_reg(smb137b_chg->client, STATUS_F_REG, &temp);
+	if (ret) {
+		dev_err(&smb137b_chg->client->dev,
+			"%s couldn't read status f reg %d\n", __func__, ret);
+		goto out;
+	}
+	if (smb137b_chg->batt_present != !(temp & BATT_PRESENT_STAT)) {
+		smb137b_chg->batt_present = !(temp & BATT_PRESENT_STAT);
+		notify_batt_changed = 1;
+	}
+
+	if (!smb137b_chg->batt_present)
+		smb137b_chg->batt_chg_type = POWER_SUPPLY_CHARGE_TYPE_NONE;
+
+	if (!smb137b_chg->batt_present && smb137b_chg->charging)
+		msm_charger_notify_event(&smb137b_chg->adapter_hw_chg,
+				CHG_DONE_EVENT);
+
+	if (smb137b_chg->batt_present
+		&& smb137b_chg->charging
+		&& smb137b_chg->batt_chg_type
+			!= POWER_SUPPLY_CHARGE_TYPE_FAST) {
+		ret = smb137b_read_reg(smb137b_chg->client,
+						STATUS_E_REG, &temp);
+		if (ret) {
+			dev_err(&smb137b_chg->client->dev,
+				"%s couldn't read cntrl reg\n", __func__);
+			goto out;
+
+		} else {
+			if (temp & CHARGER_ERROR_STAT) {
+				dev_err(&smb137b_chg->client->dev,
+					"%s error E=0x%x\n", __func__, temp);
+				smb137b_dbg_print_status_regs(smb137b_chg);
+			}
+			if (((temp & CHARGING_STAT_E) >> 1)
+					>= FAST_CHG_E_STATUS) {
+				smb137b_chg->batt_chg_type
+					= POWER_SUPPLY_CHARGE_TYPE_FAST;
+				notify_batt_changed = 1;
+				msm_charger_notify_event(
+					&smb137b_chg->adapter_hw_chg,
+					CHG_BATT_BEGIN_FAST_CHARGING);
+			} else {
+				smb137b_chg->batt_chg_type
+					= POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
+			}
+		}
+	}
+
+out:
+	schedule_delayed_work(&smb137b_chg->charge_work,
+					SMB137B_CHG_PERIOD);
+}
+
+static void __smb137b_otg_power(int on)
+{
+	int ret;
+
+	if (on) {
+		ret = smb137b_write_reg(usb_smb137b_chg->client,
+					PIN_CTRL_REG, PIN_CTRL_REG_CHG_OFF);
+		if (ret) {
+			pr_err("%s turning off charging in pin_ctrl err=%d\n",
+								__func__, ret);
+			/*
+			 * don't change the command register if charging in
+			 * pin control cannot be turned off
+			 */
+			return;
+		}
+
+		ret = smb137b_write_reg(usb_smb137b_chg->client,
+			COMMAND_A_REG, COMMAND_A_REG_OTG_MODE);
+		if (ret)
+			pr_err("%s failed turning on OTG mode ret=%d\n",
+								__func__, ret);
+	} else {
+		ret = smb137b_write_reg(usb_smb137b_chg->client,
+			COMMAND_A_REG, COMMAND_A_REG_DEFAULT);
+		if (ret)
+			pr_err("%s failed turning off OTG mode ret=%d\n",
+								__func__, ret);
+		ret = smb137b_write_reg(usb_smb137b_chg->client,
+				PIN_CTRL_REG, PIN_CTRL_REG_DEFAULT);
+		if (ret)
+			pr_err("%s failed writing to pn_ctrl ret=%d\n",
+								__func__, ret);
+	}
+}
+static int __devinit smb137b_probe(struct i2c_client *client,
+				    const struct i2c_device_id *id)
+{
+	const struct smb137b_platform_data *pdata;
+	struct smb137b_data *smb137b_chg;
+	int ret = 0;
+
+	pdata = client->dev.platform_data;
+
+	if (pdata == NULL) {
+		dev_err(&client->dev, "%s no platform data\n", __func__);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (!i2c_check_functionality(client->adapter,
+				I2C_FUNC_SMBUS_BYTE_DATA)) {
+		ret = -EIO;
+		goto out;
+	}
+
+	smb137b_chg = kzalloc(sizeof(*smb137b_chg), GFP_KERNEL);
+	if (!smb137b_chg) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	INIT_DELAYED_WORK(&smb137b_chg->charge_work, smb137b_charge_sm);
+	smb137b_chg->client = client;
+	smb137b_chg->valid_n_gpio = pdata->valid_n_gpio;
+	smb137b_chg->batt_mah_rating = pdata->batt_mah_rating;
+	if (smb137b_chg->batt_mah_rating == 0)
+		smb137b_chg->batt_mah_rating = SMB137B_DEFAULT_BATT_RATING;
+
+	/*It supports USB-WALL charger and PC USB charger */
+	smb137b_chg->adapter_hw_chg.type = CHG_TYPE_USB;
+	smb137b_chg->adapter_hw_chg.rating = pdata->batt_mah_rating;
+	smb137b_chg->adapter_hw_chg.name = "smb137b-charger";
+	smb137b_chg->adapter_hw_chg.start_charging = smb137b_start_charging;
+	smb137b_chg->adapter_hw_chg.stop_charging = smb137b_stop_charging;
+	smb137b_chg->adapter_hw_chg.charging_switched = smb137b_charger_switch;
+
+	if (pdata->chg_detection_config)
+		ret = pdata->chg_detection_config();
+	if (ret) {
+		dev_err(&client->dev, "%s valid config failed ret=%d\n",
+			__func__, ret);
+		goto free_smb137b_chg;
+	}
+
+	ret = gpio_request(pdata->valid_n_gpio, "smb137b_charger_valid");
+	if (ret) {
+		dev_err(&client->dev, "%s gpio_request failed for %d ret=%d\n",
+			__func__, pdata->valid_n_gpio, ret);
+		goto free_smb137b_chg;
+	}
+
+	i2c_set_clientdata(client, smb137b_chg);
+
+	ret = msm_charger_register(&smb137b_chg->adapter_hw_chg);
+	if (ret) {
+		dev_err(&client->dev, "%s msm_charger_register\
+			failed for ret=%d\n", __func__, ret);
+		goto free_valid_gpio;
+	}
+
+	ret = irq_set_irq_wake(client->irq, 1);
+	if (ret) {
+		dev_err(&client->dev, "%s failed for irq_set_irq_wake %d ret =%d\n",
+			 __func__, client->irq, ret);
+		goto unregister_charger;
+	}
+
+	ret = request_threaded_irq(client->irq, NULL,
+				   smb137b_valid_handler,
+				   IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
+				   "smb137b_charger_valid", client);
+	if (ret) {
+		dev_err(&client->dev,
+			"%s request_threaded_irq failed for %d ret =%d\n",
+			__func__, client->irq, ret);
+		goto disable_irq_wake;
+	}
+
+	ret = gpio_get_value_cansleep(smb137b_chg->valid_n_gpio);
+	if (ret < 0) {
+		dev_err(&client->dev,
+			"%s gpio_get_value failed for %d ret=%d\n", __func__,
+			pdata->valid_n_gpio, ret);
+		/* assume absent */
+		ret = 1;
+	}
+	if (!ret) {
+		msm_charger_notify_event(&smb137b_chg->adapter_hw_chg,
+			CHG_INSERTED_EVENT);
+		smb137b_chg->usb_status = SMB137B_PRESENT;
+	}
+
+	ret = smb137b_read_reg(smb137b_chg->client, DEV_ID_REG,
+			&smb137b_chg->dev_id_reg);
+
+	ret = device_create_file(&smb137b_chg->client->dev, &dev_attr_id_reg);
+
+	/* TODO read min_design and max_design from chip registers */
+	smb137b_chg->min_design = 3200;
+	smb137b_chg->max_design = 4200;
+
+	smb137b_chg->batt_status = POWER_SUPPLY_STATUS_DISCHARGING;
+	smb137b_chg->batt_chg_type = POWER_SUPPLY_CHARGE_TYPE_NONE;
+
+	device_init_wakeup(&client->dev, 1);
+
+	mutex_lock(&init_lock);
+	usb_smb137b_chg = smb137b_chg;
+	if (init_otg_power)
+		__smb137b_otg_power(init_otg_power);
+	mutex_unlock(&init_lock);
+
+	smb137b_create_debugfs_entries(smb137b_chg);
+	dev_dbg(&client->dev,
+		"%s OK device_id = %x chg_state=%d\n", __func__,
+		smb137b_chg->dev_id_reg, smb137b_chg->usb_status);
+	return 0;
+
+disable_irq_wake:
+	irq_set_irq_wake(client->irq, 0);
+unregister_charger:
+	msm_charger_unregister(&smb137b_chg->adapter_hw_chg);
+free_valid_gpio:
+	gpio_free(pdata->valid_n_gpio);
+free_smb137b_chg:
+	kfree(smb137b_chg);
+out:
+	return ret;
+}
+
+void smb137b_otg_power(int on)
+{
+	pr_debug("%s Enter on=%d\n", __func__, on);
+
+	mutex_lock(&init_lock);
+	if (!usb_smb137b_chg) {
+		init_otg_power = !!on;
+		pr_warning("%s called when not initialized\n", __func__);
+		mutex_unlock(&init_lock);
+		return;
+	}
+	__smb137b_otg_power(on);
+	mutex_unlock(&init_lock);
+}
+
+static int __devexit smb137b_remove(struct i2c_client *client)
+{
+	const struct smb137b_platform_data *pdata;
+	struct smb137b_data *smb137b_chg = i2c_get_clientdata(client);
+
+	pdata = client->dev.platform_data;
+	device_init_wakeup(&client->dev, 0);
+	irq_set_irq_wake(client->irq, 0);
+	free_irq(client->irq, client);
+	gpio_free(pdata->valid_n_gpio);
+	cancel_delayed_work_sync(&smb137b_chg->charge_work);
+
+	msm_charger_notify_event(&smb137b_chg->adapter_hw_chg,
+			 CHG_REMOVED_EVENT);
+	msm_charger_unregister(&smb137b_chg->adapter_hw_chg);
+	smb137b_destroy_debugfs_entries();
+	kfree(smb137b_chg);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int smb137b_suspend(struct device *dev)
+{
+	struct smb137b_data *smb137b_chg = dev_get_drvdata(dev);
+
+	dev_dbg(&smb137b_chg->client->dev, "%s\n", __func__);
+	if (smb137b_chg->charging)
+		return -EBUSY;
+	return 0;
+}
+
+static int smb137b_resume(struct device *dev)
+{
+	struct smb137b_data *smb137b_chg = dev_get_drvdata(dev);
+
+	dev_dbg(&smb137b_chg->client->dev, "%s\n", __func__);
+	return 0;
+}
+
+static const struct dev_pm_ops smb137b_pm_ops = {
+	.suspend = smb137b_suspend,
+	.resume = smb137b_resume,
+};
+#endif
+
+static const struct i2c_device_id smb137b_id[] = {
+	{"smb137b", 0},
+	{},
+};
+MODULE_DEVICE_TABLE(i2c, smb137b_id);
+
+static struct i2c_driver smb137b_driver = {
+	.driver = {
+		   .name = "smb137b",
+		   .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+		   .pm = &smb137b_pm_ops,
+#endif
+	},
+	.probe = smb137b_probe,
+	.remove = __devexit_p(smb137b_remove),
+	.id_table = smb137b_id,
+};
+
+static int __init smb137b_init(void)
+{
+	return i2c_add_driver(&smb137b_driver);
+}
+module_init(smb137b_init);
+
+static void __exit smb137b_exit(void)
+{
+	return i2c_del_driver(&smb137b_driver);
+}
+module_exit(smb137b_exit);
+
+MODULE_AUTHOR("Abhijeet Dharmapurikar <adharmap@codeaurora.org>");
+MODULE_DESCRIPTION("Driver for SMB137B Charger chip");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("i2c:smb137b");