Merge commit 'AU_LINUX_ANDROID_ICS.04.00.04.00.126' into msm-3.4

AU_LINUX_ANDROID_ICS.04.00.04.00.126 from msm-3.0.
First parent is from google/android-3.4.

* commit 'AU_LINUX_ANDROID_ICS.04.00.04.00.126': (8712 commits)
  PRNG: Device tree entry for qrng device.
  vidc:1080p: Set video core timeout value for Thumbnail mode
  msm: sps: improve the debugging support in SPS driver
  board-8064 msm: Overlap secure and non secure video firmware heaps.
  msm: clock: Add handoff ops for 7x30 and copper XO clocks
  msm_fb: display: Wait for external vsync before DTV IOMMU unmap
  msm: Fix ciruclar dependency in debug UART settings
  msm: gdsc: Add GDSC regulator driver for msm-copper
  defconfig: Enable Mobicore Driver.
  mobicore: Add mobicore driver.
  mobicore: rename variable to lower case.
  mobicore: rename folder.
  mobicore: add makefiles
  mobicore: initial import of kernel driver
  ASoC: msm: Add SLIMBUS_2_RX CPU DAI
  board-8064-gpio: Update FUNC for EPM SPI CS
  msm_fb: display: Remove chicken bit config during video playback
  mmc: msm_sdcc: enable the sanitize capability
  msm-fb: display: lm2 writeback support on mpq platfroms
  msm_fb: display: Disable LVDS phy & pll during panel off
  ...

Signed-off-by: Steve Muckle <smuckle@codeaurora.org>
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 99dc29f..a263750 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -288,6 +288,133 @@
 	  Say Y to enable support for the battery charger control sysfs and
 	  platform data of MAX8998/LP3974 PMICs.
 
+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 PMIC8XXX_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 || PM8921_CHARGER)
+	depends on I2C
+	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 SMB349_CHARGER
+	tristate "smb349 charger"
+	depends on I2C
+	help
+	  Say Y to enable support for the SMB349 switching mode based charger
+	  and sysfs. The driver supports controlling charger-enable and
+	  current limiting capabilities. The driver also lets the
+	  SMB349 be operated as a slave device via the power supply
+	  framework.
+
+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 PM8XXX_CCADC
+	tristate "PM8XXX battery current adc driver"
+	depends on MFD_PM8921_CORE
+	help
+	  Say Y here to enable support for pm8921 chip bms subdevice
+
+config LTC4088_CHARGER
+	tristate "LTC4088 Charger driver"
+	depends on GPIOLIB
+	help
+	  Say Y here to enable support for ltc4088 chip charger. It controls the
+	  operations through GPIO pins.
+
+config PM8921_BMS
+	select PM8XXX_CCADC
+	tristate "PM8921 Battery Monitoring System driver"
+	depends on MFD_PM8921_CORE
+	help
+	  Say Y here to enable support for pm8921 chip bms subdevice
+
 config CHARGER_SMB347
 	tristate "Summit Microelectronics SMB347 Battery Charger"
 	depends on I2C
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index b6b2434..007d75b 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -43,4 +43,18 @@
 obj-$(CONFIG_CHARGER_MANAGER)	+= charger-manager.o
 obj-$(CONFIG_CHARGER_MAX8997)	+= max8997_charger.o
 obj-$(CONFIG_CHARGER_MAX8998)	+= max8998_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_SMB349_CHARGER)   += smb349.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_PM8XXX_CCADC)	+= pm8xxx-ccadc.o
+obj-$(CONFIG_PM8921_BMS)	+= pm8921-bms.o
+obj-$(CONFIG_PM8921_CHARGER)	+= pm8921-charger.o
+obj-$(CONFIG_LTC4088_CHARGER)	+= ltc4088-charger.o
 obj-$(CONFIG_CHARGER_SMB347)	+= smb347-charger.o
diff --git a/drivers/power/bq27520_fuelgauger.c b/drivers/power/bq27520_fuelgauger.c
new file mode 100644
index 0000000..3c191cd
--- /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_battery_gauge_register(&bq27520_batt_gauge);
+	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();
+
+	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..7ebbf46
--- /dev/null
+++ b/drivers/power/isl9519q.c
@@ -0,0 +1,849 @@
+/* Copyright (c) 2010-2012, 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/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/power_supply.h>
+#include <linux/slab.h>
+#include <linux/i2c/isl9519.h>
+#include <linux/msm_adc.h>
+#include <linux/spinlock.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_SEC	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;
+	int				suspended;
+	int				charge_at_resume;
+	struct power_supply		dc_psy;
+	spinlock_t			lock;
+	bool				notify_by_pmic;
+	bool				trickle;
+};
+
+static struct isl9519q_struct *the_isl_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;
+	}
+
+	pr_debug("reg=0x%x.val=0x%x.\n", reg, *val);
+
+	return 0;
+}
+
+static int isl9519q_write_reg(struct i2c_client *client, int reg,
+	u16 val)
+{
+	int ret;
+	struct isl9519q_struct *isl_chg;
+
+	pr_debug("reg=0x%x.val=0x%x.\n", reg, val);
+
+	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;
+}
+
+/**
+ * Read charge-current via ADC.
+ *
+ * The ISL CCMON (charge-current-monitor) pin is connected to
+ * the PMIC MPP#X pin.
+ * This not required when notify_by_pmic is used where the PMIC
+ * uses BMS to notify the ISL on charging-done / charge-resume.
+ */
+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("called for %d\n", channel);
+	ret = adc_channel_open(channel, &h);
+	if (ret) {
+		pr_err("couldnt open channel %d ret=%d\n", channel, ret);
+		goto out;
+	}
+	init_completion(&conv_complete_evt);
+	ret = adc_channel_request_conv(h, &conv_complete_evt);
+	if (ret) {
+		pr_err("couldnt request conv channel %d ret=%d\n",
+						channel, ret);
+		goto out;
+	}
+	ret = wait_for_completion_interruptible(&conv_complete_evt);
+	if (ret) {
+		pr_err("wait interrupted channel %d ret=%d\n", channel, ret);
+		goto out;
+	}
+	ret = adc_channel_read_result(h, &adc_chan_result);
+	if (ret) {
+		pr_err("couldnt read result channel %d ret=%d\n",
+						channel, ret);
+		goto out;
+	}
+	ret = adc_channel_close(h);
+	if (ret)
+		pr_err("couldnt close channel %d ret=%d\n", channel, ret);
+	if (mv_reading)
+		*mv_reading = (int)adc_chan_result.measurement;
+
+	pr_debug("done for %d\n", channel);
+	return adc_chan_result.physical;
+out:
+	*mv_reading = 0;
+	pr_debug("done with error for %d\n", channel);
+
+	return -EINVAL;
+}
+
+static bool is_trickle_charging(struct isl9519q_struct *isl_chg)
+{
+	u16 ctrl = 0;
+	int ret;
+
+	ret = isl9519q_read_reg(isl_chg->client, CONTROL_REG, &ctrl);
+
+	if (!ret) {
+		pr_debug("control_reg=0x%x.\n", ctrl);
+	} else {
+		dev_err(&isl_chg->client->dev,
+			"%s couldnt read cntrl reg\n", __func__);
+	}
+
+	if (ctrl & TRCKL_CHG_STATUS_BIT)
+		return true;
+
+	return false;
+}
+
+static void isl_adapter_check_ichg(struct isl9519q_struct *isl_chg)
+{
+	int ichg; /* isl charger current */
+	int mv_reading = 0;
+
+	ichg = 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__, ichg);
+
+	if (ichg >= 0 && ichg <= isl_chg->term_current)
+		msm_charger_notify_event(&isl_chg->adapter_hw_chg,
+					 CHG_DONE_EVENT);
+
+	isl_chg->trickle = is_trickle_charging(isl_chg);
+	if (isl_chg->trickle)
+		msm_charger_notify_event(&isl_chg->adapter_hw_chg,
+					 CHG_BATT_BEGIN_FAST_CHARGING);
+}
+
+/**
+ * isl9519q_worker
+ *
+ * Periodic task required to kick the ISL HW watchdog to keep
+ * charging.
+ *
+ * @isl9519_work: work context.
+ */
+static void isl9519q_worker(struct work_struct *isl9519_work)
+{
+	struct isl9519q_struct *isl_chg;
+
+	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) {
+		pr_debug("stop charging.\n");
+		isl9519q_write_reg(isl_chg->client, CHG_CURRENT_REG, 0);
+		return; /* Stop periodic worker */
+	}
+
+	/* Kick the dog by writting to CHG_CURRENT_REG */
+	isl9519q_write_reg(isl_chg->client, CHG_CURRENT_REG,
+			   isl_chg->chgcurrent);
+
+	if (isl_chg->notify_by_pmic)
+		isl_chg->trickle = is_trickle_charging(isl_chg);
+	else
+		isl_adapter_check_ichg(isl_chg);
+
+	schedule_delayed_work(&isl_chg->charge_work,
+			      (ISL9519_CHG_PERIOD_SEC * HZ));
+}
+
+static int isl9519q_start_charging(struct isl9519q_struct *isl_chg,
+				   int chg_voltage, int chg_current)
+{
+	pr_debug("\n");
+
+	if (isl_chg->charging) {
+		pr_warn("already charging.\n");
+		return 0;
+	}
+
+	if (isl_chg->suspended) {
+		pr_warn("suspended - can't start charging.\n");
+		isl_chg->charge_at_resume = 1;
+		return 0;
+	}
+
+	dev_dbg(&isl_chg->client->dev,
+		"%s starting timed work.period=%d seconds.\n",
+		__func__, (int) ISL9519_CHG_PERIOD_SEC);
+
+	/*
+	 * The ISL will start charging from the worker context.
+	 * This API might be called from interrupt context.
+	 */
+	schedule_delayed_work(&isl_chg->charge_work, 1);
+
+	isl_chg->charging = true;
+
+	return 0;
+}
+
+static int isl9519q_stop_charging(struct isl9519q_struct *isl_chg)
+{
+	pr_debug("\n");
+
+	if (!(isl_chg->charging)) {
+		pr_warn("already not charging.\n");
+		return 0;
+	}
+
+	if (isl_chg->suspended) {
+		isl_chg->charge_at_resume = 0;
+		return 0;
+	}
+
+	dev_dbg(&isl_chg->client->dev, "%s\n", __func__);
+
+	isl_chg->charging = false;
+	isl_chg->trickle = false;
+	/*
+	 * The ISL will stop charging from the worker context.
+	 * This API might be called from interrupt context.
+	 */
+	schedule_delayed_work(&isl_chg->charge_work, 1);
+
+	return 0;
+}
+
+static int isl_adapter_start_charging(struct msm_hardware_charger *hw_chg,
+				      int chg_voltage, int chg_current)
+{
+	int rc;
+	struct isl9519q_struct *isl_chg;
+
+	isl_chg = container_of(hw_chg, struct isl9519q_struct, adapter_hw_chg);
+	rc = isl9519q_start_charging(isl_chg, chg_voltage, chg_current);
+
+	return rc;
+}
+
+static int isl_adapter_stop_charging(struct msm_hardware_charger *hw_chg)
+{
+	int rc;
+	struct isl9519q_struct *isl_chg;
+
+	isl_chg = container_of(hw_chg, struct isl9519q_struct, adapter_hw_chg);
+	rc = isl9519q_stop_charging(isl_chg);
+
+	return rc;
+}
+
+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;
+}
+
+static enum power_supply_property pm_power_props[] = {
+	POWER_SUPPLY_PROP_ONLINE,
+	POWER_SUPPLY_PROP_CURRENT_MAX,
+	POWER_SUPPLY_PROP_CHARGE_TYPE,
+};
+
+static char *pm_power_supplied_to[] = {
+	"battery",
+};
+
+static int get_prop_charge_type(struct isl9519q_struct *isl_chg)
+{
+	if (!isl_chg->present)
+		return POWER_SUPPLY_CHARGE_TYPE_NONE;
+
+	if (isl_chg->trickle)
+		return POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
+
+	if (isl_chg->charging)
+		return POWER_SUPPLY_CHARGE_TYPE_FAST;
+
+	return POWER_SUPPLY_CHARGE_TYPE_NONE;
+}
+static int pm_power_get_property(struct power_supply *psy,
+				  enum power_supply_property psp,
+				  union power_supply_propval *val)
+{
+	struct isl9519q_struct *isl_chg = container_of(psy,
+						struct isl9519q_struct,
+						dc_psy);
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_CURRENT_MAX:
+		val->intval = isl_chg->chgcurrent;
+		break;
+	case POWER_SUPPLY_PROP_ONLINE:
+		val->intval = (int)isl_chg->present;
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_TYPE:
+		val->intval = get_prop_charge_type(isl_chg);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int pm_power_set_property(struct power_supply *psy,
+				  enum power_supply_property psp,
+				  const union power_supply_propval *val)
+{
+	struct isl9519q_struct *isl_chg = container_of(psy,
+						struct isl9519q_struct,
+						dc_psy);
+	unsigned long flags;
+	int rc;
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_ONLINE:
+		if (val->intval) {
+			isl_chg->present = val->intval;
+		} else {
+			isl_chg->present = 0;
+			if (isl_chg->charging)
+				goto stop_charging;
+		}
+		break;
+	case POWER_SUPPLY_PROP_CURRENT_MAX:
+		if (val->intval) {
+			if (isl_chg->chgcurrent != val->intval)
+				return -EINVAL;
+		}
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_TYPE:
+		if (val->intval && isl_chg->present) {
+			if (val->intval == POWER_SUPPLY_CHARGE_TYPE_FAST)
+				goto start_charging;
+			if (val->intval == POWER_SUPPLY_CHARGE_TYPE_NONE)
+				goto stop_charging;
+		} else {
+			return -EINVAL;
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+	power_supply_changed(&isl_chg->dc_psy);
+	return 0;
+
+start_charging:
+	spin_lock_irqsave(&isl_chg->lock, flags);
+	rc = isl9519q_start_charging(isl_chg, 0, isl_chg->chgcurrent);
+	if (rc)
+		pr_err("Failed to start charging rc=%d\n", rc);
+	spin_unlock_irqrestore(&isl_chg->lock, flags);
+	power_supply_changed(&isl_chg->dc_psy);
+	return rc;
+
+stop_charging:
+	spin_lock_irqsave(&isl_chg->lock, flags);
+	rc = isl9519q_stop_charging(isl_chg);
+	if (rc)
+		pr_err("Failed to start charging rc=%d\n", rc);
+	spin_unlock_irqrestore(&isl_chg->lock, flags);
+	power_supply_changed(&isl_chg->dc_psy);
+	return rc;
+}
+
+#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_init_adapter(struct isl9519q_struct *isl_chg)
+{
+	int ret;
+	struct i2c_client *client = isl_chg->client;
+	struct isl_platform_data *pdata = client->dev.platform_data;
+
+	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 = isl_adapter_start_charging;
+	isl_chg->adapter_hw_chg.stop_charging = isl_adapter_stop_charging;
+	isl_chg->adapter_hw_chg.charging_switched = isl9519q_charging_switched;
+
+	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 out;
+	}
+
+	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);
+
+	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;
+	}
+
+	return 0;
+
+unregister:
+	msm_charger_unregister(&isl_chg->adapter_hw_chg);
+free_gpio:
+	gpio_free(pdata->valid_n_gpio);
+out:
+	return ret;
+
+}
+
+static int __devinit isl9519q_init_ext_chg(struct isl9519q_struct *isl_chg)
+{
+	int ret;
+
+	isl_chg->dc_psy.name = "dc";
+	isl_chg->dc_psy.type = POWER_SUPPLY_TYPE_MAINS;
+	isl_chg->dc_psy.supplied_to = pm_power_supplied_to;
+	isl_chg->dc_psy.num_supplicants = ARRAY_SIZE(pm_power_supplied_to);
+	isl_chg->dc_psy.properties = pm_power_props;
+	isl_chg->dc_psy.num_properties = ARRAY_SIZE(pm_power_props);
+	isl_chg->dc_psy.get_property = pm_power_get_property;
+	isl_chg->dc_psy.set_property = pm_power_set_property;
+
+	ret = power_supply_register(&isl_chg->client->dev, &isl_chg->dc_psy);
+	if (ret) {
+		pr_err("failed to register dc charger.ret=%d.\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+static int set_reg(void *data, u64 val)
+{
+	int addr = (int)data;
+	int ret;
+	u16 temp;
+
+	temp = (u16) val;
+	ret = isl9519q_write_reg(the_isl_chg->client, addr, temp);
+
+	if (ret) {
+		pr_err("isl9519q_write_reg to %x value =%d errored = %d\n",
+			addr, temp, ret);
+		return -EAGAIN;
+	}
+	return 0;
+}
+static int get_reg(void *data, u64 *val)
+{
+	int addr = (int)data;
+	int ret;
+	u16 temp;
+
+	ret = isl9519q_read_reg(the_isl_chg->client, addr, &temp);
+	if (ret) {
+		pr_err("isl9519q_read_reg to %x value =%d errored = %d\n",
+			addr, temp, ret);
+		return -EAGAIN;
+	}
+
+	*val = temp;
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(reg_fops, get_reg, set_reg, "0x%02llx\n");
+
+static void create_debugfs_entries(struct isl9519q_struct *isl_chg)
+{
+	isl_chg->dent = debugfs_create_dir("isl9519q", NULL);
+
+	if (IS_ERR(isl_chg->dent)) {
+		pr_err("isl9519q driver couldn't create debugfs dir\n");
+		return;
+	}
+
+	debugfs_create_file("CHG_CURRENT_REG", 0644, isl_chg->dent,
+				(void *) CHG_CURRENT_REG, &reg_fops);
+	debugfs_create_file("MAX_SYS_VOLTAGE_REG", 0644, isl_chg->dent,
+				(void *) MAX_SYS_VOLTAGE_REG, &reg_fops);
+	debugfs_create_file("CONTROL_REG", 0644, isl_chg->dent,
+				(void *) CONTROL_REG, &reg_fops);
+	debugfs_create_file("MIN_SYS_VOLTAGE_REG", 0644, isl_chg->dent,
+				(void *) MIN_SYS_VOLTAGE_REG, &reg_fops);
+	debugfs_create_file("INPUT_CURRENT_REG", 0644, isl_chg->dent,
+				(void *) INPUT_CURRENT_REG, &reg_fops);
+	debugfs_create_file("MANUFACTURER_ID_REG", 0644, isl_chg->dent,
+				(void *) MANUFACTURER_ID_REG, &reg_fops);
+	debugfs_create_file("DEVICE_ID_REG", 0644, isl_chg->dent,
+				(void *) DEVICE_ID_REG, &reg_fops);
+}
+
+static void remove_debugfs_entries(struct isl9519q_struct *isl_chg)
+{
+	if (isl_chg->dent)
+		debugfs_remove_recursive(isl_chg->dent);
+}
+
+static int __devinit isl9519q_hwinit(struct isl9519q_struct *isl_chg)
+{
+	int ret;
+
+	ret = isl9519q_write_reg(isl_chg->client, MAX_SYS_VOLTAGE_REG,
+			isl_chg->max_system_voltage);
+	if (ret) {
+		pr_err("Failed to set MAX_SYS_VOLTAGE rc=%d\n", ret);
+		return ret;
+	}
+
+	ret = isl9519q_write_reg(isl_chg->client, MIN_SYS_VOLTAGE_REG,
+			isl_chg->min_system_voltage);
+	if (ret) {
+		pr_err("Failed to set MIN_SYS_VOLTAGE rc=%d\n", ret);
+		return ret;
+	}
+
+	if (isl_chg->input_current) {
+		ret = isl9519q_write_reg(isl_chg->client,
+				INPUT_CURRENT_REG,
+				isl_chg->input_current);
+		if (ret) {
+			pr_err("Failed to set INPUT_CURRENT rc=%d\n", ret);
+			return ret;
+		}
+	}
+	return 0;
+}
+
+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;
+
+	pr_debug("\n");
+
+	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;
+	}
+
+	spin_lock_init(&isl_chg->lock);
+
+	INIT_DELAYED_WORK(&isl_chg->charge_work, isl9519q_worker);
+	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 is Notified by PMIC to start/stop charging, rather than
+	 * handling interrupt from ISL for End-Of-Chargring, and
+	 * monitoring the charge-current periodically. The valid_n_gpio
+	 * is also not used, dc-present is detected by PMIC.
+	 */
+	isl_chg->notify_by_pmic = (client->irq == 0);
+	i2c_set_clientdata(client, isl_chg);
+
+	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;
+		}
+	}
+
+	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_hwinit(isl_chg);
+	if (ret)
+		goto free_isl_chg;
+
+	if (isl_chg->notify_by_pmic)
+		ret = isl9519q_init_ext_chg(isl_chg);
+	else
+		ret = isl9519q_init_adapter(isl_chg);
+
+	if (ret)
+		goto free_isl_chg;
+
+	the_isl_chg = isl_chg;
+	create_debugfs_entries(isl_chg);
+
+	pr_info("OK.\n");
+
+	return 0;
+
+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);
+	if (isl_chg->notify_by_pmic) {
+		power_supply_unregister(&isl_chg->dc_psy);
+	} else {
+		msm_charger_notify_event(&isl_chg->adapter_hw_chg,
+							CHG_REMOVED_EVENT);
+		msm_charger_unregister(&isl_chg->adapter_hw_chg);
+	}
+	remove_debugfs_entries(isl_chg);
+	the_isl_chg = NULL;
+	kfree(isl_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;
+
+	isl_chg->suspended  = 1;
+	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__);
+	isl_chg->suspended  = 0;
+	if (isl_chg->charge_at_resume) {
+		isl_chg->charge_at_resume = 0;
+		isl9519q_start_charging(isl_chg, 0, 0);
+	}
+	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);
+}
+
+late_initcall_sync(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/ltc4088-charger.c b/drivers/power/ltc4088-charger.c
new file mode 100644
index 0000000..dbc75cd
--- /dev/null
+++ b/drivers/power/ltc4088-charger.c
@@ -0,0 +1,338 @@
+/*
+ * 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/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/errno.h>
+#include <linux/power_supply.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/power/ltc4088-charger.h>
+
+#define MAX_CURRENT_UA(n)	(n)
+#define MAX_CURRENT_MA(n)	(n * MAX_CURRENT_UA(1000))
+
+/**
+ * ltc4088_max_current - A typical current values supported by the charger
+ * @LTC4088_MAX_CURRENT_100mA:  100mA current
+ * @LTC4088_MAX_CURRENT_500mA:  500mA current
+ * @LTC4088_MAX_CURRENT_1A:     1A current
+ */
+enum ltc4088_max_current {
+	LTC4088_MAX_CURRENT_100mA = 100,
+	LTC4088_MAX_CURRENT_500mA = 500,
+	LTC4088_MAX_CURRENT_1A = 1000,
+};
+
+/**
+ * struct ltc4088_chg_chip - Device information
+ * @dev:			Device pointer to access the parent
+ * @lock:			Enable mutual exclusion
+ * @usb_psy:			USB device information
+ * @gpio_mode_select_d0:	GPIO #pin for D0 charger line
+ * @gpio_mode_select_d1:	GPIO #pin for D1 charger line
+ * @gpio_mode_select_d2:	GPIO #pin for D2 charger line
+ * @max_current:		Maximum current that is supplied at this time
+ */
+struct ltc4088_chg_chip {
+	struct device		*dev;
+	struct mutex		lock;
+	struct power_supply	usb_psy;
+	unsigned int		gpio_mode_select_d0;
+	unsigned int		gpio_mode_select_d1;
+	unsigned int		gpio_mode_select_d2;
+	unsigned int		max_current;
+};
+
+static enum power_supply_property pm_power_props[] = {
+	POWER_SUPPLY_PROP_CURRENT_MAX,
+	POWER_SUPPLY_PROP_ONLINE,
+};
+
+static char *pm_power_supplied_to[] = {
+	"battery",
+};
+
+static int ltc4088_set_charging(struct ltc4088_chg_chip *chip, bool enable)
+{
+	mutex_lock(&chip->lock);
+
+	if (enable) {
+		gpio_set_value_cansleep(chip->gpio_mode_select_d2, 0);
+	} else {
+		/* When disabling charger, set the max current to 0 also */
+		chip->max_current = 0;
+		gpio_set_value_cansleep(chip->gpio_mode_select_d0, 1);
+		gpio_set_value_cansleep(chip->gpio_mode_select_d1, 1);
+		gpio_set_value_cansleep(chip->gpio_mode_select_d2, 1);
+	}
+
+	mutex_unlock(&chip->lock);
+
+	return 0;
+}
+
+static void ltc4088_set_max_current(struct ltc4088_chg_chip *chip, int value)
+{
+	mutex_lock(&chip->lock);
+
+	/* If current is less than 100mA, we can not support that granularity */
+	if (value <  MAX_CURRENT_MA(LTC4088_MAX_CURRENT_100mA)) {
+		chip->max_current = 0;
+		gpio_set_value_cansleep(chip->gpio_mode_select_d0, 1);
+		gpio_set_value_cansleep(chip->gpio_mode_select_d1, 1);
+	} else if (value <  MAX_CURRENT_MA(LTC4088_MAX_CURRENT_500mA)) {
+		chip->max_current = MAX_CURRENT_MA(LTC4088_MAX_CURRENT_100mA);
+		gpio_set_value_cansleep(chip->gpio_mode_select_d0, 0);
+		gpio_set_value_cansleep(chip->gpio_mode_select_d1, 0);
+	} else if (value <  MAX_CURRENT_MA(LTC4088_MAX_CURRENT_1A)) {
+		chip->max_current = MAX_CURRENT_MA(LTC4088_MAX_CURRENT_500mA);
+		gpio_set_value_cansleep(chip->gpio_mode_select_d0, 0);
+		gpio_set_value_cansleep(chip->gpio_mode_select_d1, 1);
+	} else {
+		chip->max_current = MAX_CURRENT_MA(LTC4088_MAX_CURRENT_1A);
+		gpio_set_value_cansleep(chip->gpio_mode_select_d0, 1);
+		gpio_set_value_cansleep(chip->gpio_mode_select_d1, 0);
+	}
+
+	mutex_unlock(&chip->lock);
+}
+
+static void ltc4088_set_charging_off(struct ltc4088_chg_chip *chip)
+{
+	gpio_set_value_cansleep(chip->gpio_mode_select_d0, 1);
+	gpio_set_value_cansleep(chip->gpio_mode_select_d1, 1);
+}
+
+static int ltc4088_set_initial_state(struct ltc4088_chg_chip *chip)
+{
+	int rc;
+
+	rc = gpio_request(chip->gpio_mode_select_d0, "ltc4088_D0");
+	if (rc) {
+		pr_err("gpio request failed for GPIO %d\n",
+				chip->gpio_mode_select_d0);
+		return rc;
+	}
+
+	rc = gpio_request(chip->gpio_mode_select_d1, "ltc4088_D1");
+	if (rc) {
+		pr_err("gpio request failed for GPIO %d\n",
+				chip->gpio_mode_select_d1);
+		goto gpio_err_d0;
+	}
+
+	rc = gpio_request(chip->gpio_mode_select_d2, "ltc4088_D2");
+	if (rc) {
+		pr_err("gpio request failed for GPIO %d\n",
+				chip->gpio_mode_select_d2);
+		goto gpio_err_d1;
+	}
+
+	rc = gpio_direction_output(chip->gpio_mode_select_d0, 0);
+	if (rc) {
+		pr_err("failed to set direction for GPIO %d\n",
+				chip->gpio_mode_select_d0);
+		goto gpio_err_d2;
+	}
+
+	rc = gpio_direction_output(chip->gpio_mode_select_d1, 0);
+	if (rc) {
+		pr_err("failed to set direction for GPIO %d\n",
+				chip->gpio_mode_select_d1);
+		goto gpio_err_d2;
+	}
+
+	rc = gpio_direction_output(chip->gpio_mode_select_d2, 1);
+	if (rc) {
+		pr_err("failed to set direction for GPIO %d\n",
+				chip->gpio_mode_select_d2);
+		goto gpio_err_d2;
+	}
+
+	return 0;
+
+gpio_err_d2:
+	gpio_free(chip->gpio_mode_select_d2);
+gpio_err_d1:
+	gpio_free(chip->gpio_mode_select_d1);
+gpio_err_d0:
+	gpio_free(chip->gpio_mode_select_d0);
+	return rc;
+}
+
+static int pm_power_get_property(struct power_supply *psy,
+				  enum power_supply_property psp,
+				  union power_supply_propval *val)
+{
+	struct ltc4088_chg_chip *chip;
+
+	if (psy->type == POWER_SUPPLY_TYPE_USB) {
+		chip = container_of(psy, struct ltc4088_chg_chip,
+						usb_psy);
+		switch (psp) {
+		case POWER_SUPPLY_PROP_ONLINE:
+			if (chip->max_current)
+				val->intval = 1;
+			else
+				val->intval = 0;
+			break;
+		case POWER_SUPPLY_PROP_CURRENT_MAX:
+			val->intval = chip->max_current;
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+	return 0;
+}
+
+static int pm_power_set_property(struct power_supply *psy,
+				  enum power_supply_property psp,
+				  const union power_supply_propval *val)
+{
+	struct ltc4088_chg_chip *chip;
+
+	if (psy->type == POWER_SUPPLY_TYPE_USB) {
+		chip = container_of(psy, struct ltc4088_chg_chip,
+						usb_psy);
+		switch (psp) {
+		case POWER_SUPPLY_PROP_ONLINE:
+			ltc4088_set_charging(chip, val->intval);
+			break;
+		case POWER_SUPPLY_PROP_CURRENT_MAX:
+			ltc4088_set_max_current(chip, val->intval);
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+	return 0;
+}
+
+static int __devinit ltc4088_charger_probe(struct platform_device *pdev)
+{
+	int rc;
+	struct ltc4088_chg_chip *chip;
+	const struct ltc4088_charger_platform_data *pdata
+			= pdev->dev.platform_data;
+
+	if (!pdata) {
+		pr_err("missing platform data\n");
+		return -EINVAL;
+	}
+
+	chip = kzalloc(sizeof(struct ltc4088_chg_chip),
+					GFP_KERNEL);
+	if (!chip) {
+		pr_err("Cannot allocate pm_chg_chip\n");
+		return -ENOMEM;
+	}
+
+	chip->dev = &pdev->dev;
+
+	if (pdata->gpio_mode_select_d0 < 0 ||
+		 pdata->gpio_mode_select_d1 < 0 ||
+		 pdata->gpio_mode_select_d2 < 0) {
+		pr_err("Invalid platform data supplied\n");
+		rc = -EINVAL;
+		goto free_chip;
+	}
+
+	mutex_init(&chip->lock);
+
+	chip->gpio_mode_select_d0 = pdata->gpio_mode_select_d0;
+	chip->gpio_mode_select_d1 = pdata->gpio_mode_select_d1;
+	chip->gpio_mode_select_d2 = pdata->gpio_mode_select_d2;
+
+	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->usb_psy.set_property = pm_power_set_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_chip;
+	}
+
+	platform_set_drvdata(pdev, chip);
+
+	rc = ltc4088_set_initial_state(chip);
+	if (rc < 0) {
+		pr_err("setting initial state failed rc = %d\n", rc);
+		goto unregister_usb;
+	}
+
+	return 0;
+
+unregister_usb:
+	platform_set_drvdata(pdev, NULL);
+	power_supply_unregister(&chip->usb_psy);
+free_chip:
+	kfree(chip);
+
+	return rc;
+}
+
+static int __devexit ltc4088_charger_remove(struct platform_device *pdev)
+{
+	struct ltc4088_chg_chip *chip = platform_get_drvdata(pdev);
+
+	ltc4088_set_charging_off(chip);
+
+	gpio_free(chip->gpio_mode_select_d2);
+	gpio_free(chip->gpio_mode_select_d1);
+	gpio_free(chip->gpio_mode_select_d0);
+
+	power_supply_unregister(&chip->usb_psy);
+
+	platform_set_drvdata(pdev, NULL);
+	mutex_destroy(&chip->lock);
+	kfree(chip);
+
+	return 0;
+}
+
+static struct platform_driver ltc4088_charger_driver = {
+	.probe	= ltc4088_charger_probe,
+	.remove	= __devexit_p(ltc4088_charger_remove),
+	.driver	= {
+		.name	= LTC4088_CHARGER_DEV_NAME,
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init ltc4088_charger_init(void)
+{
+	return platform_driver_register(&ltc4088_charger_driver);
+}
+
+static void __exit ltc4088_charger_exit(void)
+{
+	platform_driver_unregister(&ltc4088_charger_driver);
+}
+
+subsys_initcall(ltc4088_charger_init);
+module_exit(ltc4088_charger_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("LTC4088 charger/battery driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:" LTC4088_CHARGER_DEV_NAME);
diff --git a/drivers/power/msm_battery.c b/drivers/power/msm_battery.c
new file mode 100644
index 0000000..0555399
--- /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  0
+
+#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..8594ec2
--- /dev/null
+++ b/drivers/power/msm_charger.c
@@ -0,0 +1,1286 @@
+/* Copyright (c) 2010-2012, 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)
+{
+	int capacity;
+
+	if (msm_batt_gauge && msm_batt_gauge->get_batt_remaining_capacity)
+		capacity = msm_batt_gauge->get_batt_remaining_capacity();
+	else
+		capacity = msm_chg.get_batt_capacity_percent();
+
+	if (capacity <= 10)
+		pr_err("battery capacity very low = %d\n", capacity);
+
+	return capacity;
+}
+
+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;
+}
+
+static void msm_enable_system_current(struct msm_hardware_charger_priv *priv)
+{
+	if (priv->hw_chg->start_system_current)
+		priv->hw_chg->start_system_current(priv->hw_chg,
+					 priv->max_source_current);
+}
+
+static void msm_disable_system_current(struct msm_hardware_charger_priv *priv)
+{
+	if (priv->hw_chg->stop_system_current)
+		priv->hw_chg->stop_system_current(priv->hw_chg);
+}
+
+/* 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)
+{
+	struct msm_hardware_charger_priv *old_chg_priv = NULL;
+
+	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) {
+		/*
+		 * a better charger was found, ask the current charger
+		 * to stop charging if it was charging
+		 */
+		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;
+		old_chg_priv = msm_chg.current_chg_priv;
+		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);
+
+		msm_enable_system_current(msm_chg.current_chg_priv);
+		/*
+		 * since a better charger was chosen, ask the old
+		 * charger to stop providing system current
+		 */
+		if (old_chg_priv != NULL)
+			msm_disable_system_current(old_chg_priv);
+
+		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) {
+		msm_disable_system_current(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;
+			msm_enable_system_current(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 determine_initial_batt_status(void)
+{
+	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();
+
+	/* 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)
+{
+	wake_lock_destroy(&msm_chg.wl);
+	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:
+	kfree(priv);
+	return rc;
+}
+EXPORT_SYMBOL(msm_charger_register);
+
+void msm_battery_gauge_register(struct msm_battery_gauge *batt_gauge)
+{
+	int rc;
+
+	if (msm_batt_gauge) {
+		msm_batt_gauge = batt_gauge;
+		pr_err("msm-charger %s multiple battery gauge called\n",
+								__func__);
+	} else {
+		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;
+		}
+
+		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);
+	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..b0439bc
--- /dev/null
+++ b/drivers/power/pm8921-bms.c
@@ -0,0 +1,2720 @@
+/* Copyright (c) 2011-2012, 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/power_supply.h>
+#include <linux/mfd/pm8xxx/pm8921-bms.h>
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/mfd/pm8xxx/pm8xxx-adc.h>
+#include <linux/mfd/pm8xxx/ccadc.h>
+#include <linux/interrupt.h>
+#include <linux/bitops.h>
+#include <linux/debugfs.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+
+#define BMS_CONTROL		0x224
+#define BMS_S1_DELAY		0x225
+#define BMS_OUTPUT0		0x230
+#define BMS_OUTPUT1		0x231
+#define BMS_TOLERANCES		0x232
+#define BMS_TEST1		0x237
+
+#define ADC_ARB_SECP_CNTRL	0x190
+#define ADC_ARB_SECP_AMUX_CNTRL	0x191
+#define ADC_ARB_SECP_ANA_PARAM	0x192
+#define ADC_ARB_SECP_DIG_PARAM	0x193
+#define ADC_ARB_SECP_RSV	0x194
+#define ADC_ARB_SECP_DATA1	0x195
+#define ADC_ARB_SECP_DATA0	0x196
+
+#define ADC_ARB_BMS_CNTRL	0x18D
+#define AMUX_TRIM_2		0x322
+#define TEST_PROGRAM_REV	0x339
+
+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_soc_params {
+	uint16_t	last_good_ocv_raw;
+	int		cc;
+
+	int		last_good_ocv_uv;
+};
+
+struct pm8921_rbatt_params {
+	uint16_t	ocv_for_rbatt_raw;
+	uint16_t	vsense_for_rbatt_raw;
+	uint16_t	vbatt_for_rbatt_raw;
+
+	int		ocv_for_rbatt_uv;
+	int		vsense_for_rbatt_uv;
+	int		vbatt_for_rbatt_uv;
+};
+
+/**
+ * struct pm8921_bms_chip -
+ * @bms_output_lock:	lock to prevent concurrent bms reads
+ *
+ * @last_ocv_uv_mutex:	mutex to protect simultaneous invocations of calculate
+ *			state of charge, note that last_ocv_uv could be
+ *			changed as soc is adjusted. This mutex protects
+ *			simultaneous updates of last_ocv_uv as well. This mutex
+ *			also protects changes to *_at_100 variables used in
+ *			faking 100% SOC.
+ */
+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 sf_lut		*pc_sf_lut;
+	struct sf_lut		*rbatt_sf_lut;
+	int			delta_rbatt_mohm;
+	struct work_struct	calib_hkadc_work;
+	struct delayed_work	calib_ccadc_work;
+	unsigned int		calib_delay_ms;
+	unsigned int		revision;
+	unsigned int		xoadc_v0625_usb_present;
+	unsigned int		xoadc_v0625_usb_absent;
+	unsigned int		xoadc_v0625;
+	unsigned int		xoadc_v125;
+	unsigned int		batt_temp_channel;
+	unsigned int		vbat_channel;
+	unsigned int		ref625mv_channel;
+	unsigned int		ref1p25v_channel;
+	unsigned int		batt_id_channel;
+	unsigned int		pmic_bms_irq[PM_BMS_MAX_INTS];
+	DECLARE_BITMAP(enabled_irqs, PM_BMS_MAX_INTS);
+	struct mutex		bms_output_lock;
+	struct single_row_lut	*adjusted_fcc_temp_lut;
+	unsigned int		charging_began;
+	unsigned int		start_percent;
+	unsigned int		end_percent;
+	enum battery_type	batt_type;
+	uint16_t		ocv_reading_at_100;
+	int			cc_reading_at_100;
+	int			max_voltage_uv;
+
+	int			batt_temp_suspend;
+	int			soc_rbatt_suspend;
+	int			default_rbatt_mohm;
+	int			amux_2_trim_delta;
+	uint16_t		prev_last_good_ocv_raw;
+	unsigned int		rconn_mohm;
+	struct mutex		last_ocv_uv_mutex;
+	int			last_ocv_uv;
+	int			last_cc_uah; /* used for Iavg calc for UUC */
+	struct timeval		t;
+	int			last_uuc_uah;
+	int			enable_fcc_learning;
+};
+
+static struct pm8921_bms_chip *the_chip;
+
+#define DEFAULT_RBATT_MOHMS		128
+#define DEFAULT_OCV_MICROVOLTS		3900000
+#define DEFAULT_CHARGE_CYCLES		0
+
+static int last_usb_cal_delta_uv = 1800;
+module_param(last_usb_cal_delta_uv, int, 0644);
+
+static int last_chargecycles = DEFAULT_CHARGE_CYCLES;
+static int last_charge_increase;
+module_param(last_chargecycles, int, 0644);
+module_param(last_charge_increase, int, 0644);
+
+static int last_rbatt = -EINVAL;
+static int last_soc = -EINVAL;
+static int last_real_fcc_mah = -EINVAL;
+static int last_real_fcc_batt_temp = -EINVAL;
+
+static int bms_ops_set(const char *val, const struct kernel_param *kp)
+{
+	if (*(int *)kp->arg == -EINVAL)
+		return param_set_int(val, kp);
+	else
+		return 0;
+}
+
+static struct kernel_param_ops bms_param_ops = {
+	.set = bms_ops_set,
+	.get = param_get_int,
+};
+
+module_param_cb(last_rbatt, &bms_param_ops, &last_rbatt, 0644);
+module_param_cb(last_soc, &bms_param_ops, &last_soc, 0644);
+
+/*
+ * bms_fake_battery is set in setups where a battery emulator is used instead
+ * of a real battery. This makes the bms driver report a different/fake value
+ * regardless of the calculated state of charge.
+ */
+static int bms_fake_battery = -EINVAL;
+module_param(bms_fake_battery, int, 0644);
+
+/* bms_start_XXX and bms_end_XXX are read only */
+static int bms_start_percent;
+static int bms_start_ocv_uv;
+static int bms_start_cc_uah;
+static int bms_end_percent;
+static int bms_end_ocv_uv;
+static int bms_end_cc_uah;
+
+static int bms_ro_ops_set(const char *val, const struct kernel_param *kp)
+{
+	return -EINVAL;
+}
+
+static struct kernel_param_ops bms_ro_param_ops = {
+	.set = bms_ro_ops_set,
+	.get = param_get_int,
+};
+module_param_cb(bms_start_percent, &bms_ro_param_ops, &bms_start_percent, 0644);
+module_param_cb(bms_start_ocv_uv, &bms_ro_param_ops, &bms_start_ocv_uv, 0644);
+module_param_cb(bms_start_cc_uah, &bms_ro_param_ops, &bms_start_cc_uah, 0644);
+
+module_param_cb(bms_end_percent, &bms_ro_param_ops, &bms_end_percent, 0644);
+module_param_cb(bms_end_ocv_uv, &bms_ro_param_ops, &bms_end_ocv_uv, 0644);
+module_param_cb(bms_end_cc_uah, &bms_ro_param_ops, &bms_end_cc_uah, 0644);
+
+static int interpolate_fcc(struct pm8921_bms_chip *chip, int batt_temp);
+static void readjust_fcc_table(void)
+{
+	struct single_row_lut *temp, *old;
+	int i, fcc, ratio;
+
+	if (!the_chip->fcc_temp_lut) {
+		pr_err("The static fcc lut table is NULL\n");
+		return;
+	}
+
+	temp = kzalloc(sizeof(struct single_row_lut), GFP_KERNEL);
+	if (!temp) {
+		pr_err("Cannot allocate memory for adjusted fcc table\n");
+		return;
+	}
+
+	fcc = interpolate_fcc(the_chip, last_real_fcc_batt_temp);
+
+	temp->cols = the_chip->fcc_temp_lut->cols;
+	for (i = 0; i < the_chip->fcc_temp_lut->cols; i++) {
+		temp->x[i] = the_chip->fcc_temp_lut->x[i];
+		ratio = div_u64(the_chip->fcc_temp_lut->y[i] * 1000, fcc);
+		temp->y[i] =  (ratio * last_real_fcc_mah);
+		temp->y[i] /= 1000;
+		pr_debug("temp=%d, staticfcc=%d, adjfcc=%d, ratio=%d\n",
+				temp->x[i], the_chip->fcc_temp_lut->y[i],
+				temp->y[i], ratio);
+	}
+
+	old = the_chip->adjusted_fcc_temp_lut;
+	the_chip->adjusted_fcc_temp_lut = temp;
+	kfree(old);
+}
+
+static int bms_last_real_fcc_set(const char *val,
+				const struct kernel_param *kp)
+{
+	int rc = 0;
+
+	if (last_real_fcc_mah == -EINVAL)
+		rc = param_set_int(val, kp);
+	if (rc) {
+		pr_err("Failed to set last_real_fcc_mah rc=%d\n", rc);
+		return rc;
+	}
+	if (last_real_fcc_batt_temp != -EINVAL)
+		readjust_fcc_table();
+	return rc;
+}
+static struct kernel_param_ops bms_last_real_fcc_param_ops = {
+	.set = bms_last_real_fcc_set,
+	.get = param_get_int,
+};
+module_param_cb(last_real_fcc_mah, &bms_last_real_fcc_param_ops,
+					&last_real_fcc_mah, 0644);
+
+static int bms_last_real_fcc_batt_temp_set(const char *val,
+				const struct kernel_param *kp)
+{
+	int rc = 0;
+
+	if (last_real_fcc_batt_temp == -EINVAL)
+		rc = param_set_int(val, kp);
+	if (rc) {
+		pr_err("Failed to set last_real_fcc_batt_temp rc=%d\n", rc);
+		return rc;
+	}
+	if (last_real_fcc_mah != -EINVAL)
+		readjust_fcc_table();
+	return rc;
+}
+
+static struct kernel_param_ops bms_last_real_fcc_batt_temp_param_ops = {
+	.set = bms_last_real_fcc_batt_temp_set,
+	.get = param_get_int,
+};
+module_param_cb(last_real_fcc_batt_temp, &bms_last_real_fcc_batt_temp_param_ops,
+					&last_real_fcc_batt_temp, 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_enable_irq(struct pm8921_bms_chip *chip, int interrupt)
+{
+	if (!__test_and_set_bit(interrupt, chip->enabled_irqs)) {
+		dev_dbg(chip->dev, "%s %d\n", __func__,
+						chip->pmic_bms_irq[interrupt]);
+		enable_irq(chip->pmic_bms_irq[interrupt]);
+	}
+}
+
+static void pm8921_bms_disable_irq(struct pm8921_bms_chip *chip, int interrupt)
+{
+	if (__test_and_clear_bit(interrupt, chip->enabled_irqs)) {
+		pr_debug("%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;
+}
+
+static int usb_chg_plugged_in(void)
+{
+	union power_supply_propval ret = {0,};
+	static struct power_supply *psy;
+
+	if (psy == NULL) {
+		psy = power_supply_get_by_name("usb");
+		if (psy == NULL)
+			return 0;
+	}
+
+	if (psy->get_property(psy, POWER_SUPPLY_PROP_ONLINE, &ret))
+		return 0;
+
+	return ret.intval;
+}
+
+#define HOLD_OREG_DATA		BIT(1)
+static int pm_bms_lock_output_data(struct pm8921_bms_chip *chip)
+{
+	int rc;
+
+	rc = pm_bms_masked_write(chip, BMS_CONTROL, HOLD_OREG_DATA,
+					HOLD_OREG_DATA);
+	if (rc) {
+		pr_err("couldnt lock bms output rc = %d\n", rc);
+		return rc;
+	}
+	return 0;
+}
+
+static int pm_bms_unlock_output_data(struct pm8921_bms_chip *chip)
+{
+	int rc;
+
+	rc = pm_bms_masked_write(chip, BMS_CONTROL, HOLD_OREG_DATA, 0);
+	if (rc) {
+		pr_err("fail to unlock BMS_CONTROL rc = %d\n", 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	97656
+#define V_PER_BIT_DIV_FACTOR	1000
+#define XOADC_INTRINSIC_OFFSET	0x6000
+static int xoadc_reading_to_microvolt(unsigned int a)
+{
+	if (a <= XOADC_INTRINSIC_OFFSET)
+		return 0;
+
+	return (a - XOADC_INTRINSIC_OFFSET)
+			* V_PER_BIT_MUL_FACTOR / V_PER_BIT_DIV_FACTOR;
+}
+
+#define XOADC_CALIB_UV		625000
+#define VBATT_MUL_FACTOR	3
+static int adjust_xo_vbatt_reading(struct pm8921_bms_chip *chip,
+					int usb_chg, unsigned int uv)
+{
+	s64 numerator, denominator;
+	int local_delta;
+
+	if (uv == 0)
+		return 0;
+
+	/* dont adjust if not calibrated */
+	if (chip->xoadc_v0625 == 0 || chip->xoadc_v125 == 0) {
+		pr_debug("No cal yet return %d\n", VBATT_MUL_FACTOR * uv);
+		return VBATT_MUL_FACTOR * uv;
+	}
+
+	if (usb_chg)
+		local_delta = last_usb_cal_delta_uv;
+	else
+		local_delta = 0;
+
+	pr_debug("using delta = %d\n", local_delta);
+	numerator = ((s64)uv - chip->xoadc_v0625 - local_delta)
+							* XOADC_CALIB_UV;
+	denominator =  (s64)chip->xoadc_v125 - chip->xoadc_v0625 - local_delta;
+	if (denominator == 0)
+		return uv * VBATT_MUL_FACTOR;
+	return (XOADC_CALIB_UV + local_delta + div_s64(numerator, denominator))
+						* VBATT_MUL_FACTOR;
+}
+
+#define CC_RESOLUTION_N_V1	1085069
+#define CC_RESOLUTION_D_V1	100000
+#define CC_RESOLUTION_N_V2	868056
+#define CC_RESOLUTION_D_V2	10000
+static s64 cc_to_microvolt_v1(s64 cc)
+{
+	return div_s64(cc * CC_RESOLUTION_N_V1, CC_RESOLUTION_D_V1);
+}
+
+static s64 cc_to_microvolt_v2(s64 cc)
+{
+	return div_s64(cc * CC_RESOLUTION_N_V2, CC_RESOLUTION_D_V2);
+}
+
+static s64 cc_to_microvolt(struct pm8921_bms_chip *chip, s64 cc)
+{
+	/*
+	 * resolution (the value of a single bit) was changed after revision 2.0
+	 * for more accurate readings
+	 */
+	return (chip->revision < PM8XXX_REVISION_8921_2p0) ?
+				cc_to_microvolt_v1((s64)cc) :
+				cc_to_microvolt_v2((s64)cc);
+}
+
+#define CC_READING_TICKS	55
+#define SLEEP_CLK_HZ		32768
+#define SECONDS_PER_HOUR	3600
+/**
+ * ccmicrovolt_to_nvh -
+ * @cc_uv:  coulumb counter converted to uV
+ *
+ * RETURNS:	coulumb counter based charge in nVh
+ *		(nano Volt Hour)
+ */
+static s64 ccmicrovolt_to_nvh(s64 cc_uv)
+{
+	return div_s64(cc_uv * CC_READING_TICKS * 1000,
+			SLEEP_CLK_HZ * SECONDS_PER_HOUR);
+}
+
+/* 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 adjust_xo_vbatt_reading_for_mbg(struct pm8921_bms_chip *chip,
+						int result)
+{
+	int64_t numerator;
+	int64_t denominator;
+
+	if (chip->amux_2_trim_delta == 0)
+		return result;
+
+	numerator = (s64)result * 1000000;
+	denominator = (1000000 + (410 * (s64)chip->amux_2_trim_delta));
+	return div_s64(numerator, denominator);
+}
+
+static int convert_vbatt_raw_to_uv(struct pm8921_bms_chip *chip,
+					int usb_chg,
+					uint16_t reading, int *result)
+{
+	*result = xoadc_reading_to_microvolt(reading);
+	pr_debug("raw = %04x vbatt = %u\n", reading, *result);
+	*result = adjust_xo_vbatt_reading(chip, usb_chg, *result);
+	pr_debug("after adj vbatt = %u\n", *result);
+	*result = adjust_xo_vbatt_reading_for_mbg(chip, *result);
+	return 0;
+}
+
+static int convert_vsense_to_uv(struct pm8921_bms_chip *chip,
+					int16_t reading, int *result)
+{
+	*result = pm8xxx_ccadc_reading_to_microvolt(chip->revision, reading);
+	pr_debug("raw = %04x vsense = %d\n", reading, *result);
+	*result = pm8xxx_cc_adjust_for_gain(*result);
+	pr_debug("after adj vsense = %d\n", *result);
+	return 0;
+}
+
+static int read_vsense_avg(struct pm8921_bms_chip *chip, int *result)
+{
+	int rc;
+	int16_t reading;
+
+	rc = pm_bms_read_output_data(chip, VSENSE_AVG, &reading);
+	if (rc) {
+		pr_err("fail to read VSENSE_AVG rc = %d\n", rc);
+		return rc;
+	}
+
+	convert_vsense_to_uv(chip, 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_debug("x %d less than known range return y = %d lut = %pS\n",
+							x, lut->y[0], lut);
+		return lut->y[0];
+	}
+	if (x > lut->x[lut->cols - 1]) {
+		pr_debug("x %d more than known range return y = %d lut = %pS\n",
+						x, lut->y[lut->cols - 1], lut);
+		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)
+{
+	/* batt_temp is in tenths of degC - convert it to degC for lookups */
+	batt_temp = batt_temp/10;
+	return interpolate_single_lut(chip->fcc_temp_lut, batt_temp);
+}
+
+static int interpolate_fcc_adjusted(struct pm8921_bms_chip *chip, int batt_temp)
+{
+	/* batt_temp is in tenths of degC - convert it to degC for lookups */
+	batt_temp = batt_temp/10;
+	return interpolate_single_lut(chip->adjusted_fcc_temp_lut, batt_temp);
+}
+
+static int interpolate_scalingfactor_fcc(struct pm8921_bms_chip *chip,
+								int cycles)
+{
+	/*
+	 * sf table could be null when no battery aging data is available, in
+	 * that case return 100%
+	 */
+	if (chip->fcc_sf_lut)
+		return interpolate_single_lut(chip->fcc_sf_lut, cycles);
+	else
+		return 100;
+}
+
+static int interpolate_scalingfactor(struct pm8921_bms_chip *chip,
+				struct sf_lut *sf_lut,
+				int row_entry, int pc)
+{
+	int i, scalefactorrow1, scalefactorrow2, scalefactor;
+	int rows, cols;
+	int row1 = 0;
+	int row2 = 0;
+
+	/*
+	 * sf table could be null when no battery aging data is available, in
+	 * that case return 100%
+	 */
+	if (!sf_lut)
+		return 100;
+
+	rows = sf_lut->rows;
+	cols = sf_lut->cols;
+	if (pc > sf_lut->percent[0]) {
+		pr_debug("pc %d greater than known pc ranges for sfd\n", pc);
+		row1 = 0;
+		row2 = 0;
+	}
+	if (pc < sf_lut->percent[rows - 1]) {
+		pr_debug("pc %d less than known pc ranges for sf", pc);
+		row1 = rows - 1;
+		row2 = rows - 1;
+	}
+	for (i = 0; i < rows; i++) {
+		if (pc == sf_lut->percent[i]) {
+			row1 = i;
+			row2 = i;
+			break;
+		}
+		if (pc > sf_lut->percent[i]) {
+			row1 = i - 1;
+			row2 = i;
+			break;
+		}
+	}
+
+	if (row_entry < sf_lut->row_entries[0])
+		row_entry = sf_lut->row_entries[0];
+	if (row_entry > sf_lut->row_entries[cols - 1])
+		row_entry = sf_lut->row_entries[cols - 1];
+
+	for (i = 0; i < cols; i++)
+		if (row_entry <= sf_lut->row_entries[i])
+			break;
+	if (row_entry == sf_lut->row_entries[i]) {
+		scalefactor = linear_interpolate(
+				sf_lut->sf[row1][i],
+				sf_lut->percent[row1],
+				sf_lut->sf[row2][i],
+				sf_lut->percent[row2],
+				pc);
+		return scalefactor;
+	}
+
+	scalefactorrow1 = linear_interpolate(
+				sf_lut->sf[row1][i - 1],
+				sf_lut->row_entries[i - 1],
+				sf_lut->sf[row1][i],
+				sf_lut->row_entries[i],
+				row_entry);
+
+	scalefactorrow2 = linear_interpolate(
+				sf_lut->sf[row2][i - 1],
+				sf_lut->row_entries[i - 1],
+				sf_lut->sf[row2][i],
+				sf_lut->row_entries[i],
+				row_entry);
+
+	scalefactor = linear_interpolate(
+				scalefactorrow1,
+				sf_lut->percent[row1],
+				scalefactorrow2,
+				sf_lut->percent[row2],
+				pc);
+
+	return scalefactor;
+}
+
+static int is_between(int left, int right, int value)
+{
+	if (left >= right && left >= value && value >= right)
+		return 1;
+	if (left <= right && left <= value && value <= right)
+		return 1;
+
+	return 0;
+}
+
+static int interpolate_pc(struct pm8921_bms_chip *chip,
+				int batt_temp, int ocv)
+{
+	int i, j, pcj, pcj_minus_one, pc;
+	int rows = chip->pc_temp_ocv_lut->rows;
+	int cols = chip->pc_temp_ocv_lut->cols;
+
+	/* batt_temp is in tenths of degC - convert it to degC for lookups */
+	batt_temp = batt_temp/10;
+
+	if (batt_temp < chip->pc_temp_ocv_lut->temp[0]) {
+		pr_debug("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_debug("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;
+			}
+		}
+	}
+
+	/*
+	 * batt_temp is within temperature for
+	 * column j-1 and j
+	 */
+	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];
+
+	pcj_minus_one = 0;
+	pcj = 0;
+	for (i = 0; i < rows-1; i++) {
+		if (pcj == 0
+			&& is_between(chip->pc_temp_ocv_lut->ocv[i][j],
+				chip->pc_temp_ocv_lut->ocv[i+1][j], ocv)) {
+			pcj = 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);
+		}
+
+		if (pcj_minus_one == 0
+			&& is_between(chip->pc_temp_ocv_lut->ocv[i][j-1],
+				chip->pc_temp_ocv_lut->ocv[i+1][j-1], ocv)) {
+
+			pcj_minus_one = linear_interpolate(
+				chip->pc_temp_ocv_lut->percent[i],
+				chip->pc_temp_ocv_lut->ocv[i][j-1],
+				chip->pc_temp_ocv_lut->percent[i + 1],
+				chip->pc_temp_ocv_lut->ocv[i+1][j-1],
+				ocv);
+		}
+
+		if (pcj && pcj_minus_one) {
+			pc = linear_interpolate(
+				pcj_minus_one,
+				chip->pc_temp_ocv_lut->temp[j-1],
+				pcj,
+				chip->pc_temp_ocv_lut->temp[j],
+				batt_temp);
+			return pc;
+		}
+	}
+
+	if (pcj)
+		return pcj;
+
+	if (pcj_minus_one)
+		return pcj_minus_one;
+
+	pr_debug("%d ocv wasn't found for temp %d in the LUT returning 100%%",
+							ocv, batt_temp);
+	return 100;
+}
+
+#define BMS_MODE_BIT	BIT(6)
+#define EN_VBAT_BIT	BIT(5)
+#define OVERRIDE_MODE_DELAY_MS	20
+int pm8921_bms_get_simultaneous_battery_voltage_and_current(int *ibat_ua,
+								int *vbat_uv)
+{
+	int16_t vsense_raw;
+	int16_t vbat_raw;
+	int vsense_uv;
+	int usb_chg;
+
+	if (the_chip == NULL) {
+		pr_err("Called to early\n");
+		return -EINVAL;
+	}
+
+	mutex_lock(&the_chip->bms_output_lock);
+
+	pm8xxx_writeb(the_chip->dev->parent, BMS_S1_DELAY, 0x00);
+	pm_bms_masked_write(the_chip, BMS_CONTROL,
+			BMS_MODE_BIT | EN_VBAT_BIT, BMS_MODE_BIT | EN_VBAT_BIT);
+
+	msleep(OVERRIDE_MODE_DELAY_MS);
+
+	pm_bms_lock_output_data(the_chip);
+	pm_bms_read_output_data(the_chip, VSENSE_AVG, &vsense_raw);
+	pm_bms_read_output_data(the_chip, VBATT_AVG, &vbat_raw);
+	pm_bms_unlock_output_data(the_chip);
+	pm_bms_masked_write(the_chip, BMS_CONTROL,
+			BMS_MODE_BIT | EN_VBAT_BIT, 0);
+
+	pm8xxx_writeb(the_chip->dev->parent, BMS_S1_DELAY, 0x0B);
+
+	mutex_unlock(&the_chip->bms_output_lock);
+
+	usb_chg = usb_chg_plugged_in();
+
+	convert_vbatt_raw_to_uv(the_chip, usb_chg, vbat_raw, vbat_uv);
+	convert_vsense_to_uv(the_chip, vsense_raw, &vsense_uv);
+	*ibat_ua = vsense_uv * 1000 / (int)the_chip->r_sense;
+
+	pr_debug("vsense_raw = 0x%x vbat_raw = 0x%x"
+			" ibat_ua = %d vbat_uv = %d\n",
+			(uint16_t)vsense_raw, (uint16_t)vbat_raw,
+			*ibat_ua, *vbat_uv);
+	return 0;
+}
+EXPORT_SYMBOL(pm8921_bms_get_simultaneous_battery_voltage_and_current);
+
+static int read_rbatt_params_raw(struct pm8921_bms_chip *chip,
+				struct pm8921_rbatt_params *raw)
+{
+	int usb_chg;
+
+	mutex_lock(&chip->bms_output_lock);
+	pm_bms_lock_output_data(chip);
+
+	pm_bms_read_output_data(chip,
+			OCV_FOR_RBATT, &raw->ocv_for_rbatt_raw);
+	pm_bms_read_output_data(chip,
+			VBATT_FOR_RBATT, &raw->vbatt_for_rbatt_raw);
+	pm_bms_read_output_data(chip,
+			VSENSE_FOR_RBATT, &raw->vsense_for_rbatt_raw);
+
+	pm_bms_unlock_output_data(chip);
+	mutex_unlock(&chip->bms_output_lock);
+
+	usb_chg = usb_chg_plugged_in();
+	convert_vbatt_raw_to_uv(chip, usb_chg,
+			raw->vbatt_for_rbatt_raw, &raw->vbatt_for_rbatt_uv);
+	convert_vbatt_raw_to_uv(chip, usb_chg,
+			raw->ocv_for_rbatt_raw, &raw->ocv_for_rbatt_uv);
+	convert_vsense_to_uv(chip, raw->vsense_for_rbatt_raw,
+					&raw->vsense_for_rbatt_uv);
+
+	pr_debug("vbatt_for_rbatt_raw = 0x%x, vbatt_for_rbatt= %duV\n",
+			raw->vbatt_for_rbatt_raw, raw->vbatt_for_rbatt_uv);
+	pr_debug("ocv_for_rbatt_raw = 0x%x, ocv_for_rbatt= %duV\n",
+			raw->ocv_for_rbatt_raw, raw->ocv_for_rbatt_uv);
+	pr_debug("vsense_for_rbatt_raw = 0x%x, vsense_for_rbatt= %duV\n",
+			raw->vsense_for_rbatt_raw, raw->vsense_for_rbatt_uv);
+	return 0;
+}
+
+#define MBG_TRANSIENT_ERROR_RAW 51
+static void adjust_pon_ocv_raw(struct pm8921_bms_chip *chip,
+				struct pm8921_soc_params *raw)
+{
+	/* in 8921 parts the PON ocv is taken when the MBG is not settled.
+	 * decrease the pon ocv by 15mV raw value to account for it
+	 * Since a 1/3rd  of vbatt is supplied to the adc the raw value
+	 * needs to be adjusted by 5mV worth bits
+	 */
+	if (raw->last_good_ocv_raw >= MBG_TRANSIENT_ERROR_RAW)
+		raw->last_good_ocv_raw -= MBG_TRANSIENT_ERROR_RAW;
+}
+
+static int read_soc_params_raw(struct pm8921_bms_chip *chip,
+				struct pm8921_soc_params *raw)
+{
+	int usb_chg;
+
+	mutex_lock(&chip->bms_output_lock);
+	pm_bms_lock_output_data(chip);
+
+	pm_bms_read_output_data(chip,
+			LAST_GOOD_OCV_VALUE, &raw->last_good_ocv_raw);
+	read_cc(chip, &raw->cc);
+
+	pm_bms_unlock_output_data(chip);
+	mutex_unlock(&chip->bms_output_lock);
+
+	usb_chg =  usb_chg_plugged_in();
+
+	if (chip->prev_last_good_ocv_raw == 0) {
+		chip->prev_last_good_ocv_raw = raw->last_good_ocv_raw;
+		adjust_pon_ocv_raw(chip, raw);
+		convert_vbatt_raw_to_uv(chip, usb_chg,
+			raw->last_good_ocv_raw, &raw->last_good_ocv_uv);
+		chip->last_ocv_uv = raw->last_good_ocv_uv;
+	} else if (chip->prev_last_good_ocv_raw != raw->last_good_ocv_raw) {
+		chip->prev_last_good_ocv_raw = raw->last_good_ocv_raw;
+		convert_vbatt_raw_to_uv(chip, usb_chg,
+			raw->last_good_ocv_raw, &raw->last_good_ocv_uv);
+		chip->last_ocv_uv = raw->last_good_ocv_uv;
+		/* forget the old cc value upon ocv */
+		chip->last_cc_uah = 0;
+	} else {
+		raw->last_good_ocv_uv = chip->last_ocv_uv;
+	}
+
+	/* fake a high OCV if we are just done charging */
+	if (chip->ocv_reading_at_100 != raw->last_good_ocv_raw) {
+		chip->ocv_reading_at_100 = 0;
+		chip->cc_reading_at_100 = 0;
+	} else {
+		/*
+		 * force 100% ocv by selecting the highest voltage the
+		 * battery could ever reach
+		 */
+		raw->last_good_ocv_uv = chip->max_voltage_uv;
+		chip->last_ocv_uv = chip->max_voltage_uv;
+	}
+	pr_debug("0p625 = %duV\n", chip->xoadc_v0625);
+	pr_debug("1p25 = %duV\n", chip->xoadc_v125);
+	pr_debug("last_good_ocv_raw= 0x%x, last_good_ocv_uv= %duV\n",
+			raw->last_good_ocv_raw, raw->last_good_ocv_uv);
+	pr_debug("cc_raw= 0x%x\n", raw->cc);
+	return 0;
+}
+
+static int get_rbatt(struct pm8921_bms_chip *chip, int soc_rbatt, int batt_temp)
+{
+	int rbatt, scalefactor;
+
+	rbatt = (last_rbatt < 0) ? chip->default_rbatt_mohm : last_rbatt;
+	pr_debug("rbatt before scaling = %d\n", rbatt);
+	if (chip->rbatt_sf_lut == NULL)  {
+		pr_debug("RBATT = %d\n", rbatt);
+		return rbatt;
+	}
+	/* Convert the batt_temp to DegC from deciDegC */
+	batt_temp = batt_temp / 10;
+	scalefactor = interpolate_scalingfactor(chip, chip->rbatt_sf_lut,
+							batt_temp, soc_rbatt);
+	pr_debug("rbatt sf = %d for batt_temp = %d, soc_rbatt = %d\n",
+				scalefactor, batt_temp, soc_rbatt);
+	rbatt = (rbatt * scalefactor) / 100;
+
+	rbatt += the_chip->rconn_mohm;
+	pr_debug("adding rconn_mohm = %d rbatt = %d\n",
+				the_chip->rconn_mohm, rbatt);
+
+	if (is_between(20, 10, soc_rbatt))
+		rbatt = rbatt
+			+ ((20 - soc_rbatt) * chip->delta_rbatt_mohm) / 10;
+	else
+		if (is_between(10, 0, soc_rbatt))
+			rbatt = rbatt + chip->delta_rbatt_mohm;
+
+	pr_debug("RBATT = %d\n", rbatt);
+	return rbatt;
+}
+
+static int calculate_rbatt_resume(struct pm8921_bms_chip *chip,
+				struct pm8921_rbatt_params *raw)
+{
+	unsigned int  r_batt;
+
+	if (raw->ocv_for_rbatt_uv <= 0
+		|| raw->ocv_for_rbatt_uv <= raw->vbatt_for_rbatt_uv
+		|| raw->vsense_for_rbatt_raw <= 0) {
+		pr_debug("rbatt readings unavailable ocv = %d, vbatt = %d,"
+					"vsen = %d\n",
+					raw->ocv_for_rbatt_uv,
+					raw->vbatt_for_rbatt_uv,
+					raw->vsense_for_rbatt_raw);
+		return -EINVAL;
+	}
+	r_batt = ((raw->ocv_for_rbatt_uv - raw->vbatt_for_rbatt_uv)
+			* chip->r_sense) / raw->vsense_for_rbatt_uv;
+	pr_debug("r_batt = %umilliOhms", r_batt);
+	return r_batt;
+}
+
+static int calculate_fcc_uah(struct pm8921_bms_chip *chip, int batt_temp,
+							int chargecycles)
+{
+	int initfcc, result, scalefactor = 0;
+
+	if (chip->adjusted_fcc_temp_lut == NULL) {
+		initfcc = interpolate_fcc(chip, batt_temp);
+
+		scalefactor = interpolate_scalingfactor_fcc(chip, chargecycles);
+
+		/* Multiply the initial FCC value by the scale factor. */
+		result = (initfcc * scalefactor * 1000) / 100;
+		pr_debug("fcc = %d uAh\n", result);
+		return result;
+	} else {
+		return 1000 * interpolate_fcc_adjusted(chip, batt_temp);
+	}
+}
+
+static int get_battery_uvolts(struct pm8921_bms_chip *chip, int *uvolts)
+{
+	int rc;
+	struct pm8xxx_adc_chan_result result;
+
+	rc = pm8xxx_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);
+	*uvolts = (int)result.physical;
+	return 0;
+}
+
+static int adc_based_ocv(struct pm8921_bms_chip *chip, int *ocv)
+{
+	int vbatt, rbatt, ibatt_ua, rc;
+
+	rc = get_battery_uvolts(chip, &vbatt);
+	if (rc) {
+		pr_err("failed to read vbatt from adc rc = %d\n", rc);
+		return rc;
+	}
+
+	rc =  pm8921_bms_get_battery_current(&ibatt_ua);
+	if (rc) {
+		pr_err("failed to read batt current rc = %d\n", rc);
+		return rc;
+	}
+
+	rbatt = (last_rbatt < 0) ? chip->default_rbatt_mohm : last_rbatt;
+	*ocv = vbatt + (ibatt_ua * rbatt)/1000;
+	return 0;
+}
+
+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(chip,
+					chip->pc_sf_lut, 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;
+}
+
+/**
+ * calculate_cc_uah -
+ * @chip:		the bms chip pointer
+ * @cc:			the cc reading from bms h/w
+ * @val:		return value
+ * @coulumb_counter:	adjusted coulumb counter for 100%
+ *
+ * RETURNS: in val pointer coulumb counter based charger in uAh
+ *          (micro Amp hour)
+ */
+static void calculate_cc_uah(struct pm8921_bms_chip *chip, int cc, int *val)
+{
+	int64_t cc_voltage_uv, cc_nvh, cc_uah;
+
+	cc_voltage_uv = cc;
+	cc_voltage_uv -= chip->cc_reading_at_100;
+	pr_debug("cc = %d. after subtracting 0x%x cc = %lld\n",
+					cc, chip->cc_reading_at_100,
+					cc_voltage_uv);
+	cc_voltage_uv = cc_to_microvolt(chip, cc_voltage_uv);
+	cc_voltage_uv = pm8xxx_cc_adjust_for_gain(cc_voltage_uv);
+	pr_debug("cc_voltage_uv = %lld microvolts\n", cc_voltage_uv);
+	cc_nvh = ccmicrovolt_to_nvh(cc_voltage_uv);
+	pr_debug("cc_nvh = %lld nano_volt_hour\n", cc_nvh);
+	cc_uah = div_s64(cc_nvh, chip->r_sense);
+	*val = cc_uah;
+}
+
+static int calculate_uuc_uah_at_given_current(struct pm8921_bms_chip *chip,
+				 int batt_temp, int chargecycles,
+				int rbatt, int fcc_uah, int i_ma)
+{
+	int unusable_uv, pc_unusable, uuc;
+
+	/* calculate unusable charge with itest */
+	unusable_uv = (rbatt * i_ma) + (chip->v_failure * 1000);
+	pc_unusable = calculate_pc(chip, unusable_uv, batt_temp, chargecycles);
+	uuc = (fcc_uah * pc_unusable) / 100;
+	pr_debug("For i_ma = %d, unusable_uv = %d unusable_pc = %d uuc = %d\n",
+					i_ma, unusable_uv, pc_unusable, uuc);
+	return uuc;
+}
+
+/* soc_rbatt when uuc_reported should be equal to uuc_now */
+#define SOC_RBATT_CHG		80
+#define SOC_RBATT_DISCHG	10
+static int calculate_unusable_charge_uah(struct pm8921_bms_chip *chip,
+				int rbatt, int fcc_uah, int cc_uah,
+				int soc_rbatt, int batt_temp, int chargecycles)
+{
+	struct timeval now;
+	int delta_time_s;
+	int delta_cc_uah;
+	int iavg_ua, iavg_ma;
+	int uuc_uah_itest, uuc_uah_iavg, uuc_now, uuc_reported;
+	s64 stepsize = 0;
+	int firsttime = 0;
+
+	delta_cc_uah = cc_uah - chip->last_cc_uah;
+	do_gettimeofday(&now);
+	if (chip->t.tv_sec != 0) {
+		delta_time_s = (now.tv_sec - chip->t.tv_sec);
+	} else {
+		/* uuc calculation for the first time */
+		delta_time_s = 0;
+		firsttime = 1;
+	}
+
+	if (delta_time_s != 0)
+		iavg_ua = div_s64((s64)delta_cc_uah * 3600, delta_time_s);
+	else
+		iavg_ua = 0;
+
+	iavg_ma = iavg_ua/1000;
+
+	pr_debug("t.tv_sec = %d, now.tv_sec = %d\n", (int)chip->t.tv_sec,
+				(int)now.tv_sec);
+
+	pr_debug("delta_time_s = %d iavg_ma = %d\n", delta_time_s, iavg_ma);
+
+	if (iavg_ma == 0) {
+		pr_debug("Iavg = 0 returning last uuc = %d\n",
+				chip->last_uuc_uah);
+		uuc_reported = chip->last_uuc_uah;
+		goto out;
+	}
+
+	/* calculate unusable charge with itest */
+	uuc_uah_itest = calculate_uuc_uah_at_given_current(chip,
+					batt_temp, chargecycles,
+					rbatt, fcc_uah, chip->i_test);
+
+	pr_debug("itest = %d uuc_itest = %d\n", chip->i_test, uuc_uah_itest);
+
+	/* calculate unusable charge with iavg */
+	iavg_ma = max(0, iavg_ma);
+	uuc_uah_iavg = calculate_uuc_uah_at_given_current(chip,
+					batt_temp, chargecycles,
+					rbatt, fcc_uah, iavg_ma);
+	pr_debug("iavg = %d uuc_iavg = %d\n", iavg_ma, uuc_uah_iavg);
+
+	if (firsttime) {
+		if (cc_uah < chip->last_cc_uah)
+			chip->last_uuc_uah = uuc_uah_itest;
+		else
+			chip->last_uuc_uah = uuc_uah_iavg;
+		pr_debug("firsttime uuc_prev = %d\n", chip->last_uuc_uah);
+	}
+
+	uuc_now = min(uuc_uah_itest, uuc_uah_iavg);
+
+	uuc_reported = -EINVAL;
+	if (cc_uah < chip->last_cc_uah) {
+		/* charging */
+		if (uuc_now < chip->last_uuc_uah) {
+			stepsize = max(1, (SOC_RBATT_CHG - soc_rbatt));
+			/* uuc_reported = uuc_prev + deltauuc / stepsize */
+			uuc_reported = div_s64 (stepsize * chip->last_uuc_uah
+					+ (uuc_now - chip->last_uuc_uah),
+					stepsize);
+			uuc_reported = max(0, uuc_reported);
+		}
+	} else {
+		if (uuc_now > chip->last_uuc_uah) {
+			stepsize = max(1, (soc_rbatt - SOC_RBATT_DISCHG));
+			/* uuc_reported = uuc_prev + deltauuc / stepsize */
+			uuc_reported = div_s64 (stepsize * chip->last_uuc_uah
+					+ (uuc_now - chip->last_uuc_uah),
+					stepsize);
+			uuc_reported = max(0, uuc_reported);
+		}
+	}
+	if (uuc_reported == -EINVAL)
+		uuc_reported = chip->last_uuc_uah;
+
+	pr_debug("uuc_now = %d uuc_prev = %d stepsize = %d uuc_reported = %d\n",
+			uuc_now, chip->last_uuc_uah, (int)stepsize,
+			uuc_reported);
+
+out:
+	/* remember the reported uuc */
+	chip->last_uuc_uah = uuc_reported;
+
+	/* remember cc_uah */
+	chip->last_cc_uah = cc_uah;
+
+	/* remember this time */
+	chip->t = now;
+
+	return uuc_reported;
+}
+
+/* calculate remainging charge at the time of ocv */
+static int calculate_remaining_charge_uah(struct pm8921_bms_chip *chip,
+						struct pm8921_soc_params *raw,
+						int fcc_uah, int batt_temp,
+						int chargecycles)
+{
+	int  ocv, pc;
+
+	ocv = raw->last_good_ocv_uv;
+	pc = calculate_pc(chip, ocv, batt_temp, chargecycles);
+	pr_debug("ocv = %d pc = %d\n", ocv, pc);
+	return (fcc_uah * pc) / 100;
+}
+
+static void calculate_soc_params(struct pm8921_bms_chip *chip,
+						struct pm8921_soc_params *raw,
+						int batt_temp, int chargecycles,
+						int *fcc_uah,
+						int *unusable_charge_uah,
+						int *remaining_charge_uah,
+						int *cc_uah,
+						int *rbatt)
+{
+	int soc_rbatt;
+
+	*fcc_uah = calculate_fcc_uah(chip, batt_temp, chargecycles);
+	pr_debug("FCC = %uuAh batt_temp = %d, cycles = %d\n",
+					*fcc_uah, batt_temp, chargecycles);
+
+
+	/* calculate remainging charge */
+	*remaining_charge_uah = calculate_remaining_charge_uah(chip, raw,
+					*fcc_uah, batt_temp, chargecycles);
+	pr_debug("RC = %uuAh\n", *remaining_charge_uah);
+
+	/* calculate cc micro_volt_hour */
+	calculate_cc_uah(chip, raw->cc, cc_uah);
+	pr_debug("cc_uah = %duAh raw->cc = %x cc = %lld after subtracting %x\n",
+				*cc_uah, raw->cc,
+				(int64_t)raw->cc - chip->cc_reading_at_100,
+				chip->cc_reading_at_100);
+
+	soc_rbatt = ((*remaining_charge_uah - *cc_uah) * 100) / *fcc_uah;
+	if (soc_rbatt < 0)
+		soc_rbatt = 0;
+	*rbatt = get_rbatt(chip, soc_rbatt, batt_temp);
+
+	*unusable_charge_uah = calculate_unusable_charge_uah(chip, *rbatt,
+					*fcc_uah, *cc_uah, soc_rbatt,
+					batt_temp,
+					chargecycles);
+	pr_debug("UUC = %uuAh\n", *unusable_charge_uah);
+}
+
+static int calculate_real_fcc_uah(struct pm8921_bms_chip *chip,
+				struct pm8921_soc_params *raw,
+				int batt_temp, int chargecycles,
+				int *ret_fcc_uah)
+{
+	int fcc_uah, unusable_charge_uah;
+	int remaining_charge_uah;
+	int cc_uah;
+	int real_fcc_uah;
+	int rbatt;
+
+	calculate_soc_params(chip, raw, batt_temp, chargecycles,
+						&fcc_uah,
+						&unusable_charge_uah,
+						&remaining_charge_uah,
+						&cc_uah,
+						&rbatt);
+
+	real_fcc_uah = remaining_charge_uah - cc_uah;
+	*ret_fcc_uah = fcc_uah;
+	pr_debug("real_fcc = %d, RC = %d CC = %d fcc = %d\n",
+			real_fcc_uah, remaining_charge_uah, cc_uah, fcc_uah);
+	return real_fcc_uah;
+}
+
+static int bound_soc(int soc)
+{
+	soc = max(0, soc);
+	soc = min(100, soc);
+	return soc;
+}
+
+static int last_soc_est = -EINVAL;
+static int adjust_soc(struct pm8921_bms_chip *chip, int soc, int batt_temp,
+		int rbatt , int fcc_uah, int uuc_uah, int cc_uah)
+{
+	int ibat_ua = 0, vbat_uv = 0;
+	int ocv_est_uv = 0, soc_est = 0, pc_est = 0, pc = 0;
+	int delta_ocv_uv = 0;
+	int n = 0;
+	int rc_new_uah = 0;
+	int pc_new = 0;
+	int soc_new = 0;
+	int m = 0;
+
+	pm8921_bms_get_simultaneous_battery_voltage_and_current(&ibat_ua,
+		&vbat_uv);
+
+	if (ibat_ua < 0)
+		goto out;
+	ocv_est_uv = vbat_uv + (ibat_ua * rbatt)/1000;
+	pc_est = calculate_pc(chip, ocv_est_uv, batt_temp, last_chargecycles);
+	soc_est = div_s64((s64)fcc_uah * pc_est - uuc_uah*100,
+						(s64)fcc_uah - uuc_uah);
+	soc_est = bound_soc(soc_est);
+
+	/*
+	 * do not adjust if soc_est is between 45 and 25 OR soc_est is
+	 * same as what bms calculated
+	 */
+	if (is_between(45, 25, soc_est) || soc_est == soc)
+		goto out;
+
+	if (last_soc_est == -EINVAL)
+		last_soc_est = soc;
+
+	n = min(200, max(1 , soc + soc_est + last_soc_est));
+	/* remember the last soc_est in last_soc_est */
+	last_soc_est = soc_est;
+
+	pc = calculate_pc(chip, chip->last_ocv_uv,
+				batt_temp, last_chargecycles);
+	if (pc > 0) {
+		pc_new = calculate_pc(chip, chip->last_ocv_uv - (++m * 1000),
+						batt_temp, last_chargecycles);
+		while (pc_new == pc) {
+			/* start taking 10mV steps */
+			m = m + 10;
+			pc_new = calculate_pc(chip,
+						chip->last_ocv_uv - (m * 1000),
+						batt_temp, last_chargecycles);
+		}
+	} else {
+		/*
+		 * pc is already at the lowest point,
+		 * assume 1 millivolt translates to 1% pc
+		 */
+		pc = 1;
+		pc_new = 0;
+		m = 1;
+	}
+
+	delta_ocv_uv = div_s64((soc - soc_est) * (s64)m * 1000,
+							n * (pc - pc_new));
+	chip->last_ocv_uv -= delta_ocv_uv;
+
+	if (chip->last_ocv_uv >= chip->max_voltage_uv)
+		chip->last_ocv_uv = chip->max_voltage_uv;
+
+	/* calculate the soc based on this new ocv */
+	pc_new = calculate_pc(chip, chip->last_ocv_uv,
+						batt_temp, last_chargecycles);
+	rc_new_uah = (fcc_uah * pc_new) / 100;
+	soc_new = (rc_new_uah - cc_uah - uuc_uah)*100 / (fcc_uah - uuc_uah);
+	soc_new = bound_soc(soc_new);
+
+	/*
+	 * if soc_new is ZERO force it higher so that phone doesnt report soc=0
+	 * soc = 0 should happen only when soc_est == 0
+	 */
+	if (soc_new == 0 && soc_est != 0)
+		soc_new = 1;
+
+	soc = soc_new;
+
+out:
+	pr_debug("ibat_ua = %d, vbat_uv = %d, ocv_est_uv = %d, pc_est = %d, "
+		"soc_est = %d, n = %d, delta_ocv_uv = %d, last_ocv_uv = %d, "
+		"pc_new = %d, soc_new = %d\n",
+		ibat_ua, vbat_uv, ocv_est_uv, pc_est,
+		soc_est, n, delta_ocv_uv, chip->last_ocv_uv,
+		pc_new, soc_new);
+
+	return soc;
+}
+
+/*
+ * Remaining Usable Charge = remaining_charge (charge at ocv instance)
+ *				- coloumb counter charge
+ *				- unusable charge (due to battery resistance)
+ * SOC% = (remaining usable charge/ fcc - usable_charge);
+ */
+static int calculate_state_of_charge(struct pm8921_bms_chip *chip,
+					struct pm8921_soc_params *raw,
+					int batt_temp, int chargecycles)
+{
+	int remaining_usable_charge_uah, fcc_uah, unusable_charge_uah;
+	int remaining_charge_uah, soc;
+	int cc_uah;
+	int rbatt;
+
+	calculate_soc_params(chip, raw, batt_temp, chargecycles,
+						&fcc_uah,
+						&unusable_charge_uah,
+						&remaining_charge_uah,
+						&cc_uah,
+						&rbatt);
+
+	/* calculate remaining usable charge */
+	remaining_usable_charge_uah = remaining_charge_uah
+					- cc_uah
+					- unusable_charge_uah;
+
+	pr_debug("RUC = %duAh\n", remaining_usable_charge_uah);
+	if (fcc_uah - unusable_charge_uah <= 0) {
+		pr_warn("FCC = %duAh, UUC = %duAh forcing soc = 0\n",
+						fcc_uah, unusable_charge_uah);
+		soc = 0;
+	} else {
+		soc = (remaining_usable_charge_uah * 100)
+			/ (fcc_uah - unusable_charge_uah);
+	}
+
+	if (soc > 100)
+		soc = 100;
+	pr_debug("SOC = %u%%\n", soc);
+
+	if (bms_fake_battery != -EINVAL) {
+		pr_debug("Returning Fake SOC = %d%%\n", bms_fake_battery);
+		return bms_fake_battery;
+	}
+
+	if (soc < 0) {
+		pr_err("bad rem_usb_chg = %d rem_chg %d,"
+				"cc_uah %d, unusb_chg %d\n",
+				remaining_usable_charge_uah,
+				remaining_charge_uah,
+				cc_uah, unusable_charge_uah);
+
+		pr_err("for bad rem_usb_chg last_ocv_uv = %d"
+				"chargecycles = %d, batt_temp = %d"
+				"fcc = %d soc =%d\n",
+				chip->last_ocv_uv, chargecycles, batt_temp,
+				fcc_uah, soc);
+		soc = 0;
+	}
+
+	soc = adjust_soc(chip, soc, batt_temp, rbatt,
+					fcc_uah, unusable_charge_uah, cc_uah);
+
+	if (last_soc == -EINVAL || soc <= last_soc) {
+		last_soc = soc;
+	} else {
+		/*
+		 * soc > last_soc
+		 * the device must be charging for reporting a higher soc, if
+		 * not ignore this soc and continue reporting the last_soc
+		 */
+		if (the_chip->start_percent != -EINVAL)
+			last_soc = soc;
+		else
+			pr_debug("soc = %d reporting last_soc = %d\n", soc,
+								last_soc);
+	}
+
+	pr_debug("Reported SOC = %u%%\n", last_soc);
+	return last_soc;
+}
+#define MIN_DELTA_625_UV	1000
+static void calib_hkadc(struct pm8921_bms_chip *chip)
+{
+	int voltage, rc;
+	struct pm8xxx_adc_chan_result result;
+	int usb_chg;
+	int this_delta;
+
+	rc = pm8xxx_adc_read(the_chip->ref1p25v_channel, &result);
+	if (rc) {
+		pr_err("ADC failed for 1.25volts rc = %d\n", rc);
+		return;
+	}
+	voltage = xoadc_reading_to_microvolt(result.adc_code);
+
+	pr_debug("result 1.25v = 0x%x, voltage = %duV adc_meas = %lld\n",
+				result.adc_code, voltage, result.measurement);
+
+	chip->xoadc_v125 = voltage;
+
+	rc = pm8xxx_adc_read(the_chip->ref625mv_channel, &result);
+	if (rc) {
+		pr_err("ADC failed for 1.25volts rc = %d\n", rc);
+		return;
+	}
+	voltage = xoadc_reading_to_microvolt(result.adc_code);
+
+	usb_chg = usb_chg_plugged_in();
+	pr_debug("result 0.625V = 0x%x, voltage = %duV adc_meas = %lld "
+				"usb_chg = %d\n",
+				result.adc_code, voltage, result.measurement,
+				usb_chg);
+
+	if (usb_chg)
+		chip->xoadc_v0625_usb_present = voltage;
+	else
+		chip->xoadc_v0625_usb_absent = voltage;
+
+	chip->xoadc_v0625 = voltage;
+	if (chip->xoadc_v0625_usb_present && chip->xoadc_v0625_usb_absent) {
+		this_delta = chip->xoadc_v0625_usb_present
+						- chip->xoadc_v0625_usb_absent;
+		pr_debug("this_delta= %duV\n", this_delta);
+		if (this_delta > MIN_DELTA_625_UV)
+			last_usb_cal_delta_uv = this_delta;
+		pr_debug("625V_present= %d, 625V_absent= %d, delta = %duV\n",
+			chip->xoadc_v0625_usb_present,
+			chip->xoadc_v0625_usb_absent,
+			last_usb_cal_delta_uv);
+	}
+}
+
+static void calibrate_hkadc_work(struct work_struct *work)
+{
+	struct pm8921_bms_chip *chip = container_of(work,
+				struct pm8921_bms_chip, calib_hkadc_work);
+
+	calib_hkadc(chip);
+}
+
+void pm8921_bms_calibrate_hkadc(void)
+{
+	schedule_work(&the_chip->calib_hkadc_work);
+}
+
+static void calibrate_ccadc_work(struct work_struct *work)
+{
+	struct pm8921_bms_chip *chip = container_of(work,
+				struct pm8921_bms_chip, calib_ccadc_work.work);
+
+	pm8xxx_calib_ccadc();
+	calib_hkadc(chip);
+	schedule_delayed_work(&chip->calib_ccadc_work,
+			round_jiffies_relative(msecs_to_jiffies
+			(chip->calib_delay_ms)));
+}
+
+int pm8921_bms_get_vsense_avg(int *result)
+{
+	int rc = -EINVAL;
+
+	if (the_chip) {
+		mutex_lock(&the_chip->bms_output_lock);
+		pm_bms_lock_output_data(the_chip);
+		rc = read_vsense_avg(the_chip, result);
+		pm_bms_unlock_output_data(the_chip);
+		mutex_unlock(&the_chip->bms_output_lock);
+	}
+
+	pr_err("called before initialization\n");
+	return rc;
+}
+EXPORT_SYMBOL(pm8921_bms_get_vsense_avg);
+
+int pm8921_bms_get_battery_current(int *result_ua)
+{
+	int vsense;
+
+	if (!the_chip) {
+		pr_err("called before initialization\n");
+		return -EINVAL;
+	}
+	if (the_chip->r_sense == 0) {
+		pr_err("r_sense is zero\n");
+		return -EINVAL;
+	}
+
+	mutex_lock(&the_chip->bms_output_lock);
+	pm_bms_lock_output_data(the_chip);
+	read_vsense_avg(the_chip, &vsense);
+	pm_bms_unlock_output_data(the_chip);
+	mutex_unlock(&the_chip->bms_output_lock);
+	pr_debug("vsense=%duV\n", vsense);
+	/* cast for signed division */
+	*result_ua = vsense * 1000 / (int)the_chip->r_sense;
+	pr_debug("ibat=%duA\n", *result_ua);
+	return 0;
+}
+EXPORT_SYMBOL(pm8921_bms_get_battery_current);
+
+int pm8921_bms_get_percent_charge(void)
+{
+	int batt_temp, rc;
+	struct pm8xxx_adc_chan_result result;
+	struct pm8921_soc_params raw;
+	int soc;
+
+	if (!the_chip) {
+		pr_err("called before initialization\n");
+		return -EINVAL;
+	}
+
+	rc = pm8xxx_adc_read(the_chip->batt_temp_channel, &result);
+	if (rc) {
+		pr_err("error reading adc channel = %d, rc = %d\n",
+					the_chip->batt_temp_channel, rc);
+		return rc;
+	}
+	pr_debug("batt_temp phy = %lld meas = 0x%llx", result.physical,
+						result.measurement);
+	batt_temp = (int)result.physical;
+
+	mutex_lock(&the_chip->last_ocv_uv_mutex);
+	read_soc_params_raw(the_chip, &raw);
+
+	soc = calculate_state_of_charge(the_chip, &raw,
+					batt_temp, last_chargecycles);
+	mutex_unlock(&the_chip->last_ocv_uv_mutex);
+	return soc;
+}
+EXPORT_SYMBOL_GPL(pm8921_bms_get_percent_charge);
+
+int pm8921_bms_get_rbatt(void)
+{
+	int batt_temp, rc;
+	struct pm8xxx_adc_chan_result result;
+	struct pm8921_soc_params raw;
+	int fcc_uah;
+	int unusable_charge_uah;
+	int remaining_charge_uah;
+	int cc_uah;
+	int rbatt;
+
+	if (!the_chip) {
+		pr_err("called before initialization\n");
+		return -EINVAL;
+	}
+
+	rc = pm8xxx_adc_read(the_chip->batt_temp_channel, &result);
+	if (rc) {
+		pr_err("error reading adc channel = %d, rc = %d\n",
+					the_chip->batt_temp_channel, rc);
+		return rc;
+	}
+	pr_debug("batt_temp phy = %lld meas = 0x%llx\n", result.physical,
+						result.measurement);
+	batt_temp = (int)result.physical;
+
+	mutex_lock(&the_chip->last_ocv_uv_mutex);
+
+	read_soc_params_raw(the_chip, &raw);
+
+	calculate_soc_params(the_chip, &raw, batt_temp, last_chargecycles,
+						&fcc_uah,
+						&unusable_charge_uah,
+						&remaining_charge_uah,
+						&cc_uah,
+						&rbatt);
+	mutex_unlock(&the_chip->last_ocv_uv_mutex);
+
+	return rbatt;
+}
+EXPORT_SYMBOL_GPL(pm8921_bms_get_rbatt);
+
+int pm8921_bms_get_fcc(void)
+{
+	int batt_temp, rc;
+	struct pm8xxx_adc_chan_result result;
+
+	if (!the_chip) {
+		pr_err("called before initialization\n");
+		return -EINVAL;
+	}
+
+	rc = pm8xxx_adc_read(the_chip->batt_temp_channel, &result);
+	if (rc) {
+		pr_err("error reading adc channel = %d, rc = %d\n",
+					the_chip->batt_temp_channel, rc);
+		return rc;
+	}
+	pr_debug("batt_temp phy = %lld meas = 0x%llx", result.physical,
+						result.measurement);
+	batt_temp = (int)result.physical;
+	return calculate_fcc_uah(the_chip, batt_temp, last_chargecycles);
+}
+EXPORT_SYMBOL_GPL(pm8921_bms_get_fcc);
+
+#define IBAT_TOL_MASK		0x0F
+#define OCV_TOL_MASK			0xF0
+#define IBAT_TOL_DEFAULT	0x03
+#define IBAT_TOL_NOCHG		0x0F
+#define OCV_TOL_DEFAULT		0x20
+#define OCV_TOL_NO_OCV		0x00
+void pm8921_bms_charging_began(void)
+{
+	int batt_temp, rc;
+	struct pm8xxx_adc_chan_result result;
+	struct pm8921_soc_params raw;
+
+	rc = pm8xxx_adc_read(the_chip->batt_temp_channel, &result);
+	if (rc) {
+		pr_err("error reading adc channel = %d, rc = %d\n",
+				the_chip->batt_temp_channel, rc);
+		return;
+	}
+	pr_debug("batt_temp phy = %lld meas = 0x%llx\n", result.physical,
+						result.measurement);
+	batt_temp = (int)result.physical;
+
+	mutex_lock(&the_chip->last_ocv_uv_mutex);
+	read_soc_params_raw(the_chip, &raw);
+
+	the_chip->start_percent = calculate_state_of_charge(the_chip, &raw,
+					batt_temp, last_chargecycles);
+	mutex_unlock(&the_chip->last_ocv_uv_mutex);
+
+	bms_start_percent = the_chip->start_percent;
+	bms_start_ocv_uv = raw.last_good_ocv_uv;
+	calculate_cc_uah(the_chip, raw.cc, &bms_start_cc_uah);
+	pm_bms_masked_write(the_chip, BMS_TOLERANCES,
+			IBAT_TOL_MASK, IBAT_TOL_DEFAULT);
+	pr_debug("start_percent = %u%%\n", the_chip->start_percent);
+}
+EXPORT_SYMBOL_GPL(pm8921_bms_charging_began);
+
+#define DELTA_FCC_PERCENT	3
+#define MIN_START_PERCENT_FOR_LEARNING	30
+void pm8921_bms_charging_end(int is_battery_full)
+{
+	int batt_temp, rc;
+	struct pm8xxx_adc_chan_result result;
+	struct pm8921_soc_params raw;
+
+	if (the_chip == NULL)
+		return;
+
+	rc = pm8xxx_adc_read(the_chip->batt_temp_channel, &result);
+	if (rc) {
+		pr_err("error reading adc channel = %d, rc = %d\n",
+				the_chip->batt_temp_channel, rc);
+		return;
+	}
+	pr_debug("batt_temp phy = %lld meas = 0x%llx\n", result.physical,
+						result.measurement);
+	batt_temp = (int)result.physical;
+
+	mutex_lock(&the_chip->last_ocv_uv_mutex);
+
+	read_soc_params_raw(the_chip, &raw);
+
+	calculate_cc_uah(the_chip, raw.cc, &bms_end_cc_uah);
+
+	bms_end_ocv_uv = raw.last_good_ocv_uv;
+
+	if (is_battery_full && the_chip->enable_fcc_learning
+		&& the_chip->start_percent <= MIN_START_PERCENT_FOR_LEARNING) {
+		int fcc_uah, new_fcc_uah, delta_fcc_uah;
+
+		new_fcc_uah = calculate_real_fcc_uah(the_chip, &raw,
+						batt_temp, last_chargecycles,
+						&fcc_uah);
+		delta_fcc_uah = new_fcc_uah - fcc_uah;
+		if (delta_fcc_uah < 0)
+			delta_fcc_uah = -delta_fcc_uah;
+
+		if (delta_fcc_uah * 100  > (DELTA_FCC_PERCENT * fcc_uah)) {
+			/* new_fcc_uah is outside the scope limit it */
+			if (new_fcc_uah > fcc_uah)
+				new_fcc_uah
+				= (fcc_uah +
+					(DELTA_FCC_PERCENT * fcc_uah) / 100);
+			else
+				new_fcc_uah
+				= (fcc_uah -
+					(DELTA_FCC_PERCENT * fcc_uah) / 100);
+
+			pr_debug("delta_fcc=%d > %d percent of fcc=%d"
+					"restring it to %d\n",
+					delta_fcc_uah, DELTA_FCC_PERCENT,
+					fcc_uah, new_fcc_uah);
+		}
+
+		last_real_fcc_mah = new_fcc_uah/1000;
+		last_real_fcc_batt_temp = batt_temp;
+		readjust_fcc_table();
+
+	}
+
+	if (is_battery_full) {
+		the_chip->ocv_reading_at_100 = raw.last_good_ocv_raw;
+		the_chip->cc_reading_at_100 = raw.cc;
+
+		the_chip->last_ocv_uv = the_chip->max_voltage_uv;
+		raw.last_good_ocv_uv = the_chip->max_voltage_uv;
+		/*
+		 * since we are treating this as an ocv event
+		 * forget the old cc value
+		 */
+		the_chip->last_cc_uah = 0;
+		pr_debug("EOC ocv_reading = 0x%x cc = 0x%x\n",
+				the_chip->ocv_reading_at_100,
+				the_chip->cc_reading_at_100);
+	}
+
+	the_chip->end_percent = calculate_state_of_charge(the_chip, &raw,
+					batt_temp, last_chargecycles);
+	mutex_unlock(&the_chip->last_ocv_uv_mutex);
+
+	bms_end_percent = the_chip->end_percent;
+
+	if (the_chip->end_percent > the_chip->start_percent) {
+		last_charge_increase +=
+			the_chip->end_percent - the_chip->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",
+			the_chip->end_percent,
+			last_charge_increase,
+			last_chargecycles);
+	the_chip->start_percent = -EINVAL;
+	the_chip->end_percent = -EINVAL;
+	pm_bms_masked_write(the_chip, BMS_TOLERANCES,
+				IBAT_TOL_MASK, IBAT_TOL_NOCHG);
+}
+EXPORT_SYMBOL_GPL(pm8921_bms_charging_end);
+
+int pm8921_bms_stop_ocv_updates(struct pm8921_bms_chip *chip)
+{
+	pr_debug("stopping ocv updates\n");
+	return pm_bms_masked_write(chip, BMS_TOLERANCES,
+			OCV_TOL_MASK, OCV_TOL_NO_OCV);
+}
+EXPORT_SYMBOL_GPL(pm8921_bms_stop_ocv_updates);
+
+int pm8921_bms_start_ocv_updates(struct pm8921_bms_chip *chip)
+{
+	pr_debug("stopping ocv updates\n");
+	return pm_bms_masked_write(chip, BMS_TOLERANCES,
+			OCV_TOL_MASK, OCV_TOL_DEFAULT);
+}
+EXPORT_SYMBOL_GPL(pm8921_bms_start_ocv_updates);
+
+static irqreturn_t pm8921_bms_sbi_write_ok_handler(int irq, void *data)
+{
+	pr_debug("irq = %d triggered", irq);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t pm8921_bms_cc_thr_handler(int irq, void *data)
+{
+	pr_debug("irq = %d triggered", irq);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t pm8921_bms_vsense_thr_handler(int irq, void *data)
+{
+	pr_debug("irq = %d triggered", irq);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t pm8921_bms_vsense_for_r_handler(int irq, void *data)
+{
+	pr_debug("irq = %d triggered", irq);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t pm8921_bms_ocv_for_r_handler(int irq, void *data)
+{
+	struct pm8921_bms_chip *chip = data;
+
+	pr_debug("irq = %d triggered", irq);
+	schedule_work(&chip->calib_hkadc_work);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t pm8921_bms_good_ocv_handler(int irq, void *data)
+{
+	struct pm8921_bms_chip *chip = data;
+
+	pr_debug("irq = %d triggered", irq);
+	schedule_work(&chip->calib_hkadc_work);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t pm8921_bms_vsense_avg_handler(int irq, void *data)
+{
+	pr_debug("irq = %d triggered", irq);
+	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;
+}
+
+static int pm8921_bms_suspend(struct device *dev)
+{
+	int rc;
+	struct pm8xxx_adc_chan_result result;
+	struct pm8921_bms_chip *chip = dev_get_drvdata(dev);
+	struct pm8921_soc_params raw;
+	int fcc_uah;
+	int remaining_charge_uah;
+	int cc_uah;
+
+	chip->batt_temp_suspend = 0;
+	rc = pm8xxx_adc_read(chip->batt_temp_channel, &result);
+	if (rc) {
+		pr_err("error reading adc channel = %d, rc = %d\n",
+					chip->batt_temp_channel, rc);
+	}
+	chip->batt_temp_suspend = (int)result.physical;
+
+	mutex_lock(&chip->last_ocv_uv_mutex);
+	read_soc_params_raw(chip, &raw);
+
+	fcc_uah = calculate_fcc_uah(chip,
+			chip->batt_temp_suspend, last_chargecycles);
+	pr_debug("FCC = %uuAh batt_temp = %d, cycles = %d\n",
+			fcc_uah, chip->batt_temp_suspend, last_chargecycles);
+	/* calculate remainging charge */
+	remaining_charge_uah = calculate_remaining_charge_uah(chip, &raw,
+					fcc_uah, chip->batt_temp_suspend,
+					last_chargecycles);
+	pr_debug("RC = %uuAh\n", remaining_charge_uah);
+
+	/* calculate cc micro_volt_hour */
+	calculate_cc_uah(chip, raw.cc, &cc_uah);
+	pr_debug("cc_uah = %duAh raw->cc = %x cc = %lld after subtracting %x\n",
+				cc_uah, raw.cc,
+				(int64_t)raw.cc - chip->cc_reading_at_100,
+				chip->cc_reading_at_100);
+	chip->soc_rbatt_suspend = ((remaining_charge_uah - cc_uah) * 100)
+						/ fcc_uah;
+	mutex_unlock(&chip->last_ocv_uv_mutex);
+
+	return 0;
+}
+
+#define DELTA_RBATT_PERCENT	10
+static int pm8921_bms_resume(struct device *dev)
+{
+	struct pm8921_rbatt_params raw;
+	struct pm8921_bms_chip *chip = dev_get_drvdata(dev);
+	int rbatt;
+	int expected_rbatt;
+	int scalefactor;
+	int delta_rbatt;
+
+	read_rbatt_params_raw(chip, &raw);
+	rbatt = calculate_rbatt_resume(chip, &raw);
+
+	if (rbatt < 0)
+		return 0;
+
+	expected_rbatt
+		= (last_rbatt < 0) ? chip->default_rbatt_mohm : last_rbatt;
+
+	if (chip->rbatt_sf_lut) {
+		scalefactor = interpolate_scalingfactor(chip,
+						chip->rbatt_sf_lut,
+						chip->batt_temp_suspend / 10,
+						chip->soc_rbatt_suspend);
+		rbatt = rbatt * 100 / scalefactor;
+	}
+
+	delta_rbatt = expected_rbatt - rbatt;
+	if (delta_rbatt)
+		delta_rbatt = -delta_rbatt;
+	/*
+	 * only update last_rbatt if rbatt is within some
+	 * percent of expected_rbatt
+	 */
+	if (delta_rbatt * 100 <= DELTA_RBATT_PERCENT * expected_rbatt)
+		last_rbatt = rbatt;
+
+	return 0;
+}
+
+static const struct dev_pm_ops pm8921_pm_ops = {
+	.suspend	= pm8921_bms_suspend,
+	.resume		= pm8921_bms_resume,
+};
+#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);
+	}
+
+	/* The charger will call start charge later if usb is present */
+	pm_bms_masked_write(chip, BMS_TOLERANCES,
+				IBAT_TOL_MASK, IBAT_TOL_NOCHG);
+	return 0;
+}
+
+static void check_initial_ocv(struct pm8921_bms_chip *chip)
+{
+	int ocv_uv, rc;
+	int16_t ocv_raw;
+	int usb_chg;
+
+	/*
+	 * Check if a ocv is available in bms hw,
+	 * if not compute it here at boot time and save it
+	 * in the last_ocv_uv.
+	 */
+	ocv_uv = 0;
+	pm_bms_read_output_data(chip, LAST_GOOD_OCV_VALUE, &ocv_raw);
+	usb_chg = usb_chg_plugged_in();
+	rc = convert_vbatt_raw_to_uv(chip, usb_chg, ocv_raw, &ocv_uv);
+	if (rc || ocv_uv == 0) {
+		rc = adc_based_ocv(chip, &ocv_uv);
+		if (rc) {
+			pr_err("failed to read adc based ocv_uv rc = %d\n", rc);
+			ocv_uv = DEFAULT_OCV_MICROVOLTS;
+		}
+	}
+	chip->last_ocv_uv = ocv_uv;
+	pr_debug("ocv_uv = %d last_ocv_uv = %d\n", ocv_uv, chip->last_ocv_uv);
+}
+
+static int64_t read_battery_id(struct pm8921_bms_chip *chip)
+{
+	int rc;
+	struct pm8xxx_adc_chan_result result;
+
+	rc = pm8xxx_adc_read(chip->batt_id_channel, &result);
+	if (rc) {
+		pr_err("error reading batt id channel = %d, rc = %d\n",
+					chip->vbat_channel, rc);
+		return rc;
+	}
+	pr_debug("batt_id phy = %lld meas = 0x%llx\n", result.physical,
+						result.measurement);
+	return result.adc_code;
+}
+
+#define PALLADIUM_ID_MIN	0x7F40
+#define PALLADIUM_ID_MAX	0x7F5A
+#define DESAY_5200_ID_MIN	0x7F7F
+#define DESAY_5200_ID_MAX	0x802F
+static int set_battery_data(struct pm8921_bms_chip *chip)
+{
+	int64_t battery_id;
+
+	if (chip->batt_type == BATT_DESAY)
+		goto desay;
+	else if (chip->batt_type == BATT_PALLADIUM)
+		goto palladium;
+
+	battery_id = read_battery_id(chip);
+	if (battery_id < 0) {
+		pr_err("cannot read battery id err = %lld\n", battery_id);
+		return battery_id;
+	}
+
+	if (is_between(PALLADIUM_ID_MIN, PALLADIUM_ID_MAX, battery_id)) {
+		goto palladium;
+	} else if (is_between(DESAY_5200_ID_MIN, DESAY_5200_ID_MAX,
+				battery_id)) {
+		goto desay;
+	} else {
+		pr_warn("invalid battid, palladium 1500 assumed batt_id %llx\n",
+				battery_id);
+		goto palladium;
+	}
+
+palladium:
+		chip->fcc = palladium_1500_data.fcc;
+		chip->fcc_temp_lut = palladium_1500_data.fcc_temp_lut;
+		chip->fcc_sf_lut = palladium_1500_data.fcc_sf_lut;
+		chip->pc_temp_ocv_lut = palladium_1500_data.pc_temp_ocv_lut;
+		chip->pc_sf_lut = palladium_1500_data.pc_sf_lut;
+		chip->rbatt_sf_lut = palladium_1500_data.rbatt_sf_lut;
+		chip->default_rbatt_mohm
+				= palladium_1500_data.default_rbatt_mohm;
+		chip->delta_rbatt_mohm = palladium_1500_data.delta_rbatt_mohm;
+		return 0;
+desay:
+		chip->fcc = desay_5200_data.fcc;
+		chip->fcc_temp_lut = desay_5200_data.fcc_temp_lut;
+		chip->pc_temp_ocv_lut = desay_5200_data.pc_temp_ocv_lut;
+		chip->pc_sf_lut = desay_5200_data.pc_sf_lut;
+		chip->rbatt_sf_lut = desay_5200_data.rbatt_sf_lut;
+		chip->default_rbatt_mohm = desay_5200_data.default_rbatt_mohm;
+		chip->delta_rbatt_mohm = desay_5200_data.delta_rbatt_mohm;
+		return 0;
+}
+
+enum bms_request_operation {
+	CALC_RBATT,
+	CALC_FCC,
+	CALC_PC,
+	CALC_SOC,
+	CALIB_HKADC,
+	CALIB_CCADC,
+	GET_VBAT_VSENSE_SIMULTANEOUS,
+	STOP_OCV,
+	START_OCV,
+};
+
+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;
+	int ibat_ua, vbat_uv;
+	struct pm8921_soc_params raw;
+	struct pm8921_rbatt_params rraw;
+
+	read_soc_params_raw(the_chip, &raw);
+	read_rbatt_params_raw(the_chip, &rraw);
+
+	*val = 0;
+
+	/* global irq number passed in via data */
+	switch (param) {
+	case CALC_RBATT:
+		*val = calculate_rbatt_resume(the_chip, &rraw);
+		break;
+	case CALC_FCC:
+		*val = calculate_fcc_uah(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, &raw,
+					test_batt_temp, test_chargecycle);
+		break;
+	case CALIB_HKADC:
+		/* reading this will trigger calibration */
+		*val = 0;
+		calib_hkadc(the_chip);
+		break;
+	case CALIB_CCADC:
+		/* reading this will trigger calibration */
+		*val = 0;
+		pm8xxx_calib_ccadc();
+		break;
+	case GET_VBAT_VSENSE_SIMULTANEOUS:
+		/* reading this will call simultaneous vbat and vsense */
+		*val =
+		pm8921_bms_get_simultaneous_battery_voltage_and_current(
+			&ibat_ua,
+			&vbat_uv);
+	default:
+		ret = -EINVAL;
+	}
+	return ret;
+}
+
+static int set_calc(void *data, u64 val)
+{
+	int param = (int)data;
+	int ret = 0;
+
+	switch (param) {
+	case STOP_OCV:
+		pm8921_bms_stop_ocv_updates(the_chip);
+		break;
+	case START_OCV:
+		pm8921_bms_start_ocv_updates(the_chip);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+	return ret;
+}
+DEFINE_SIMPLE_ATTRIBUTE(calc_fops, get_calc, set_calc, "%llu\n");
+
+static int get_reading(void *data, u64 * val)
+{
+	int param = (int)data;
+	int ret = 0;
+	struct pm8921_soc_params raw;
+	struct pm8921_rbatt_params rraw;
+
+	read_soc_params_raw(the_chip, &raw);
+	read_rbatt_params_raw(the_chip, &rraw);
+
+	*val = 0;
+
+	switch (param) {
+	case CC_MSB:
+	case CC_LSB:
+		*val = raw.cc;
+		break;
+	case LAST_GOOD_OCV_VALUE:
+		*val = raw.last_good_ocv_uv;
+		break;
+	case VBATT_FOR_RBATT:
+		*val = rraw.vbatt_for_rbatt_uv;
+		break;
+	case VSENSE_FOR_RBATT:
+		*val = rraw.vsense_for_rbatt_uv;
+		break;
+	case OCV_FOR_RBATT:
+		*val = rraw.ocv_for_rbatt_uv;
+		break;
+	case VSENSE_AVG:
+		read_vsense_avg(the_chip, (uint *)val);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+	return ret;
+}
+DEFINE_SIMPLE_ATTRIBUTE(reading_fops, get_reading, NULL, "%lld\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("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("read_vsense_avg", 0644, chip->dent,
+				(void *)VSENSE_AVG, &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);
+	debugfs_create_file("calib_hkadc", 0644, chip->dent,
+				(void *)CALIB_HKADC, &calc_fops);
+	debugfs_create_file("calib_ccadc", 0644, chip->dent,
+				(void *)CALIB_CCADC, &calc_fops);
+	debugfs_create_file("stop_ocv", 0644, chip->dent,
+				(void *)STOP_OCV, &calc_fops);
+	debugfs_create_file("start_ocv", 0644, chip->dent,
+				(void *)START_OCV, &calc_fops);
+
+	debugfs_create_file("simultaneous", 0644, chip->dent,
+			(void *)GET_VBAT_VSENSE_SIMULTANEOUS, &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);
+	}
+}
+
+#define REG_SBI_CONFIG		0x04F
+#define PAGE3_ENABLE_MASK	0x6
+#define PROGRAM_REV_MASK	0x0F
+#define PROGRAM_REV		0x9
+static int read_ocv_trim(struct pm8921_bms_chip *chip)
+{
+	int rc;
+	u8 reg, sbi_config;
+
+	rc = pm8xxx_readb(chip->dev->parent, REG_SBI_CONFIG, &sbi_config);
+	if (rc) {
+		pr_err("error = %d reading sbi config reg\n", rc);
+		return rc;
+	}
+
+	reg = sbi_config | PAGE3_ENABLE_MASK;
+	rc = pm8xxx_writeb(chip->dev->parent, REG_SBI_CONFIG, reg);
+	if (rc) {
+		pr_err("error = %d writing sbi config reg\n", rc);
+		return rc;
+	}
+
+	rc = pm8xxx_readb(chip->dev->parent, TEST_PROGRAM_REV, &reg);
+	if (rc)
+		pr_err("Error %d reading %d addr %d\n",
+			rc, reg, TEST_PROGRAM_REV);
+	pr_err("program rev reg is 0x%x\n", reg);
+	reg &= PROGRAM_REV_MASK;
+
+	/* If the revision is equal or higher do not adjust trim delta */
+	if (reg >= PROGRAM_REV) {
+		chip->amux_2_trim_delta = 0;
+		goto restore_sbi_config;
+	}
+
+	rc = pm8xxx_readb(chip->dev->parent, AMUX_TRIM_2, &reg);
+	if (rc) {
+		pr_err("error = %d reading trim reg\n", rc);
+		return rc;
+	}
+
+	pr_err("trim reg is 0x%x\n", reg);
+	chip->amux_2_trim_delta = abs(0x49 - reg);
+	pr_err("trim delta is %d\n", chip->amux_2_trim_delta);
+
+restore_sbi_config:
+	rc = pm8xxx_writeb(chip->dev->parent, REG_SBI_CONFIG, sbi_config);
+	if (rc) {
+		pr_err("error = %d writing sbi config reg\n", rc);
+		return rc;
+	}
+
+	return 0;
+}
+
+static int __devinit pm8921_bms_probe(struct platform_device *pdev)
+{
+	int rc = 0;
+	int vbatt;
+	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;
+	}
+
+	mutex_init(&chip->bms_output_lock);
+	mutex_init(&chip->last_ocv_uv_mutex);
+	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->max_voltage_uv = pdata->max_voltage_uv;
+	chip->batt_type = pdata->battery_type;
+	chip->rconn_mohm = pdata->rconn_mohm;
+	chip->start_percent = -EINVAL;
+	chip->end_percent = -EINVAL;
+	rc = set_battery_data(chip);
+	if (rc) {
+		pr_err("%s bad battery data %d\n", __func__, rc);
+		goto free_chip;
+	}
+
+	if (chip->pc_temp_ocv_lut == NULL) {
+		pr_err("temp ocv lut table is NULL\n");
+		rc = -EINVAL;
+		goto free_chip;
+	}
+
+	/* set defaults in the battery data */
+	if (chip->default_rbatt_mohm <= 0)
+		chip->default_rbatt_mohm = DEFAULT_RBATT_MOHMS;
+
+	chip->batt_temp_channel = pdata->bms_cdata.batt_temp_channel;
+	chip->vbat_channel = pdata->bms_cdata.vbat_channel;
+	chip->ref625mv_channel = pdata->bms_cdata.ref625mv_channel;
+	chip->ref1p25v_channel = pdata->bms_cdata.ref1p25v_channel;
+	chip->batt_id_channel = pdata->bms_cdata.batt_id_channel;
+	chip->revision = pm8xxx_get_revision(chip->dev->parent);
+	chip->enable_fcc_learning = pdata->enable_fcc_learning;
+	INIT_WORK(&chip->calib_hkadc_work, calibrate_hkadc_work);
+
+	rc = request_irqs(chip, pdev);
+	if (rc) {
+		pr_err("couldn't register interrupts rc = %d\n", rc);
+		goto free_chip;
+	}
+
+	rc = pm8921_bms_hw_init(chip);
+	if (rc) {
+		pr_err("couldn't init hardware rc = %d\n", rc);
+		goto free_irqs;
+	}
+
+	platform_set_drvdata(pdev, chip);
+	the_chip = chip;
+	create_debugfs_entries(chip);
+
+	rc = read_ocv_trim(chip);
+	if (rc) {
+		pr_err("couldn't adjust ocv_trim rc= %d\n", rc);
+		goto free_irqs;
+	}
+	check_initial_ocv(chip);
+
+	INIT_DELAYED_WORK(&chip->calib_ccadc_work, calibrate_ccadc_work);
+	/* begin calibration only on chips > 2.0 */
+	if (chip->revision >= PM8XXX_REVISION_8921_2p0)
+		schedule_delayed_work(&chip->calib_ccadc_work, 0);
+
+	/* initial hkadc calibration */
+	schedule_work(&chip->calib_hkadc_work);
+	/* enable the vbatt reading interrupts for scheduling hkadc calib */
+	pm8921_bms_enable_irq(chip, PM8921_BMS_GOOD_OCV);
+	pm8921_bms_enable_irq(chip, PM8921_BMS_OCV_FOR_R);
+
+	get_battery_uvolts(chip, &vbatt);
+	pr_info("OK battery_capacity_at_boot=%d volt = %d ocv = %d\n",
+				pm8921_bms_get_percent_charge(),
+				vbatt, chip->last_ocv_uv);
+	return 0;
+
+free_irqs:
+	free_irqs(chip);
+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);
+	kfree(chip->adjusted_fcc_temp_lut);
+	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,
+		.pm	= &pm8921_pm_ops
+	},
+};
+
+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..c983389
--- /dev/null
+++ b/drivers/power/pm8921-charger.c
@@ -0,0 +1,3973 @@
+/* Copyright (c) 2011-2012, 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/pm8xxx/pm8xxx-adc.h>
+#include <linux/mfd/pm8xxx/ccadc.h>
+#include <linux/mfd/pm8xxx/core.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>
+
+#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
+#define IUSB_FINE_RES		0x2B6
+
+/* check EOC every 10 seconds */
+#define EOC_CHECK_PERIOD_MS	10000
+/* check for USB unplug every 200 msecs */
+#define UNPLUG_CHECK_WAIT_PERIOD_MS 200
+
+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,
+};
+
+struct fsm_state_to_batt_status {
+	enum chg_fsm_state	fsm_state;
+	int			batt_state;
+};
+
+static struct fsm_state_to_batt_status map[] = {
+	{FSM_STATE_OFF_0, POWER_SUPPLY_STATUS_UNKNOWN},
+	{FSM_STATE_BATFETDET_START_12, POWER_SUPPLY_STATUS_UNKNOWN},
+	{FSM_STATE_BATFETDET_END_16, POWER_SUPPLY_STATUS_UNKNOWN},
+	/*
+	 * for CHG_HIGHI_1 report NOT_CHARGING if battery missing,
+	 * too hot/cold, charger too hot
+	 */
+	{FSM_STATE_ON_CHG_HIGHI_1, POWER_SUPPLY_STATUS_FULL},
+	{FSM_STATE_ATC_2A, POWER_SUPPLY_STATUS_CHARGING},
+	{FSM_STATE_ATC_2B, POWER_SUPPLY_STATUS_CHARGING},
+	{FSM_STATE_ON_BAT_3, POWER_SUPPLY_STATUS_DISCHARGING},
+	{FSM_STATE_ATC_FAIL_4, POWER_SUPPLY_STATUS_DISCHARGING},
+	{FSM_STATE_DELAY_5, POWER_SUPPLY_STATUS_UNKNOWN },
+	{FSM_STATE_ON_CHG_AND_BAT_6, POWER_SUPPLY_STATUS_CHARGING},
+	{FSM_STATE_FAST_CHG_7, POWER_SUPPLY_STATUS_CHARGING},
+	{FSM_STATE_TRKL_CHG_8, POWER_SUPPLY_STATUS_CHARGING},
+	{FSM_STATE_CHG_FAIL_9, POWER_SUPPLY_STATUS_DISCHARGING},
+	{FSM_STATE_EOC_10, POWER_SUPPLY_STATUS_FULL},
+	{FSM_STATE_ON_CHG_VREGOK_11, POWER_SUPPLY_STATUS_NOT_CHARGING},
+	{FSM_STATE_ATC_PAUSE_13, POWER_SUPPLY_STATUS_NOT_CHARGING},
+	{FSM_STATE_FAST_CHG_PAUSE_14, POWER_SUPPLY_STATUS_NOT_CHARGING},
+	{FSM_STATE_TRKL_CHG_PAUSE_15, POWER_SUPPLY_STATUS_NOT_CHARGING},
+	{FSM_STATE_START_BOOT, POWER_SUPPLY_STATUS_NOT_CHARGING},
+	{FSM_STATE_FLCB_VREGOK, POWER_SUPPLY_STATUS_NOT_CHARGING},
+	{FSM_STATE_FLCB, POWER_SUPPLY_STATUS_NOT_CHARGING},
+};
+
+enum chg_regulation_loop {
+	VDD_LOOP = BIT(3),
+	BAT_CURRENT_LOOP = BIT(2),
+	INPUT_CURRENT_LOOP = BIT(1),
+	INPUT_VOLTAGE_LOOP = BIT(0),
+	CHG_ALL_LOOPS = VDD_LOOP | BAT_CURRENT_LOOP
+			| INPUT_CURRENT_LOOP | INPUT_VOLTAGE_LOOP,
+};
+
+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_battery_full;
+	int			is_charging;
+	struct	work_struct	work;
+};
+
+/**
+ * struct pm8921_chg_chip -device information
+ * @dev:			device pointer to access the parent
+ * @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_mv:		the max volts the batt should be charged up to
+ * @min_voltage_mv:		the min battery voltage before turning the FETon
+ * @cool_temp_dc:		the cool temp threshold in deciCelcius
+ * @warm_temp_dc:		the warm temp threshold in deciCelcius
+ * @resume_voltage_delta:	the voltage delta from vdd max 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			max_bat_chg_current;
+	unsigned int			pmic_chg_irq[PM_CHG_MAX_INTS];
+	unsigned int			safety_time;
+	unsigned int			ttrkl_time;
+	unsigned int			update_time;
+	unsigned int			max_voltage_mv;
+	unsigned int			min_voltage_mv;
+	int				cool_temp_dc;
+	int				warm_temp_dc;
+	unsigned int			temp_check_period;
+	unsigned int			cool_bat_chg_current;
+	unsigned int			warm_bat_chg_current;
+	unsigned int			cool_bat_voltage;
+	unsigned int			warm_bat_voltage;
+	unsigned int			is_bat_cool;
+	unsigned int			is_bat_warm;
+	unsigned int			resume_voltage_delta;
+	unsigned int			term_current;
+	unsigned int			vbat_channel;
+	unsigned int			batt_temp_channel;
+	unsigned int			batt_id_channel;
+	struct power_supply		usb_psy;
+	struct power_supply		dc_psy;
+	struct power_supply		*ext_psy;
+	struct power_supply		batt_psy;
+	struct dentry			*dent;
+	struct bms_notify		bms_notify;
+	bool				keep_btm_on_suspend;
+	bool				ext_charging;
+	bool				ext_charge_done;
+	bool				iusb_fine_res;
+	DECLARE_BITMAP(enabled_irqs, PM_CHG_MAX_INTS);
+	struct work_struct		battery_id_valid_work;
+	int64_t				batt_id_min;
+	int64_t				batt_id_max;
+	int				trkl_voltage;
+	int				weak_voltage;
+	int				trkl_current;
+	int				weak_current;
+	int				vin_min;
+	unsigned int			*thermal_mitigation;
+	int				thermal_levels;
+	struct delayed_work		update_heartbeat_work;
+	struct delayed_work		eoc_work;
+	struct delayed_work		unplug_check_work;
+	struct delayed_work		vin_collapse_check_work;
+	struct wake_lock		eoc_wake_lock;
+	enum pm8921_chg_cold_thr	cold_thr;
+	enum pm8921_chg_hot_thr		hot_thr;
+	int				rconn_mohm;
+	enum pm8921_chg_led_src_config	led_src_config;
+};
+
+/* user space parameter to limit usb current */
+static unsigned int usb_max_current;
+/*
+ * usb_target_ma is used for wall charger
+ * adaptive input current limiting only. Use
+ * pm_iusbmax_get() to get current maximum usb current setting.
+ */
+static int usb_target_ma;
+static int charging_disabled;
+static int thermal_mitigation;
+
+static struct pm8921_chg_chip *the_chip;
+
+static struct pm8xxx_adc_arb_btm_param btm_config;
+
+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;
+}
+
+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)
+{
+	return pm_chg_get_rt_status(chip, USBIN_VALID_IRQ);
+}
+
+/* Treat OverVoltage/UnderVoltage as source missing */
+static int is_dc_chg_plugged_in(struct pm8921_chg_chip *chip)
+{
+	return pm_chg_get_rt_status(chip, DCIN_VALID_IRQ);
+}
+
+#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 READ_BANK_6		0x60
+static int pm_chg_get_regulation_loop(struct pm8921_chg_chip *chip)
+{
+	u8 temp;
+	int err;
+
+	temp = READ_BANK_6;
+	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;
+	}
+
+	/* return the lower 4 bits */
+	return temp & CHG_ALL_LOOPS;
+}
+
+#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_FAILED_CLEAR	BIT(0)
+#define ATC_FAILED_CLEAR	BIT(1)
+static int pm_chg_failed_clear(struct pm8921_chg_chip *chip, int clear)
+{
+	int rc;
+
+	rc = pm_chg_masked_write(chip, CHG_CNTRL_3, ATC_FAILED_CLEAR,
+				clear ? ATC_FAILED_CLEAR : 0);
+	rc |= pm_chg_masked_write(chip, CHG_CNTRL_3, CHG_FAILED_CLEAR,
+				clear ? CHG_FAILED_CLEAR : 0);
+	return rc;
+}
+
+#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);
+}
+
+static int pm_is_chg_charge_dis(struct pm8921_chg_chip *chip)
+{
+	u8 temp;
+
+	pm8xxx_readb(chip->dev->parent, CHG_CNTRL, &temp);
+	return  temp & CHG_CHARGE_DIS_BIT;
+}
+#define PM8921_CHG_V_MIN_MV	3240
+#define PM8921_CHG_V_STEP_MV	20
+#define PM8921_CHG_V_STEP_10MV_OFFSET_BIT	BIT(7)
+#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)
+{
+	int remainder;
+	u8 temp = 0;
+
+	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;
+
+	remainder = voltage % 20;
+	if (remainder >= 10) {
+		temp |= PM8921_CHG_V_STEP_10MV_OFFSET_BIT;
+	}
+
+	pr_debug("voltage=%d setting %02x\n", voltage, temp);
+	return pm8xxx_writeb(chip->dev->parent, CHG_VDD_MAX, temp);
+}
+
+static int pm_chg_vddmax_get(struct pm8921_chg_chip *chip, int *voltage)
+{
+	u8 temp;
+	int rc;
+
+	rc = pm8xxx_readb(chip->dev->parent, CHG_VDD_MAX, &temp);
+	if (rc) {
+		pr_err("rc = %d while reading vdd max\n", rc);
+		*voltage = 0;
+		return rc;
+	}
+	*voltage = (int)(temp & PM8921_CHG_V_MASK) * PM8921_CHG_V_STEP_MV
+							+ PM8921_CHG_V_MIN_MV;
+	if (temp & PM8921_CHG_V_STEP_10MV_OFFSET_BIT)
+		*voltage =  *voltage + 10;
+	return 0;
+}
+
+static int pm_chg_vddmax_set(struct pm8921_chg_chip *chip, int voltage)
+{
+	int current_mv, ret, steps, i;
+	bool increase;
+
+	ret = 0;
+
+	if (voltage < PM8921_CHG_VDDMAX_MIN
+		|| voltage > PM8921_CHG_VDDMAX_MAX) {
+		pr_err("bad mV=%d asked to set\n", voltage);
+		return -EINVAL;
+	}
+
+	ret = pm_chg_vddmax_get(chip, &current_mv);
+	if (ret) {
+		pr_err("Failed to read vddmax rc=%d\n", ret);
+		return -EINVAL;
+	}
+	if (current_mv == voltage)
+		return 0;
+
+	/* Only change in increments when USB is present */
+	if (is_usb_chg_plugged_in(chip)) {
+		if (current_mv < voltage) {
+			steps = (voltage - current_mv) / PM8921_CHG_V_STEP_MV;
+			increase = true;
+		} else {
+			steps = (current_mv - voltage) / PM8921_CHG_V_STEP_MV;
+			increase = false;
+		}
+		for (i = 0; i < steps; i++) {
+			if (increase)
+				current_mv += PM8921_CHG_V_STEP_MV;
+			else
+				current_mv -= PM8921_CHG_V_STEP_MV;
+			ret |= __pm_chg_vddmax_set(chip, current_mv);
+		}
+	}
+	ret |= __pm_chg_vddmax_set(chip, voltage);
+	return ret;
+}
+
+#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_VINMIN_MIN_MV	3800
+#define PM8921_CHG_VINMIN_STEP_MV	100
+#define PM8921_CHG_VINMIN_USABLE_MAX	6500
+#define PM8921_CHG_VINMIN_USABLE_MIN	4300
+#define PM8921_CHG_VINMIN_MASK		0x1F
+static int pm_chg_vinmin_set(struct pm8921_chg_chip *chip, int voltage)
+{
+	u8 temp;
+
+	if (voltage < PM8921_CHG_VINMIN_USABLE_MIN
+			|| voltage > PM8921_CHG_VINMIN_USABLE_MAX) {
+		pr_err("bad mV=%d asked to set\n", voltage);
+		return -EINVAL;
+	}
+	temp = (voltage - PM8921_CHG_VINMIN_MIN_MV) / PM8921_CHG_VINMIN_STEP_MV;
+	pr_debug("voltage=%d setting %02x\n", voltage, temp);
+	return pm_chg_masked_write(chip, CHG_VIN_MIN, PM8921_CHG_VINMIN_MASK,
+									temp);
+}
+
+static int pm_chg_vinmin_get(struct pm8921_chg_chip *chip)
+{
+	u8 temp;
+	int rc, voltage_mv;
+
+	rc = pm8xxx_readb(chip->dev->parent, CHG_VIN_MIN, &temp);
+	temp &= PM8921_CHG_VINMIN_MASK;
+
+	voltage_mv = PM8921_CHG_VINMIN_MIN_MV +
+			(int)temp * PM8921_CHG_VINMIN_STEP_MV;
+
+	return voltage_mv;
+}
+
+#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_MA		50
+#define PM8921_CHG_ITERM_MAX_MA		200
+#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_MA
+			|| chg_current > PM8921_CHG_ITERM_MAX_MA) {
+		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_ITERM, PM8921_CHG_ITERM_MASK,
+					 temp);
+}
+
+static int pm_chg_iterm_get(struct pm8921_chg_chip *chip, int *chg_current)
+{
+	u8 temp;
+	int rc;
+
+	rc = pm8xxx_readb(chip->dev->parent, CHG_ITERM, &temp);
+	if (rc) {
+		pr_err("err=%d reading CHG_ITEM\n", rc);
+		*chg_current = 0;
+		return rc;
+	}
+	temp &= PM8921_CHG_ITERM_MASK;
+	*chg_current = (int)temp * PM8921_CHG_ITERM_STEP_MA
+					+ PM8921_CHG_ITERM_MIN_MA;
+	return 0;
+}
+
+struct usb_ma_limit_entry {
+	int	usb_ma;
+	u8	value;
+};
+
+static struct usb_ma_limit_entry usb_ma_table[] = {
+	{100, 0x0},
+	{200, 0x1},
+	{500, 0x2},
+	{600, 0x3},
+	{700, 0x4},
+	{800, 0x5},
+	{850, 0x6},
+	{900, 0x8},
+	{950, 0x7},
+	{1000, 0x9},
+	{1100, 0xA},
+	{1200, 0xB},
+	{1300, 0xC},
+	{1400, 0xD},
+	{1500, 0xE},
+	{1600, 0xF},
+};
+
+#define PM8921_CHG_IUSB_MASK 0x1C
+#define PM8921_CHG_IUSB_SHIFT 2
+#define PM8921_CHG_IUSB_MAX  7
+#define PM8921_CHG_IUSB_MIN  0
+#define PM8917_IUSB_FINE_RES BIT(0)
+static int pm_chg_iusbmax_set(struct pm8921_chg_chip *chip, int reg_val)
+{
+	u8 temp, fineres;
+	int rc;
+
+	fineres = PM8917_IUSB_FINE_RES & usb_ma_table[reg_val].value;
+	reg_val = usb_ma_table[reg_val].value >> 1;
+
+	if (reg_val < PM8921_CHG_IUSB_MIN || reg_val > PM8921_CHG_IUSB_MAX) {
+		pr_err("bad mA=%d asked to set\n", reg_val);
+		return -EINVAL;
+	}
+	temp = reg_val << PM8921_CHG_IUSB_SHIFT;
+
+	/* IUSB_FINE_RES */
+	if (chip->iusb_fine_res) {
+		/* Clear IUSB_FINE_RES bit to avoid overshoot */
+		rc = pm_chg_masked_write(chip, IUSB_FINE_RES,
+			PM8917_IUSB_FINE_RES, 0);
+
+		rc |= pm_chg_masked_write(chip, PBL_ACCESS2,
+			PM8921_CHG_IUSB_MASK, temp);
+
+		if (rc) {
+			pr_err("Failed to write PBL_ACCESS2 rc=%d\n", rc);
+			return rc;
+		}
+
+		if (fineres) {
+			rc = pm_chg_masked_write(chip, IUSB_FINE_RES,
+				PM8917_IUSB_FINE_RES, fineres);
+			if (rc)
+				pr_err("Failed to write ISUB_FINE_RES rc=%d\n",
+					rc);
+		}
+	} else {
+		rc = pm_chg_masked_write(chip, PBL_ACCESS2,
+			PM8921_CHG_IUSB_MASK, temp);
+		if (rc)
+			pr_err("Failed to write PBL_ACCESS2 rc=%d\n", rc);
+	}
+
+	return rc;
+}
+
+static int pm_chg_iusbmax_get(struct pm8921_chg_chip *chip, int *mA)
+{
+	u8 temp, fineres;
+	int rc, i;
+
+	fineres = 0;
+	*mA = 0;
+	rc = pm8xxx_readb(chip->dev->parent, PBL_ACCESS2, &temp);
+	if (rc) {
+		pr_err("err=%d reading PBL_ACCESS2\n", rc);
+		return rc;
+	}
+
+	if (chip->iusb_fine_res) {
+		rc = pm8xxx_readb(chip->dev->parent, IUSB_FINE_RES, &fineres);
+		if (rc) {
+			pr_err("err=%d reading IUSB_FINE_RES\n", rc);
+			return rc;
+		}
+	}
+	temp &= PM8921_CHG_IUSB_MASK;
+	temp = temp >> PM8921_CHG_IUSB_SHIFT;
+
+	temp = (temp << 1) | (fineres & PM8917_IUSB_FINE_RES);
+	for (i = ARRAY_SIZE(usb_ma_table) - 1; i >= 0; i--) {
+		if (usb_ma_table[i].value == temp)
+			break;
+	}
+
+	*mA = usb_ma_table[i].usb_ma;
+
+	return rc;
+}
+
+#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);
+}
+
+#define PM8921_CHG_TCHG_MASK	0x7F
+#define PM8921_CHG_TCHG_MIN	4
+#define PM8921_CHG_TCHG_MAX	512
+#define PM8921_CHG_TCHG_STEP	4
+static int pm_chg_tchg_max_set(struct pm8921_chg_chip *chip, int minutes)
+{
+	u8 temp;
+
+	if (minutes < PM8921_CHG_TCHG_MIN || minutes > PM8921_CHG_TCHG_MAX) {
+		pr_err("bad max minutes =%d asked to set\n", minutes);
+		return -EINVAL;
+	}
+
+	temp = (minutes - 1)/PM8921_CHG_TCHG_STEP;
+	return pm_chg_masked_write(chip, CHG_TCHG_MAX, PM8921_CHG_TCHG_MASK,
+					 temp);
+}
+
+#define PM8921_CHG_TTRKL_MASK	0x1F
+#define PM8921_CHG_TTRKL_MIN	1
+#define PM8921_CHG_TTRKL_MAX	64
+static int pm_chg_ttrkl_max_set(struct pm8921_chg_chip *chip, int minutes)
+{
+	u8 temp;
+
+	if (minutes < PM8921_CHG_TTRKL_MIN || minutes > PM8921_CHG_TTRKL_MAX) {
+		pr_err("bad max minutes =%d asked to set\n", minutes);
+		return -EINVAL;
+	}
+
+	temp = minutes - 1;
+	return pm_chg_masked_write(chip, CHG_TTRKL_MAX, PM8921_CHG_TTRKL_MASK,
+					 temp);
+}
+
+#define PM8921_CHG_VTRKL_MIN_MV		2050
+#define PM8921_CHG_VTRKL_MAX_MV		2800
+#define PM8921_CHG_VTRKL_STEP_MV	50
+#define PM8921_CHG_VTRKL_SHIFT		4
+#define PM8921_CHG_VTRKL_MASK		0xF0
+static int pm_chg_vtrkl_low_set(struct pm8921_chg_chip *chip, int millivolts)
+{
+	u8 temp;
+
+	if (millivolts < PM8921_CHG_VTRKL_MIN_MV
+			|| millivolts > PM8921_CHG_VTRKL_MAX_MV) {
+		pr_err("bad voltage = %dmV asked to set\n", millivolts);
+		return -EINVAL;
+	}
+
+	temp = (millivolts - PM8921_CHG_VTRKL_MIN_MV)/PM8921_CHG_VTRKL_STEP_MV;
+	temp = temp << PM8921_CHG_VTRKL_SHIFT;
+	return pm_chg_masked_write(chip, CHG_VTRICKLE, PM8921_CHG_VTRKL_MASK,
+					 temp);
+}
+
+#define PM8921_CHG_VWEAK_MIN_MV		2100
+#define PM8921_CHG_VWEAK_MAX_MV		3600
+#define PM8921_CHG_VWEAK_STEP_MV	100
+#define PM8921_CHG_VWEAK_MASK		0x0F
+static int pm_chg_vweak_set(struct pm8921_chg_chip *chip, int millivolts)
+{
+	u8 temp;
+
+	if (millivolts < PM8921_CHG_VWEAK_MIN_MV
+			|| millivolts > PM8921_CHG_VWEAK_MAX_MV) {
+		pr_err("bad voltage = %dmV asked to set\n", millivolts);
+		return -EINVAL;
+	}
+
+	temp = (millivolts - PM8921_CHG_VWEAK_MIN_MV)/PM8921_CHG_VWEAK_STEP_MV;
+	return pm_chg_masked_write(chip, CHG_VTRICKLE, PM8921_CHG_VWEAK_MASK,
+					 temp);
+}
+
+#define PM8921_CHG_ITRKL_MIN_MA		50
+#define PM8921_CHG_ITRKL_MAX_MA		200
+#define PM8921_CHG_ITRKL_MASK		0x0F
+#define PM8921_CHG_ITRKL_STEP_MA	10
+static int pm_chg_itrkl_set(struct pm8921_chg_chip *chip, int milliamps)
+{
+	u8 temp;
+
+	if (milliamps < PM8921_CHG_ITRKL_MIN_MA
+		|| milliamps > PM8921_CHG_ITRKL_MAX_MA) {
+		pr_err("bad current = %dmA asked to set\n", milliamps);
+		return -EINVAL;
+	}
+
+	temp = (milliamps - PM8921_CHG_ITRKL_MIN_MA)/PM8921_CHG_ITRKL_STEP_MA;
+
+	return pm_chg_masked_write(chip, CHG_ITRICKLE, PM8921_CHG_ITRKL_MASK,
+					 temp);
+}
+
+#define PM8921_CHG_IWEAK_MIN_MA		325
+#define PM8921_CHG_IWEAK_MAX_MA		525
+#define PM8921_CHG_IWEAK_SHIFT		7
+#define PM8921_CHG_IWEAK_MASK		0x80
+static int pm_chg_iweak_set(struct pm8921_chg_chip *chip, int milliamps)
+{
+	u8 temp;
+
+	if (milliamps < PM8921_CHG_IWEAK_MIN_MA
+		|| milliamps > PM8921_CHG_IWEAK_MAX_MA) {
+		pr_err("bad current = %dmA asked to set\n", milliamps);
+		return -EINVAL;
+	}
+
+	if (milliamps < PM8921_CHG_IWEAK_MAX_MA)
+		temp = 0;
+	else
+		temp = 1;
+
+	temp = temp << PM8921_CHG_IWEAK_SHIFT;
+	return pm_chg_masked_write(chip, CHG_ITRICKLE, PM8921_CHG_IWEAK_MASK,
+					 temp);
+}
+
+#define PM8921_CHG_BATT_TEMP_THR_COLD	BIT(1)
+#define PM8921_CHG_BATT_TEMP_THR_COLD_SHIFT	1
+static int pm_chg_batt_cold_temp_config(struct pm8921_chg_chip *chip,
+					enum pm8921_chg_cold_thr cold_thr)
+{
+	u8 temp;
+
+	temp = cold_thr << PM8921_CHG_BATT_TEMP_THR_COLD_SHIFT;
+	temp = temp & PM8921_CHG_BATT_TEMP_THR_COLD;
+	return pm_chg_masked_write(chip, CHG_CNTRL_2,
+					PM8921_CHG_BATT_TEMP_THR_COLD,
+					 temp);
+}
+
+#define PM8921_CHG_BATT_TEMP_THR_HOT		BIT(0)
+#define PM8921_CHG_BATT_TEMP_THR_HOT_SHIFT	0
+static int pm_chg_batt_hot_temp_config(struct pm8921_chg_chip *chip,
+					enum pm8921_chg_hot_thr hot_thr)
+{
+	u8 temp;
+
+	temp = hot_thr << PM8921_CHG_BATT_TEMP_THR_HOT_SHIFT;
+	temp = temp & PM8921_CHG_BATT_TEMP_THR_HOT;
+	return pm_chg_masked_write(chip, CHG_CNTRL_2,
+					PM8921_CHG_BATT_TEMP_THR_HOT,
+					 temp);
+}
+
+#define PM8921_CHG_LED_SRC_CONFIG_SHIFT	4
+#define PM8921_CHG_LED_SRC_CONFIG_MASK	0x30
+static int pm_chg_led_src_config(struct pm8921_chg_chip *chip,
+				enum pm8921_chg_led_src_config led_src_config)
+{
+	u8 temp;
+
+	if (led_src_config < LED_SRC_GND ||
+			led_src_config > LED_SRC_BYPASS)
+		return -EINVAL;
+
+	if (led_src_config == LED_SRC_BYPASS)
+		return 0;
+
+	temp = led_src_config << PM8921_CHG_LED_SRC_CONFIG_SHIFT;
+
+	return pm_chg_masked_write(chip, CHG_CNTRL_3,
+					PM8921_CHG_LED_SRC_CONFIG_MASK, temp);
+}
+
+static void disable_input_voltage_regulation(struct pm8921_chg_chip *chip)
+{
+	u8 temp;
+	int rc;
+
+	rc = pm8xxx_writeb(chip->dev->parent, CHG_BUCK_CTRL_TEST3, 0x70);
+	if (rc) {
+		pr_err("Failed to write 0x70 to CTRL_TEST3 rc = %d\n", rc);
+		return;
+	}
+	rc = pm8xxx_readb(chip->dev->parent, CHG_BUCK_CTRL_TEST3, &temp);
+	if (rc) {
+		pr_err("Failed to read CTRL_TEST3 rc = %d\n", rc);
+		return;
+	}
+	/* set the input voltage disable bit and the write bit */
+	temp |= 0x81;
+	rc = pm8xxx_writeb(chip->dev->parent, CHG_BUCK_CTRL_TEST3, temp);
+	if (rc) {
+		pr_err("Failed to write 0x%x to CTRL_TEST3 rc=%d\n", temp, rc);
+		return;
+	}
+}
+
+static void enable_input_voltage_regulation(struct pm8921_chg_chip *chip)
+{
+	u8 temp;
+	int rc;
+
+	rc = pm8xxx_writeb(chip->dev->parent, CHG_BUCK_CTRL_TEST3, 0x70);
+	if (rc) {
+		pr_err("Failed to write 0x70 to CTRL_TEST3 rc = %d\n", rc);
+		return;
+	}
+	rc = pm8xxx_readb(chip->dev->parent, CHG_BUCK_CTRL_TEST3, &temp);
+	if (rc) {
+		pr_err("Failed to read CTRL_TEST3 rc = %d\n", rc);
+		return;
+	}
+	/* unset the input voltage disable bit */
+	temp &= 0xFE;
+	/* set the write bit */
+	temp |= 0x80;
+	rc = pm8xxx_writeb(chip->dev->parent, CHG_BUCK_CTRL_TEST3, temp);
+	if (rc) {
+		pr_err("Failed to write 0x%x to CTRL_TEST3 rc=%d\n", temp, rc);
+		return;
+	}
+}
+
+static int64_t read_battery_id(struct pm8921_chg_chip *chip)
+{
+	int rc;
+	struct pm8xxx_adc_chan_result result;
+
+	rc = pm8xxx_adc_read(chip->batt_id_channel, &result);
+	if (rc) {
+		pr_err("error reading batt id channel = %d, rc = %d\n",
+					chip->vbat_channel, rc);
+		return rc;
+	}
+	pr_debug("batt_id phy = %lld meas = 0x%llx\n", result.physical,
+						result.measurement);
+	return result.physical;
+}
+
+static int is_battery_valid(struct pm8921_chg_chip *chip)
+{
+	int64_t rc;
+
+	if (chip->batt_id_min == 0 && chip->batt_id_max == 0)
+		return 1;
+
+	rc = read_battery_id(chip);
+	if (rc < 0) {
+		pr_err("error reading batt id channel = %d, rc = %lld\n",
+					chip->vbat_channel, rc);
+		/* assume battery id is valid when adc error happens */
+		return 1;
+	}
+
+	if (rc < chip->batt_id_min || rc > chip->batt_id_max) {
+		pr_err("batt_id phy =%lld is not valid\n", rc);
+		return 0;
+	}
+	return 1;
+}
+
+static void check_battery_valid(struct pm8921_chg_chip *chip)
+{
+	if (is_battery_valid(chip) == 0) {
+		pr_err("batt_id not valid, disbling charging\n");
+		pm_chg_auto_enable(chip, 0);
+	} else {
+		pm_chg_auto_enable(chip, !charging_disabled);
+	}
+}
+
+static void battery_id_valid(struct work_struct *work)
+{
+	struct pm8921_chg_chip *chip = container_of(work,
+				struct pm8921_chg_chip, battery_id_valid_work);
+
+	check_battery_valid(chip);
+}
+
+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 pm8921_chg_is_enabled(struct pm8921_chg_chip *chip, int interrupt)
+{
+	return test_bit(interrupt, chip->enabled_irqs);
+}
+
+static bool is_ext_charging(struct pm8921_chg_chip *chip)
+{
+	union power_supply_propval ret = {0,};
+
+	if (!chip->ext_psy)
+		return false;
+	if (chip->ext_psy->get_property(chip->ext_psy,
+					POWER_SUPPLY_PROP_CHARGE_TYPE, &ret))
+		return false;
+	if (ret.intval > POWER_SUPPLY_CHARGE_TYPE_NONE)
+		return ret.intval;
+
+	return false;
+}
+
+static bool is_ext_trickle_charging(struct pm8921_chg_chip *chip)
+{
+	union power_supply_propval ret = {0,};
+
+	if (!chip->ext_psy)
+		return false;
+	if (chip->ext_psy->get_property(chip->ext_psy,
+					POWER_SUPPLY_PROP_CHARGE_TYPE, &ret))
+		return false;
+	if (ret.intval == POWER_SUPPLY_CHARGE_TYPE_TRICKLE)
+		return true;
+
+	return false;
+}
+
+static int is_battery_charging(int fsm_state)
+{
+	if (is_ext_charging(the_chip))
+		return 1;
+
+	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(n->is_battery_full);
+		n->is_battery_full = 0;
+	}
+}
+
+static void bms_notify_check(struct pm8921_chg_chip *chip)
+{
+	int fsm_state, new_is_charging;
+
+	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));
+	}
+}
+
+static enum power_supply_property pm_power_props_usb[] = {
+	POWER_SUPPLY_PROP_PRESENT,
+	POWER_SUPPLY_PROP_ONLINE,
+	POWER_SUPPLY_PROP_CURRENT_MAX,
+};
+
+static enum power_supply_property pm_power_props_mains[] = {
+	POWER_SUPPLY_PROP_PRESENT,
+	POWER_SUPPLY_PROP_ONLINE,
+};
+
+static char *pm_power_supplied_to[] = {
+	"battery",
+};
+
+#define USB_WALL_THRESHOLD_MA	500
+static int pm_power_get_property_mains(struct power_supply *psy,
+				  enum power_supply_property psp,
+				  union power_supply_propval *val)
+{
+	/* Check if called before init */
+	if (!the_chip)
+		return -EINVAL;
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_PRESENT:
+	case POWER_SUPPLY_PROP_ONLINE:
+		val->intval = 0;
+		if (charging_disabled)
+			return 0;
+
+		/* check external charger first before the dc path */
+		if (is_ext_charging(the_chip)) {
+			val->intval = 1;
+			return 0;
+		}
+
+		if (pm_is_chg_charge_dis(the_chip)) {
+			val->intval = 0;
+			return 0;
+		}
+
+		if (the_chip->dc_present) {
+			val->intval = 1;
+			return 0;
+		}
+
+		/* USB with max current greater than 500 mA connected */
+		if (usb_target_ma > USB_WALL_THRESHOLD_MA)
+			val->intval = is_usb_chg_plugged_in(the_chip);
+			return 0;
+
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int pm_power_get_property_usb(struct power_supply *psy,
+				  enum power_supply_property psp,
+				  union power_supply_propval *val)
+{
+	int current_max;
+
+	/* Check if called before init */
+	if (!the_chip)
+		return -EINVAL;
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_CURRENT_MAX:
+		if (pm_is_chg_charge_dis(the_chip)) {
+			val->intval = 0;
+		} else {
+			pm_chg_iusbmax_get(the_chip, &current_max);
+			val->intval = current_max;
+		}
+		break;
+	case POWER_SUPPLY_PROP_PRESENT:
+	case POWER_SUPPLY_PROP_ONLINE:
+		val->intval = 0;
+		if (charging_disabled)
+			return 0;
+
+		/*
+		 * if drawing any current from usb is disabled behave
+		 * as if no usb cable is connected
+		 */
+		if (pm_is_chg_charge_dis(the_chip))
+			return 0;
+
+		/* USB charging */
+		if (usb_target_ma < USB_WALL_THRESHOLD_MA)
+			val->intval = is_usb_chg_plugged_in(the_chip);
+		else
+		    return 0;
+		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,
+	POWER_SUPPLY_PROP_CURRENT_NOW,
+	POWER_SUPPLY_PROP_TEMP,
+	POWER_SUPPLY_PROP_ENERGY_FULL,
+};
+
+static int get_prop_battery_uvolts(struct pm8921_chg_chip *chip)
+{
+	int rc;
+	struct pm8xxx_adc_chan_result result;
+
+	rc = pm8xxx_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\n", result.physical,
+						result.measurement);
+	return (int)result.physical;
+}
+
+static unsigned int voltage_based_capacity(struct pm8921_chg_chip *chip)
+{
+	unsigned int current_voltage_uv = get_prop_battery_uvolts(chip);
+	unsigned int current_voltage_mv = current_voltage_uv / 1000;
+	unsigned int low_voltage = chip->min_voltage_mv;
+	unsigned int high_voltage = chip->max_voltage_mv;
+
+	if (current_voltage_mv <= low_voltage)
+		return 0;
+	else if (current_voltage_mv >= high_voltage)
+		return 100;
+	else
+		return (current_voltage_mv - low_voltage) * 100
+		    / (high_voltage - low_voltage);
+}
+
+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_batt_capacity(struct pm8921_chg_chip *chip)
+{
+	int percent_soc;
+
+	if (!get_prop_batt_present(chip))
+		percent_soc = voltage_based_capacity(chip);
+	else
+		percent_soc = pm8921_bms_get_percent_charge();
+
+	if (percent_soc == -ENXIO)
+		percent_soc = voltage_based_capacity(chip);
+
+	if (percent_soc <= 10)
+		pr_warn("low battery charge = %d%%\n", percent_soc);
+
+	return percent_soc;
+}
+
+static int get_prop_batt_current(struct pm8921_chg_chip *chip)
+{
+	int result_ua, rc;
+
+	rc = pm8921_bms_get_battery_current(&result_ua);
+	if (rc == -ENXIO) {
+		rc = pm8xxx_ccadc_get_battery_current(&result_ua);
+	}
+
+	if (rc) {
+		pr_err("unable to get batt current rc = %d\n", rc);
+		return rc;
+	} else {
+		return result_ua;
+	}
+}
+
+static int get_prop_batt_fcc(struct pm8921_chg_chip *chip)
+{
+	int rc;
+
+	rc = pm8921_bms_get_fcc();
+	if (rc < 0)
+		pr_err("unable to get batt fcc rc = %d\n", rc);
+	return rc;
+}
+
+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_charge_type(struct pm8921_chg_chip *chip)
+{
+	int temp;
+
+	if (!get_prop_batt_present(chip))
+		return POWER_SUPPLY_CHARGE_TYPE_NONE;
+
+	if (is_ext_trickle_charging(chip))
+		return POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
+
+	if (is_ext_charging(chip))
+		return POWER_SUPPLY_CHARGE_TYPE_FAST;
+
+	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 batt_state = POWER_SUPPLY_STATUS_DISCHARGING;
+	int fsm_state = pm_chg_get_fsm_state(chip);
+	int i;
+
+	if (chip->ext_psy) {
+		if (chip->ext_charge_done)
+			return POWER_SUPPLY_STATUS_FULL;
+		if (chip->ext_charging)
+			return POWER_SUPPLY_STATUS_CHARGING;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(map); i++)
+		if (map[i].fsm_state == fsm_state)
+			batt_state = map[i].batt_state;
+
+	if (fsm_state == FSM_STATE_ON_CHG_HIGHI_1) {
+		if (!pm_chg_get_rt_status(chip, BATT_INSERTED_IRQ)
+			|| !pm_chg_get_rt_status(chip, BAT_TEMP_OK_IRQ)
+			|| pm_chg_get_rt_status(chip, CHGHOT_IRQ)
+			|| pm_chg_get_rt_status(chip, VBATDET_LOW_IRQ))
+
+			batt_state = POWER_SUPPLY_STATUS_NOT_CHARGING;
+	}
+	return batt_state;
+}
+
+#define MAX_TOLERABLE_BATT_TEMP_DDC	680
+static int get_prop_batt_temp(struct pm8921_chg_chip *chip)
+{
+	int rc;
+	struct pm8xxx_adc_chan_result result;
+
+	rc = pm8xxx_adc_read(chip->batt_temp_channel, &result);
+	if (rc) {
+		pr_err("error reading adc channel = %d, rc = %d\n",
+					chip->vbat_channel, rc);
+		return rc;
+	}
+	pr_debug("batt_temp phy = %lld meas = 0x%llx\n", result.physical,
+						result.measurement);
+	if (result.physical > MAX_TOLERABLE_BATT_TEMP_DDC)
+		pr_err("BATT_TEMP= %d > 68degC, device will be shutdown\n",
+							(int) result.physical);
+
+	return (int)result.physical;
+}
+
+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_mv * 1000;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
+		val->intval = chip->min_voltage_mv * 1000;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+		val->intval = get_prop_battery_uvolts(chip);
+		break;
+	case POWER_SUPPLY_PROP_CAPACITY:
+		val->intval = get_prop_batt_capacity(chip);
+		break;
+	case POWER_SUPPLY_PROP_CURRENT_NOW:
+		val->intval = get_prop_batt_current(chip);
+		break;
+	case POWER_SUPPLY_PROP_TEMP:
+		val->intval = get_prop_batt_temp(chip);
+		break;
+	case POWER_SUPPLY_PROP_ENERGY_FULL:
+		val->intval = get_prop_batt_fcc(chip) * 1000;
+		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");
+	}
+}
+
+/* assumes vbus_lock is held */
+static void __pm8921_charger_vbus_draw(unsigned int mA)
+{
+	int i, rc;
+	if (!the_chip) {
+		pr_err("called before init\n");
+		return;
+	}
+
+	if (mA >= 0 && mA <= 2) {
+		usb_chg_current = 0;
+		rc = pm_chg_iusbmax_set(the_chip, 0);
+		if (rc) {
+			pr_err("unable to set iusb to %d rc = %d\n", 0, 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;
+		}
+
+		/* Check if IUSB_FINE_RES is available */
+		if ((usb_ma_table[i].value & PM8917_IUSB_FINE_RES)
+				&& !the_chip->iusb_fine_res)
+			i--;
+		if (i < 0)
+			i = 0;
+		rc = pm_chg_iusbmax_set(the_chip, i);
+		if (rc) {
+			pr_err("unable to set iusb to %d rc = %d\n", i, 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);
+
+	if (!the_chip) {
+		pr_err("chip not yet initalized\n");
+		return;
+	}
+
+	/*
+	 * Reject VBUS requests if USB connection is the only available
+	 * power source. This makes sure that if booting without
+	 * battery the iusb_max value is not decreased avoiding potential
+	 * brown_outs.
+	 *
+	 * This would also apply when the battery has been
+	 * removed from the running system.
+	 */
+	if (!get_prop_batt_present(the_chip)
+		&& !is_dc_chg_plugged_in(the_chip)) {
+		pr_err("rejected: no other power source connected\n");
+		return;
+	}
+
+	if (usb_max_current && mA > usb_max_current) {
+		pr_warn("restricting usb current to %d instead of %d\n",
+					usb_max_current, mA);
+		mA = usb_max_current;
+	}
+	if (usb_target_ma == 0 && mA > USB_WALL_THRESHOLD_MA)
+		usb_target_ma = mA;
+
+	spin_lock_irqsave(&vbus_lock, flags);
+	if (the_chip) {
+		if (mA > USB_WALL_THRESHOLD_MA)
+			__pm8921_charger_vbus_draw(USB_WALL_THRESHOLD_MA);
+		else
+			__pm8921_charger_vbus_draw(mA);
+	} else {
+		/*
+		 * called before pmic initialized,
+		 * save this value and use it at probe
+		 */
+		if (mA > USB_WALL_THRESHOLD_MA)
+			usb_chg_current = USB_WALL_THRESHOLD_MA;
+		else
+			usb_chg_current = mA;
+	}
+	spin_unlock_irqrestore(&vbus_lock, flags);
+}
+EXPORT_SYMBOL_GPL(pm8921_charger_vbus_draw);
+
+int pm8921_charger_enable(bool enable)
+{
+	int rc;
+
+	if (!the_chip) {
+		pr_err("called before init\n");
+		return -EINVAL;
+	}
+	enable = !!enable;
+	rc = pm_chg_auto_enable(the_chip, enable);
+	if (rc)
+		pr_err("Failed rc=%d\n", rc);
+	return rc;
+}
+EXPORT_SYMBOL(pm8921_charger_enable);
+
+int pm8921_is_usb_chg_plugged_in(void)
+{
+	if (!the_chip) {
+		pr_err("called before init\n");
+		return -EINVAL;
+	}
+	return is_usb_chg_plugged_in(the_chip);
+}
+EXPORT_SYMBOL(pm8921_is_usb_chg_plugged_in);
+
+int pm8921_is_dc_chg_plugged_in(void)
+{
+	if (!the_chip) {
+		pr_err("called before init\n");
+		return -EINVAL;
+	}
+	return is_dc_chg_plugged_in(the_chip);
+}
+EXPORT_SYMBOL(pm8921_is_dc_chg_plugged_in);
+
+int pm8921_is_battery_present(void)
+{
+	if (!the_chip) {
+		pr_err("called before init\n");
+		return -EINVAL;
+	}
+	return get_prop_batt_present(the_chip);
+}
+EXPORT_SYMBOL(pm8921_is_battery_present);
+
+/*
+ * Disabling the charge current limit causes current
+ * current limits to have no monitoring. An adequate charger
+ * capable of supplying high current while sustaining VIN_MIN
+ * is required if the limiting is disabled.
+ */
+int pm8921_disable_input_current_limit(bool disable)
+{
+	if (!the_chip) {
+		pr_err("called before init\n");
+		return -EINVAL;
+	}
+	if (disable) {
+		pr_warn("Disabling input current limit!\n");
+
+		return pm8xxx_writeb(the_chip->dev->parent,
+			 CHG_BUCK_CTRL_TEST3, 0xF2);
+	}
+	return 0;
+}
+EXPORT_SYMBOL(pm8921_disable_input_current_limit);
+
+int pm8921_set_max_battery_charge_current(int ma)
+{
+	if (!the_chip) {
+		pr_err("called before init\n");
+		return -EINVAL;
+	}
+	return pm_chg_ibatmax_set(the_chip, ma);
+}
+EXPORT_SYMBOL(pm8921_set_max_battery_charge_current);
+
+int pm8921_disable_source_current(bool disable)
+{
+	if (!the_chip) {
+		pr_err("called before init\n");
+		return -EINVAL;
+	}
+	if (disable)
+		pr_warn("current drawn from chg=0, battery provides current\n");
+	return pm_chg_charge_dis(the_chip, disable);
+}
+EXPORT_SYMBOL(pm8921_disable_source_current);
+
+int pm8921_regulate_input_voltage(int voltage)
+{
+	int rc;
+
+	if (!the_chip) {
+		pr_err("called before init\n");
+		return -EINVAL;
+	}
+	rc = pm_chg_vinmin_set(the_chip, voltage);
+
+	if (rc == 0)
+		the_chip->vin_min = voltage;
+
+	return rc;
+}
+
+#define USB_OV_THRESHOLD_MASK  0x60
+#define USB_OV_THRESHOLD_SHIFT  5
+int pm8921_usb_ovp_set_threshold(enum pm8921_usb_ov_threshold ov)
+{
+	u8 temp;
+
+	if (!the_chip) {
+		pr_err("called before init\n");
+		return -EINVAL;
+	}
+
+	if (ov > PM_USB_OV_7V) {
+		pr_err("limiting to over voltage threshold to 7volts\n");
+		ov = PM_USB_OV_7V;
+	}
+
+	temp = USB_OV_THRESHOLD_MASK & (ov << USB_OV_THRESHOLD_SHIFT);
+
+	return pm_chg_masked_write(the_chip, USB_OVP_CONTROL,
+				USB_OV_THRESHOLD_MASK, temp);
+}
+EXPORT_SYMBOL(pm8921_usb_ovp_set_threshold);
+
+#define USB_DEBOUNCE_TIME_MASK	0x06
+#define USB_DEBOUNCE_TIME_SHIFT 1
+int pm8921_usb_ovp_set_hystersis(enum pm8921_usb_debounce_time ms)
+{
+	u8 temp;
+
+	if (!the_chip) {
+		pr_err("called before init\n");
+		return -EINVAL;
+	}
+
+	if (ms > PM_USB_DEBOUNCE_80P5MS) {
+		pr_err("limiting debounce to 80.5ms\n");
+		ms = PM_USB_DEBOUNCE_80P5MS;
+	}
+
+	temp = USB_DEBOUNCE_TIME_MASK & (ms << USB_DEBOUNCE_TIME_SHIFT);
+
+	return pm_chg_masked_write(the_chip, USB_OVP_CONTROL,
+				USB_DEBOUNCE_TIME_MASK, temp);
+}
+EXPORT_SYMBOL(pm8921_usb_ovp_set_hystersis);
+
+#define USB_OVP_DISABLE_MASK	0x80
+int pm8921_usb_ovp_disable(int disable)
+{
+	u8 temp = 0;
+
+	if (!the_chip) {
+		pr_err("called before init\n");
+		return -EINVAL;
+	}
+
+	if (disable)
+		temp = USB_OVP_DISABLE_MASK;
+
+	return pm_chg_masked_write(the_chip, USB_OVP_CONTROL,
+				USB_OVP_DISABLE_MASK, temp);
+}
+
+bool pm8921_is_battery_charging(int *source)
+{
+	int fsm_state, is_charging, dc_present, usb_present;
+
+	if (!the_chip) {
+		pr_err("called before init\n");
+		return -EINVAL;
+	}
+	fsm_state = pm_chg_get_fsm_state(the_chip);
+	is_charging = is_battery_charging(fsm_state);
+	if (is_charging == 0) {
+		*source = PM8921_CHG_SRC_NONE;
+		return is_charging;
+	}
+
+	if (source == NULL)
+		return is_charging;
+
+	/* the battery is charging, the source is requested, find it */
+	dc_present = is_dc_chg_plugged_in(the_chip);
+	usb_present = is_usb_chg_plugged_in(the_chip);
+
+	if (dc_present && !usb_present)
+		*source = PM8921_CHG_SRC_DC;
+
+	if (usb_present && !dc_present)
+		*source = PM8921_CHG_SRC_USB;
+
+	if (usb_present && dc_present)
+		/*
+		 * The system always chooses dc for charging since it has
+		 * higher priority.
+		 */
+		*source = PM8921_CHG_SRC_DC;
+
+	return is_charging;
+}
+EXPORT_SYMBOL(pm8921_is_battery_charging);
+
+int pm8921_set_usb_power_supply_type(enum power_supply_type type)
+{
+	if (!the_chip) {
+		pr_err("called before init\n");
+		return -EINVAL;
+	}
+
+	if (type < POWER_SUPPLY_TYPE_USB)
+		return -EINVAL;
+
+	the_chip->usb_psy.type = type;
+	power_supply_changed(&the_chip->usb_psy);
+	power_supply_changed(&the_chip->dc_psy);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(pm8921_set_usb_power_supply_type);
+
+int pm8921_batt_temperature(void)
+{
+	if (!the_chip) {
+		pr_err("called before init\n");
+		return -EINVAL;
+	}
+	return get_prop_batt_temp(the_chip);
+}
+
+static void handle_usb_insertion_removal(struct pm8921_chg_chip *chip)
+{
+	int usb_present;
+
+	pm_chg_failed_clear(chip, 1);
+	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);
+		power_supply_changed(&chip->batt_psy);
+		pm8921_bms_calibrate_hkadc();
+	}
+	if (usb_present) {
+		schedule_delayed_work(&chip->unplug_check_work,
+			round_jiffies_relative(msecs_to_jiffies
+				(UNPLUG_CHECK_WAIT_PERIOD_MS)));
+		pm8921_chg_enable_irq(chip, CHG_GONE_IRQ);
+	} else {
+		/* USB unplugged reset target current */
+		usb_target_ma = 0;
+		pm8921_chg_disable_irq(chip, CHG_GONE_IRQ);
+	}
+	enable_input_voltage_regulation(chip);
+	bms_notify_check(chip);
+}
+
+static void handle_stop_ext_chg(struct pm8921_chg_chip *chip)
+{
+	if (!chip->ext_psy) {
+		pr_debug("external charger not registered.\n");
+		return;
+	}
+
+	if (!chip->ext_charging) {
+		pr_debug("already not charging.\n");
+		return;
+	}
+
+	power_supply_set_charge_type(chip->ext_psy,
+					POWER_SUPPLY_CHARGE_TYPE_NONE);
+	pm8921_disable_source_current(false); /* release BATFET */
+	power_supply_changed(&chip->dc_psy);
+	chip->ext_charging = false;
+	chip->ext_charge_done = false;
+	bms_notify_check(chip);
+	/* Update battery charging LEDs and user space battery info */
+	power_supply_changed(&chip->batt_psy);
+}
+
+static void handle_start_ext_chg(struct pm8921_chg_chip *chip)
+{
+	int dc_present;
+	int batt_present;
+	int batt_temp_ok;
+	int vbat_ov;
+	unsigned long delay =
+		round_jiffies_relative(msecs_to_jiffies(EOC_CHECK_PERIOD_MS));
+
+	if (!chip->ext_psy) {
+		pr_debug("external charger not registered.\n");
+		return;
+	}
+
+	if (chip->ext_charging) {
+		pr_debug("already charging.\n");
+		return;
+	}
+
+	dc_present = is_dc_chg_plugged_in(the_chip);
+	batt_present = pm_chg_get_rt_status(chip, BATT_INSERTED_IRQ);
+	batt_temp_ok = pm_chg_get_rt_status(chip, BAT_TEMP_OK_IRQ);
+
+	if (!dc_present) {
+		pr_warn("%s. dc not present.\n", __func__);
+		return;
+	}
+	if (!batt_present) {
+		pr_warn("%s. battery not present.\n", __func__);
+		return;
+	}
+	if (!batt_temp_ok) {
+		pr_warn("%s. battery temperature not ok.\n", __func__);
+		return;
+	}
+	pm8921_disable_source_current(true); /* Force BATFET=ON */
+	vbat_ov = pm_chg_get_rt_status(chip, VBAT_OV_IRQ);
+	if (vbat_ov) {
+		pr_warn("%s. battery over voltage.\n", __func__);
+		return;
+	}
+
+	power_supply_set_online(chip->ext_psy, dc_present);
+	power_supply_set_charge_type(chip->ext_psy,
+					POWER_SUPPLY_CHARGE_TYPE_FAST);
+	power_supply_changed(&chip->dc_psy);
+	chip->ext_charging = true;
+	chip->ext_charge_done = false;
+	bms_notify_check(chip);
+	/* Start BMS */
+	schedule_delayed_work(&chip->eoc_work, delay);
+	wake_lock(&chip->eoc_wake_lock);
+	/* Update battery charging LEDs and user space battery info */
+	power_supply_changed(&chip->batt_psy);
+}
+
+static void turn_off_usb_ovp_fet(struct pm8921_chg_chip *chip)
+{
+	u8 temp;
+	int rc;
+
+	rc = pm8xxx_writeb(chip->dev->parent, USB_OVP_TEST, 0x30);
+	if (rc) {
+		pr_err("Failed to write 0x30 to USB_OVP_TEST rc = %d\n", rc);
+		return;
+	}
+	rc = pm8xxx_readb(chip->dev->parent, USB_OVP_TEST, &temp);
+	if (rc) {
+		pr_err("Failed to read from USB_OVP_TEST rc = %d\n", rc);
+		return;
+	}
+	/* set ovp fet disable bit and the write bit */
+	temp |= 0x81;
+	rc = pm8xxx_writeb(chip->dev->parent, USB_OVP_TEST, temp);
+	if (rc) {
+		pr_err("Failed to write 0x%x USB_OVP_TEST rc=%d\n", temp, rc);
+		return;
+	}
+}
+
+static void turn_on_usb_ovp_fet(struct pm8921_chg_chip *chip)
+{
+	u8 temp;
+	int rc;
+
+	rc = pm8xxx_writeb(chip->dev->parent, USB_OVP_TEST, 0x30);
+	if (rc) {
+		pr_err("Failed to write 0x30 to USB_OVP_TEST rc = %d\n", rc);
+		return;
+	}
+	rc = pm8xxx_readb(chip->dev->parent, USB_OVP_TEST, &temp);
+	if (rc) {
+		pr_err("Failed to read from USB_OVP_TEST rc = %d\n", rc);
+		return;
+	}
+	/* unset ovp fet disable bit and set the write bit */
+	temp &= 0xFE;
+	temp |= 0x80;
+	rc = pm8xxx_writeb(chip->dev->parent, USB_OVP_TEST, temp);
+	if (rc) {
+		pr_err("Failed to write 0x%x to USB_OVP_TEST rc = %d\n",
+								temp, rc);
+		return;
+	}
+}
+
+static int param_open_ovp_counter = 10;
+module_param(param_open_ovp_counter, int, 0644);
+
+#define WRITE_BANK_4		0xC0
+#define USB_OVP_DEBOUNCE_TIME 0x06
+static void unplug_ovp_fet_open(struct pm8921_chg_chip *chip)
+{
+	int chg_gone = 0, usb_chg_plugged_in = 0;
+	int count = 0;
+
+	while (count++ < param_open_ovp_counter) {
+		pm_chg_masked_write(chip, USB_OVP_CONTROL,
+						USB_OVP_DEBOUNCE_TIME, 0x0);
+		usleep(10);
+		usb_chg_plugged_in = is_usb_chg_plugged_in(chip);
+		chg_gone = pm_chg_get_rt_status(chip, CHG_GONE_IRQ);
+		pr_debug("OVP FET count = %d chg_gone=%d, usb_valid = %d\n",
+					count, chg_gone, usb_chg_plugged_in);
+
+		/* note usb_chg_plugged_in=0 => chg_gone=1 */
+		if (chg_gone == 1 && usb_chg_plugged_in == 1) {
+			pr_debug("since chg_gone = 1 dis ovp_fet for 20msec\n");
+			turn_off_usb_ovp_fet(chip);
+
+			msleep(20);
+
+			turn_on_usb_ovp_fet(chip);
+		} else {
+			break;
+		}
+	}
+	pm_chg_masked_write(chip, USB_OVP_CONTROL,
+		USB_OVP_DEBOUNCE_TIME, 0x2);
+	pr_debug("Exit count=%d chg_gone=%d, usb_valid=%d\n",
+		count, chg_gone, usb_chg_plugged_in);
+	return;
+}
+
+static int find_usb_ma_value(int value)
+{
+	int i;
+
+	for (i = ARRAY_SIZE(usb_ma_table) - 1; i >= 0; i--) {
+		if (value >= usb_ma_table[i].usb_ma)
+			break;
+	}
+
+	return i;
+}
+
+static void decrease_usb_ma_value(int *value)
+{
+	int i;
+
+	if (value) {
+		i = find_usb_ma_value(*value);
+		if (i > 0)
+			i--;
+		*value = usb_ma_table[i].usb_ma;
+	}
+}
+
+static void increase_usb_ma_value(int *value)
+{
+	int i;
+
+	if (value) {
+		i = find_usb_ma_value(*value);
+
+		if (i < (ARRAY_SIZE(usb_ma_table) - 1))
+			i++;
+		/* Get next correct entry if IUSB_FINE_RES is not available */
+		while (!the_chip->iusb_fine_res
+			&& (usb_ma_table[i].value & PM8917_IUSB_FINE_RES)
+			&& i < (ARRAY_SIZE(usb_ma_table) - 1))
+			i++;
+
+		*value = usb_ma_table[i].usb_ma;
+	}
+}
+
+static void vin_collapse_check_worker(struct work_struct *work)
+{
+	struct delayed_work *dwork = to_delayed_work(work);
+	struct pm8921_chg_chip *chip = container_of(dwork,
+			struct pm8921_chg_chip, vin_collapse_check_work);
+
+	/* AICL only for wall-chargers */
+	if (is_usb_chg_plugged_in(chip) &&
+		usb_target_ma > USB_WALL_THRESHOLD_MA) {
+		/* decrease usb_target_ma */
+		decrease_usb_ma_value(&usb_target_ma);
+		/* reset here, increase in unplug_check_worker */
+		__pm8921_charger_vbus_draw(USB_WALL_THRESHOLD_MA);
+		pr_debug("usb_now=%d, usb_target = %d\n",
+				USB_WALL_THRESHOLD_MA, usb_target_ma);
+	} else {
+		handle_usb_insertion_removal(chip);
+	}
+}
+
+#define VIN_MIN_COLLAPSE_CHECK_MS	50
+static irqreturn_t usbin_valid_irq_handler(int irq, void *data)
+{
+	if (usb_target_ma)
+		schedule_delayed_work(&the_chip->vin_collapse_check_work,
+				      round_jiffies_relative(msecs_to_jiffies
+						(VIN_MIN_COLLAPSE_CHECK_MS)));
+	else
+	    handle_usb_insertion_removal(data);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t usbin_ov_irq_handler(int irq, void *data)
+{
+	pr_err("USB OverVoltage\n");
+	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);
+	schedule_work(&chip->battery_id_valid_work);
+	handle_start_ext_chg(chip);
+	pr_debug("battery present=%d", status);
+	power_supply_changed(&chip->batt_psy);
+	return IRQ_HANDLED;
+}
+
+/*
+ * this interrupt used to restart charging a battery.
+ *
+ * Note: When DC-inserted the VBAT can't go low.
+ * VPH_PWR is provided by the ext-charger.
+ * After End-Of-Charging from DC, charging can be resumed only
+ * if DC is removed and then inserted after the battery was in use.
+ * Therefore the handle_start_ext_chg() is not called.
+ */
+static irqreturn_t vbatdet_low_irq_handler(int irq, void *data)
+{
+	struct pm8921_chg_chip *chip = data;
+	int high_transition;
+
+	high_transition = pm_chg_get_rt_status(chip, VBATDET_LOW_IRQ);
+
+	if (high_transition) {
+		/* enable auto charging */
+		pm_chg_auto_enable(chip, !charging_disabled);
+		pr_info("batt fell below resume voltage %s\n",
+			charging_disabled ? "" : "charger enabled");
+	}
+	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)
+{
+	pr_err("USB UnderVoltage\n");
+	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_debug("fsm_state=%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));
+
+	handle_stop_ext_chg(chip);
+
+	power_supply_changed(&chip->batt_psy);
+	power_supply_changed(&chip->usb_psy);
+	power_supply_changed(&chip->dc_psy);
+
+	bms_notify_check(chip);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t chgfail_irq_handler(int irq, void *data)
+{
+	struct pm8921_chg_chip *chip = data;
+	int ret;
+
+	ret = pm_chg_failed_clear(chip, 1);
+	if (ret)
+		pr_err("Failed to write CHG_FAILED_CLEAR bit\n");
+
+	pr_err("batt_present = %d, batt_temp_ok = %d, state_changed_to=%d\n",
+			get_prop_batt_present(chip),
+			pm_chg_get_rt_status(chip, BAT_TEMP_OK_IRQ),
+			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;
+
+	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);
+
+	bms_notify_check(chip);
+
+	return IRQ_HANDLED;
+}
+
+static int param_vin_disable_counter = 5;
+module_param(param_vin_disable_counter, int, 0644);
+
+static void attempt_reverse_boost_fix(struct pm8921_chg_chip *chip,
+							int count, int usb_ma)
+{
+	__pm8921_charger_vbus_draw(500);
+	pr_debug("count = %d iusb=500mA\n", count);
+	disable_input_voltage_regulation(chip);
+	pr_debug("count = %d disable_input_regulation\n", count);
+
+	msleep(20);
+
+	pr_debug("count = %d end sleep 20ms chg_gone=%d, usb_valid = %d\n",
+				count,
+				pm_chg_get_rt_status(chip, CHG_GONE_IRQ),
+				is_usb_chg_plugged_in(chip));
+	pr_debug("count = %d restoring input regulation and usb_ma = %d\n",
+		 count, usb_ma);
+	enable_input_voltage_regulation(chip);
+	__pm8921_charger_vbus_draw(usb_ma);
+}
+
+#define VIN_ACTIVE_BIT BIT(0)
+#define UNPLUG_WRKARND_RESTORE_WAIT_PERIOD_US 200
+#define VIN_MIN_INCREASE_MV 100
+static void unplug_check_worker(struct work_struct *work)
+{
+	struct delayed_work *dwork = to_delayed_work(work);
+	struct pm8921_chg_chip *chip = container_of(dwork,
+				struct pm8921_chg_chip, unplug_check_work);
+	u8 reg_loop;
+	int ibat, usb_chg_plugged_in, usb_ma;
+	int chg_gone = 0;
+
+	reg_loop = 0;
+	usb_chg_plugged_in = is_usb_chg_plugged_in(chip);
+	if (!usb_chg_plugged_in) {
+		pr_debug("Stopping Unplug Check Worker since USB is removed"
+			"reg_loop = %d, fsm = %d ibat = %d\n",
+			pm_chg_get_regulation_loop(chip),
+			pm_chg_get_fsm_state(chip),
+			get_prop_batt_current(chip)
+			);
+		return;
+	}
+
+	pm_chg_iusbmax_get(chip, &usb_ma);
+	if (usb_ma == 500 && !usb_target_ma) {
+		pr_debug("Stopping Unplug Check Worker since USB == 500mA\n");
+		disable_input_voltage_regulation(chip);
+		return;
+	}
+
+	if (usb_ma <= 100) {
+		pr_debug(
+			"Unenumerated yet or suspended usb_ma = %d skipping\n",
+			usb_ma);
+		goto check_again_later;
+	}
+	if (pm8921_chg_is_enabled(chip, CHG_GONE_IRQ))
+		pr_debug("chg gone irq is enabled\n");
+
+	reg_loop = pm_chg_get_regulation_loop(chip);
+	pr_debug("reg_loop=0x%x usb_ma = %d\n", reg_loop, usb_ma);
+
+	if ((reg_loop & VIN_ACTIVE_BIT) && (usb_ma > USB_WALL_THRESHOLD_MA)) {
+		decrease_usb_ma_value(&usb_ma);
+		usb_target_ma = usb_ma;
+		/* end AICL here */
+		__pm8921_charger_vbus_draw(usb_ma);
+		pr_debug("usb_now=%d, usb_target = %d\n",
+			usb_ma, usb_target_ma);
+	}
+
+	reg_loop = pm_chg_get_regulation_loop(chip);
+	pr_debug("reg_loop=0x%x usb_ma = %d\n", reg_loop, usb_ma);
+
+	if (reg_loop & VIN_ACTIVE_BIT) {
+		ibat = get_prop_batt_current(chip);
+
+		pr_debug("ibat = %d fsm = %d reg_loop = 0x%x\n",
+				ibat, pm_chg_get_fsm_state(chip), reg_loop);
+		if (ibat > 0) {
+			int count = 0;
+
+			while (count++ < param_vin_disable_counter
+					&& usb_chg_plugged_in == 1) {
+				attempt_reverse_boost_fix(chip, count, usb_ma);
+				usb_chg_plugged_in
+					= is_usb_chg_plugged_in(chip);
+			}
+		}
+	}
+
+	usb_chg_plugged_in = is_usb_chg_plugged_in(chip);
+	chg_gone = pm_chg_get_rt_status(chip, CHG_GONE_IRQ);
+
+	if (chg_gone == 1  && usb_chg_plugged_in == 1) {
+		/* run the worker directly */
+		pr_debug(" ver5 step: chg_gone=%d, usb_valid = %d\n",
+						chg_gone, usb_chg_plugged_in);
+		unplug_ovp_fet_open(chip);
+	}
+
+	if (!(reg_loop & VIN_ACTIVE_BIT)) {
+		/* only increase iusb_max if vin loop not active */
+		if (usb_ma < usb_target_ma) {
+			increase_usb_ma_value(&usb_ma);
+			__pm8921_charger_vbus_draw(usb_ma);
+			pr_debug("usb_now=%d, usb_target = %d\n",
+					usb_ma, usb_target_ma);
+		} else {
+			usb_target_ma = usb_ma;
+		}
+	}
+check_again_later:
+	/* schedule to check again later */
+	schedule_delayed_work(&chip->unplug_check_work,
+		      round_jiffies_relative(msecs_to_jiffies
+				(UNPLUG_CHECK_WAIT_PERIOD_MS)));
+}
+
+static irqreturn_t loop_change_irq_handler(int irq, void *data)
+{
+	struct pm8921_chg_chip *chip = data;
+
+	pr_debug("fsm_state=%d reg_loop=0x%x\n",
+		pm_chg_get_fsm_state(data),
+		pm_chg_get_regulation_loop(data));
+	schedule_work(&chip->unplug_check_work.work);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t fastchg_irq_handler(int irq, void *data)
+{
+	struct pm8921_chg_chip *chip = data;
+	int high_transition;
+
+	high_transition = pm_chg_get_rt_status(chip, FASTCHG_IRQ);
+	if (high_transition && !delayed_work_pending(&chip->eoc_work)) {
+		wake_lock(&chip->eoc_wake_lock);
+		schedule_delayed_work(&chip->eoc_work,
+				      round_jiffies_relative(msecs_to_jiffies
+						     (EOC_CHECK_PERIOD_MS)));
+	}
+	power_supply_changed(&chip->batt_psy);
+	bms_notify_check(chip);
+	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));
+	handle_stop_ext_chg(chip);
+	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;
+
+	handle_stop_ext_chg(chip);
+	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);
+	handle_stop_ext_chg(chip);
+	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));
+	handle_stop_ext_chg(chip);
+
+	power_supply_changed(&chip->batt_psy);
+	power_supply_changed(&chip->usb_psy);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t chg_gone_irq_handler(int irq, void *data)
+{
+	struct pm8921_chg_chip *chip = data;
+	int chg_gone, usb_chg_plugged_in;
+
+	usb_chg_plugged_in = is_usb_chg_plugged_in(chip);
+	chg_gone = pm_chg_get_rt_status(chip, CHG_GONE_IRQ);
+
+	pr_debug("chg_gone=%d, usb_valid = %d\n", chg_gone, usb_chg_plugged_in);
+	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);
+	return IRQ_HANDLED;
+}
+/*
+ *
+ * bat_temp_ok_irq_handler - is edge triggered, hence it will
+ * fire for two cases:
+ *
+ * If the interrupt line switches to high temperature is okay
+ * and thus charging begins.
+ * If bat_temp_ok is low this means the temperature is now
+ * too hot or cold, so charging is stopped.
+ *
+ */
+static irqreturn_t bat_temp_ok_irq_handler(int irq, void *data)
+{
+	int bat_temp_ok;
+	struct pm8921_chg_chip *chip = data;
+
+	bat_temp_ok = pm_chg_get_rt_status(chip, BAT_TEMP_OK_IRQ);
+
+	pr_debug("batt_temp_ok = %d fsm_state%d\n",
+			 bat_temp_ok, pm_chg_get_fsm_state(data));
+
+	if (bat_temp_ok)
+		handle_start_ext_chg(chip);
+	else
+		handle_stop_ext_chg(chip);
+
+	power_supply_changed(&chip->batt_psy);
+	power_supply_changed(&chip->usb_psy);
+	bms_notify_check(chip);
+	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)
+{
+	struct pm8921_chg_chip *chip = data;
+	int dc_present;
+
+	dc_present = pm_chg_get_rt_status(chip, DCIN_VALID_IRQ);
+	if (chip->ext_psy)
+		power_supply_set_online(chip->ext_psy, dc_present);
+	chip->dc_present = dc_present;
+	if (dc_present)
+		handle_start_ext_chg(chip);
+	else
+		handle_stop_ext_chg(chip);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t dcin_ov_irq_handler(int irq, void *data)
+{
+	struct pm8921_chg_chip *chip = data;
+
+	handle_stop_ext_chg(chip);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t dcin_uv_irq_handler(int irq, void *data)
+{
+	struct pm8921_chg_chip *chip = data;
+
+	handle_stop_ext_chg(chip);
+
+	return IRQ_HANDLED;
+}
+
+static int __pm_batt_external_power_changed_work(struct device *dev, void *data)
+{
+	struct power_supply *psy = &the_chip->batt_psy;
+	struct power_supply *epsy = dev_get_drvdata(dev);
+	int i, dcin_irq;
+
+	/* Only search for external supply if none is registered */
+	if (!the_chip->ext_psy) {
+		dcin_irq = the_chip->pmic_chg_irq[DCIN_VALID_IRQ];
+		for (i = 0; i < epsy->num_supplicants; i++) {
+			if (!strncmp(epsy->supplied_to[i], psy->name, 7)) {
+				if (!strncmp(epsy->name, "dc", 2)) {
+					the_chip->ext_psy = epsy;
+					dcin_valid_irq_handler(dcin_irq,
+							the_chip);
+				}
+			}
+		}
+	}
+	return 0;
+}
+
+static void pm_batt_external_power_changed(struct power_supply *psy)
+{
+	/* Only look for an external supply if it hasn't been registered */
+	if (!the_chip->ext_psy)
+		class_for_each_device(power_supply_class, NULL, psy,
+					 __pm_batt_external_power_changed_work);
+}
+
+/**
+ * update_heartbeat - internal function to update userspace
+ *		per update_time minutes
+ *
+ */
+static void update_heartbeat(struct work_struct *work)
+{
+	struct delayed_work *dwork = to_delayed_work(work);
+	struct pm8921_chg_chip *chip = container_of(dwork,
+				struct pm8921_chg_chip, update_heartbeat_work);
+
+	pm_chg_failed_clear(chip, 1);
+	power_supply_changed(&chip->batt_psy);
+	schedule_delayed_work(&chip->update_heartbeat_work,
+			      round_jiffies_relative(msecs_to_jiffies
+						     (chip->update_time)));
+}
+#define VDD_LOOP_ACTIVE_BIT	BIT(3)
+#define VDD_MAX_INCREASE_MV	400
+static int vdd_max_increase_mv = VDD_MAX_INCREASE_MV;
+module_param(vdd_max_increase_mv, int, 0644);
+
+static int ichg_threshold_ua = -400000;
+module_param(ichg_threshold_ua, int, 0644);
+static void adjust_vdd_max_for_fastchg(struct pm8921_chg_chip *chip)
+{
+	int ichg_meas_ua, vbat_uv;
+	int ichg_meas_ma;
+	int adj_vdd_max_mv, programmed_vdd_max;
+	int vbat_batt_terminal_uv;
+	int vbat_batt_terminal_mv;
+	int reg_loop;
+	int delta_mv = 0;
+
+	if (chip->rconn_mohm == 0) {
+		pr_debug("Exiting as rconn_mohm is 0\n");
+		return;
+	}
+	/* adjust vdd_max only in normal temperature zone */
+	if (chip->is_bat_cool || chip->is_bat_warm) {
+		pr_debug("Exiting is_bat_cool = %d is_batt_warm = %d\n",
+				chip->is_bat_cool, chip->is_bat_warm);
+		return;
+	}
+
+	reg_loop = pm_chg_get_regulation_loop(chip);
+	if (!(reg_loop & VDD_LOOP_ACTIVE_BIT)) {
+		pr_debug("Exiting Vdd loop is not active reg loop=0x%x\n",
+			reg_loop);
+		return;
+	}
+
+	pm8921_bms_get_simultaneous_battery_voltage_and_current(&ichg_meas_ua,
+								&vbat_uv);
+	if (ichg_meas_ua >= 0) {
+		pr_debug("Exiting ichg_meas_ua = %d > 0\n", ichg_meas_ua);
+		return;
+	}
+	if (ichg_meas_ua <= ichg_threshold_ua) {
+		pr_debug("Exiting ichg_meas_ua = %d < ichg_threshold_ua = %d\n",
+					ichg_meas_ua, ichg_threshold_ua);
+		return;
+	}
+	ichg_meas_ma = ichg_meas_ua / 1000;
+
+	/* rconn_mohm is in milliOhms */
+	vbat_batt_terminal_uv = vbat_uv + ichg_meas_ma * the_chip->rconn_mohm;
+	vbat_batt_terminal_mv = vbat_batt_terminal_uv/1000;
+	pm_chg_vddmax_get(the_chip, &programmed_vdd_max);
+
+	delta_mv =  chip->max_voltage_mv - vbat_batt_terminal_mv;
+
+	adj_vdd_max_mv = programmed_vdd_max + delta_mv;
+	pr_debug("vdd_max needs to be changed by %d mv from %d to %d\n",
+			delta_mv,
+			programmed_vdd_max,
+			adj_vdd_max_mv);
+
+	if (adj_vdd_max_mv < chip->max_voltage_mv) {
+		pr_debug("adj vdd_max lower than default max voltage\n");
+		return;
+	}
+
+	if (adj_vdd_max_mv > (chip->max_voltage_mv + vdd_max_increase_mv))
+		adj_vdd_max_mv = chip->max_voltage_mv + vdd_max_increase_mv;
+
+	pr_debug("adjusting vdd_max_mv to %d to make "
+		"vbat_batt_termial_uv = %d to %d\n",
+		adj_vdd_max_mv, vbat_batt_terminal_uv, chip->max_voltage_mv);
+	pm_chg_vddmax_set(chip, adj_vdd_max_mv);
+}
+
+enum {
+	CHG_IN_PROGRESS,
+	CHG_NOT_IN_PROGRESS,
+	CHG_FINISHED,
+};
+
+#define VBAT_TOLERANCE_MV	70
+#define CHG_DISABLE_MSLEEP	100
+static int is_charging_finished(struct pm8921_chg_chip *chip)
+{
+	int vbat_meas_uv, vbat_meas_mv, vbat_programmed, vbatdet_low;
+	int ichg_meas_ma, iterm_programmed;
+	int regulation_loop, fast_chg, vcp;
+	int rc;
+	static int last_vbat_programmed = -EINVAL;
+
+	if (!is_ext_charging(chip)) {
+		/* return if the battery is not being fastcharged */
+		fast_chg = pm_chg_get_rt_status(chip, FASTCHG_IRQ);
+		pr_debug("fast_chg = %d\n", fast_chg);
+		if (fast_chg == 0)
+			return CHG_NOT_IN_PROGRESS;
+
+		vcp = pm_chg_get_rt_status(chip, VCP_IRQ);
+		pr_debug("vcp = %d\n", vcp);
+		if (vcp == 1)
+			return CHG_IN_PROGRESS;
+
+		vbatdet_low = pm_chg_get_rt_status(chip, VBATDET_LOW_IRQ);
+		pr_debug("vbatdet_low = %d\n", vbatdet_low);
+		if (vbatdet_low == 1)
+			return CHG_IN_PROGRESS;
+
+		/* reset count if battery is hot/cold */
+		rc = pm_chg_get_rt_status(chip, BAT_TEMP_OK_IRQ);
+		pr_debug("batt_temp_ok = %d\n", rc);
+		if (rc == 0)
+			return CHG_IN_PROGRESS;
+
+		/* reset count if battery voltage is less than vddmax */
+		vbat_meas_uv = get_prop_battery_uvolts(chip);
+		if (vbat_meas_uv < 0)
+			return CHG_IN_PROGRESS;
+		vbat_meas_mv = vbat_meas_uv / 1000;
+
+		rc = pm_chg_vddmax_get(chip, &vbat_programmed);
+		if (rc) {
+			pr_err("couldnt read vddmax rc = %d\n", rc);
+			return CHG_IN_PROGRESS;
+		}
+		pr_debug("vddmax = %d vbat_meas_mv=%d\n",
+			 vbat_programmed, vbat_meas_mv);
+
+		if (last_vbat_programmed == -EINVAL)
+			last_vbat_programmed = vbat_programmed;
+		if (last_vbat_programmed !=  vbat_programmed) {
+			/* vddmax changed, reset and check again */
+			pr_debug("vddmax = %d last_vdd_max=%d\n",
+				 vbat_programmed, last_vbat_programmed);
+			last_vbat_programmed = vbat_programmed;
+			return CHG_IN_PROGRESS;
+		}
+
+		regulation_loop = pm_chg_get_regulation_loop(chip);
+		if (regulation_loop < 0) {
+			pr_err("couldnt read the regulation loop err=%d\n",
+				regulation_loop);
+			return CHG_IN_PROGRESS;
+		}
+		pr_debug("regulation_loop=%d\n", regulation_loop);
+
+		if (regulation_loop != 0 && regulation_loop != VDD_LOOP)
+			return CHG_IN_PROGRESS;
+	} /* !is_ext_charging */
+
+	/* reset count if battery chg current is more than iterm */
+	rc = pm_chg_iterm_get(chip, &iterm_programmed);
+	if (rc) {
+		pr_err("couldnt read iterm rc = %d\n", rc);
+		return CHG_IN_PROGRESS;
+	}
+
+	ichg_meas_ma = (get_prop_batt_current(chip)) / 1000;
+	pr_debug("iterm_programmed = %d ichg_meas_ma=%d\n",
+				iterm_programmed, ichg_meas_ma);
+	/*
+	 * ichg_meas_ma < 0 means battery is drawing current
+	 * ichg_meas_ma > 0 means battery is providing current
+	 */
+	if (ichg_meas_ma > 0)
+		return CHG_IN_PROGRESS;
+
+	if (ichg_meas_ma * -1 > iterm_programmed)
+		return CHG_IN_PROGRESS;
+
+	return CHG_FINISHED;
+}
+
+/**
+ * eoc_worker - internal function to check if battery EOC
+ *		has happened
+ *
+ * If all conditions favouring, if the charge current is
+ * less than the term current for three consecutive times
+ * an EOC has happened.
+ * The wakelock is released if there is no need to reshedule
+ * - this happens when the battery is removed or EOC has
+ * happened
+ */
+#define CONSECUTIVE_COUNT	3
+static void eoc_worker(struct work_struct *work)
+{
+	struct delayed_work *dwork = to_delayed_work(work);
+	struct pm8921_chg_chip *chip = container_of(dwork,
+				struct pm8921_chg_chip, eoc_work);
+	static int count;
+	int end;
+
+	pm_chg_failed_clear(chip, 1);
+	end = is_charging_finished(chip);
+
+	if (end == CHG_NOT_IN_PROGRESS) {
+		count = 0;
+		wake_unlock(&chip->eoc_wake_lock);
+		return;
+	}
+
+	if (end == CHG_FINISHED) {
+		count++;
+	} else {
+		count = 0;
+	}
+
+	if (count == CONSECUTIVE_COUNT) {
+		count = 0;
+		pr_info("End of Charging\n");
+
+		pm_chg_auto_enable(chip, 0);
+
+		if (is_ext_charging(chip))
+			chip->ext_charge_done = true;
+
+		if (chip->is_bat_warm || chip->is_bat_cool)
+			chip->bms_notify.is_battery_full = 0;
+		else
+			chip->bms_notify.is_battery_full = 1;
+		/* declare end of charging by invoking chgdone interrupt */
+		chgdone_irq_handler(chip->pmic_chg_irq[CHGDONE_IRQ], chip);
+		wake_unlock(&chip->eoc_wake_lock);
+	} else {
+		adjust_vdd_max_for_fastchg(chip);
+		pr_debug("EOC count = %d\n", count);
+		schedule_delayed_work(&chip->eoc_work,
+			      round_jiffies_relative(msecs_to_jiffies
+						     (EOC_CHECK_PERIOD_MS)));
+	}
+}
+
+static void btm_configure_work(struct work_struct *work)
+{
+	int rc;
+
+	rc = pm8xxx_adc_btm_configure(&btm_config);
+	if (rc)
+		pr_err("failed to configure btm rc=%d", rc);
+}
+
+DECLARE_WORK(btm_config_work, btm_configure_work);
+
+static void set_appropriate_battery_current(struct pm8921_chg_chip *chip)
+{
+	unsigned int chg_current = chip->max_bat_chg_current;
+
+	if (chip->is_bat_cool)
+		chg_current = min(chg_current, chip->cool_bat_chg_current);
+
+	if (chip->is_bat_warm)
+		chg_current = min(chg_current, chip->warm_bat_chg_current);
+
+	if (thermal_mitigation != 0 && chip->thermal_mitigation)
+		chg_current = min(chg_current,
+				chip->thermal_mitigation[thermal_mitigation]);
+
+	pm_chg_ibatmax_set(the_chip, chg_current);
+}
+
+#define TEMP_HYSTERISIS_DEGC 2
+static void battery_cool(bool enter)
+{
+	pr_debug("enter = %d\n", enter);
+	if (enter == the_chip->is_bat_cool)
+		return;
+	the_chip->is_bat_cool = enter;
+	if (enter) {
+		btm_config.low_thr_temp =
+			the_chip->cool_temp_dc + TEMP_HYSTERISIS_DEGC;
+		set_appropriate_battery_current(the_chip);
+		pm_chg_vddmax_set(the_chip, the_chip->cool_bat_voltage);
+		pm_chg_vbatdet_set(the_chip,
+			the_chip->cool_bat_voltage
+			- the_chip->resume_voltage_delta);
+	} else {
+		btm_config.low_thr_temp = the_chip->cool_temp_dc;
+		set_appropriate_battery_current(the_chip);
+		pm_chg_vddmax_set(the_chip, the_chip->max_voltage_mv);
+		pm_chg_vbatdet_set(the_chip,
+			the_chip->max_voltage_mv
+			- the_chip->resume_voltage_delta);
+	}
+	schedule_work(&btm_config_work);
+}
+
+static void battery_warm(bool enter)
+{
+	pr_debug("enter = %d\n", enter);
+	if (enter == the_chip->is_bat_warm)
+		return;
+	the_chip->is_bat_warm = enter;
+	if (enter) {
+		btm_config.high_thr_temp =
+			the_chip->warm_temp_dc - TEMP_HYSTERISIS_DEGC;
+		set_appropriate_battery_current(the_chip);
+		pm_chg_vddmax_set(the_chip, the_chip->warm_bat_voltage);
+		pm_chg_vbatdet_set(the_chip,
+			the_chip->warm_bat_voltage
+			- the_chip->resume_voltage_delta);
+	} else {
+		btm_config.high_thr_temp = the_chip->warm_temp_dc;
+		set_appropriate_battery_current(the_chip);
+		pm_chg_vddmax_set(the_chip, the_chip->max_voltage_mv);
+		pm_chg_vbatdet_set(the_chip,
+			the_chip->max_voltage_mv
+			- the_chip->resume_voltage_delta);
+	}
+	schedule_work(&btm_config_work);
+}
+
+static int configure_btm(struct pm8921_chg_chip *chip)
+{
+	int rc;
+
+	if (chip->warm_temp_dc != INT_MIN)
+		btm_config.btm_warm_fn = battery_warm;
+	else
+		btm_config.btm_warm_fn = NULL;
+
+	if (chip->cool_temp_dc != INT_MIN)
+		btm_config.btm_cool_fn = battery_cool;
+	else
+		btm_config.btm_cool_fn = NULL;
+
+	btm_config.low_thr_temp = chip->cool_temp_dc;
+	btm_config.high_thr_temp = chip->warm_temp_dc;
+	btm_config.interval = chip->temp_check_period;
+	rc = pm8xxx_adc_btm_configure(&btm_config);
+	if (rc)
+		pr_err("failed to configure btm rc = %d\n", rc);
+	rc = pm8xxx_adc_btm_start();
+	if (rc)
+		pr_err("failed to start btm rc = %d\n", rc);
+
+	return rc;
+}
+
+/**
+ * set_disable_status_param -
+ *
+ * Internal function to disable battery charging and also disable drawing
+ * any current from the source. The device is forced to run on a battery
+ * after this.
+ */
+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 int rconn_mohm;
+static int set_rconn_mohm(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;
+	}
+	if (chip)
+		chip->rconn_mohm = rconn_mohm;
+	return 0;
+}
+module_param_call(rconn_mohm, set_rconn_mohm, param_get_uint,
+					&rconn_mohm, 0644);
+/**
+ * set_thermal_mitigation_level -
+ *
+ * Internal function to control battery charging current to reduce
+ * temperature
+ */
+static int set_therm_mitigation_level(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;
+	}
+
+	if (!chip) {
+		pr_err("called before init\n");
+		return -EINVAL;
+	}
+
+	if (!chip->thermal_mitigation) {
+		pr_err("no thermal mitigation\n");
+		return -EINVAL;
+	}
+
+	if (thermal_mitigation < 0
+		|| thermal_mitigation >= chip->thermal_levels) {
+		pr_err("out of bound level selected\n");
+		return -EINVAL;
+	}
+
+	set_appropriate_battery_current(chip);
+	return ret;
+}
+module_param_call(thermal_mitigation, set_therm_mitigation_level,
+					param_get_uint,
+					&thermal_mitigation, 0644);
+
+static int set_usb_max_current(const char *val, struct kernel_param *kp)
+{
+	int ret, mA;
+	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;
+	}
+	if (chip) {
+		pr_warn("setting current max to %d\n", usb_max_current);
+		pm_chg_iusbmax_get(chip, &mA);
+		if (mA > usb_max_current)
+			pm8921_charger_vbus_draw(usb_max_current);
+		return 0;
+	}
+	return -EINVAL;
+}
+module_param_call(usb_max_current, set_usb_max_current,
+	param_get_uint, &usb_max_current, 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 */
+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);
+	if (chip->usb_present) {
+		schedule_delayed_work(&chip->unplug_check_work,
+			round_jiffies_relative(msecs_to_jiffies
+				(UNPLUG_CHECK_WAIT_PERIOD_MS)));
+		pm8921_chg_enable_irq(chip, CHG_GONE_IRQ);
+	}
+
+	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_INSERTED_IRQ);
+	pm8921_chg_enable_irq(chip, USBIN_OV_IRQ);
+	pm8921_chg_enable_irq(chip, USBIN_UV_IRQ);
+	pm8921_chg_enable_irq(chip, DCIN_OV_IRQ);
+	pm8921_chg_enable_irq(chip, DCIN_UV_IRQ);
+	pm8921_chg_enable_irq(chip, CHGFAIL_IRQ);
+	pm8921_chg_enable_irq(chip, FASTCHG_IRQ);
+	pm8921_chg_enable_irq(chip, VBATDET_LOW_IRQ);
+	pm8921_chg_enable_irq(chip, BAT_TEMP_OK_IRQ);
+
+	spin_lock_irqsave(&vbus_lock, flags);
+	if (usb_chg_current) {
+		/* reissue a vbus draw call */
+		__pm8921_charger_vbus_draw(usb_chg_current);
+		fastchg_irq_handler(chip->pmic_chg_irq[FASTCHG_IRQ], chip);
+	}
+	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();
+	}
+
+	check_battery_valid(chip);
+
+	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, 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 | IRQF_TRIGGER_FALLING,
+						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 | IRQF_TRIGGER_FALLING,
+						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 | IRQF_TRIGGER_FALLING,
+						chg_gone_irq_handler),
+	CHG_IRQ(BAT_TEMP_OK_IRQ, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+						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, 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;
+		}
+		chip->pmic_chg_irq[chg_irq_data[i].irq_id] = res->start;
+		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);
+			chip->pmic_chg_irq[chg_irq_data[i].irq_id] = 0;
+			goto err_out;
+		}
+		pm8921_chg_disable_irq(chip, chg_irq_data[i].irq_id);
+	}
+	return 0;
+
+err_out:
+	free_irqs(chip);
+	return -EINVAL;
+}
+
+static void pm8921_chg_force_19p2mhz_clk(struct pm8921_chg_chip *chip)
+{
+	int err;
+	u8 temp;
+
+	temp  = 0xD1;
+	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;
+	}
+
+	temp  = 0xD3;
+	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;
+	}
+
+	temp  = 0xD1;
+	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;
+	}
+
+	temp  = 0xD5;
+	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;
+	}
+
+	udelay(183);
+
+	temp  = 0xD1;
+	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;
+	}
+
+	temp  = 0xD0;
+	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;
+	}
+	udelay(32);
+
+	temp  = 0xD1;
+	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;
+	}
+
+	temp  = 0xD3;
+	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;
+	}
+}
+
+static void pm8921_chg_set_hw_clk_switching(struct pm8921_chg_chip *chip)
+{
+	int err;
+	u8 temp;
+
+	temp  = 0xD1;
+	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;
+	}
+
+	temp  = 0xD0;
+	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;
+	}
+}
+
+#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
+#define VREF_BATT_THERM_FORCE_ON	BIT(7)
+static int __devinit pm8921_chg_hw_init(struct pm8921_chg_chip *chip)
+{
+	int rc;
+	int vdd_safe;
+
+	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;
+	}
+
+	vdd_safe = chip->max_voltage_mv + VDD_MAX_INCREASE_MV;
+
+	if (vdd_safe > PM8921_CHG_VDDSAFE_MAX)
+		vdd_safe = PM8921_CHG_VDDSAFE_MAX;
+
+	rc = pm_chg_vddsafe_set(chip, vdd_safe);
+
+	if (rc) {
+		pr_err("Failed to set safe voltage to %d rc=%d\n",
+						chip->max_voltage_mv, rc);
+		return rc;
+	}
+	rc = pm_chg_vbatdet_set(chip,
+				chip->max_voltage_mv
+				- chip->resume_voltage_delta);
+	if (rc) {
+		pr_err("Failed to set vbatdet comprator voltage to %d rc=%d\n",
+			chip->max_voltage_mv - chip->resume_voltage_delta, rc);
+		return rc;
+	}
+
+	rc = pm_chg_vddmax_set(chip, chip->max_voltage_mv);
+	if (rc) {
+		pr_err("Failed to set max voltage to %d rc=%d\n",
+						chip->max_voltage_mv, 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;
+	}
+
+	rc = pm_chg_ibatmax_set(chip, chip->max_bat_chg_current);
+	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;
+	}
+
+	if (chip->safety_time != 0) {
+		rc = pm_chg_tchg_max_set(chip, chip->safety_time);
+		if (rc) {
+			pr_err("Failed to set max time to %d minutes rc=%d\n",
+							chip->safety_time, rc);
+			return rc;
+		}
+	}
+
+	if (chip->ttrkl_time != 0) {
+		rc = pm_chg_ttrkl_max_set(chip, chip->ttrkl_time);
+		if (rc) {
+			pr_err("Failed to set trkl time to %d minutes rc=%d\n",
+							chip->safety_time, rc);
+			return rc;
+		}
+	}
+
+	if (chip->vin_min != 0) {
+		rc = pm_chg_vinmin_set(chip, chip->vin_min);
+		if (rc) {
+			pr_err("Failed to set vin min to %d mV rc=%d\n",
+							chip->vin_min, rc);
+			return rc;
+		}
+	} else {
+		chip->vin_min = pm_chg_vinmin_get(chip);
+	}
+
+	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;
+	}
+
+	if (chip->trkl_voltage != 0) {
+		rc = pm_chg_vtrkl_low_set(chip, chip->trkl_voltage);
+		if (rc) {
+			pr_err("Failed to set trkl voltage to %dmv  rc=%d\n",
+							chip->trkl_voltage, rc);
+			return rc;
+		}
+	}
+
+	if (chip->weak_voltage != 0) {
+		rc = pm_chg_vweak_set(chip, chip->weak_voltage);
+		if (rc) {
+			pr_err("Failed to set weak voltage to %dmv  rc=%d\n",
+							chip->weak_voltage, rc);
+			return rc;
+		}
+	}
+
+	if (chip->trkl_current != 0) {
+		rc = pm_chg_itrkl_set(chip, chip->trkl_current);
+		if (rc) {
+			pr_err("Failed to set trkl current to %dmA  rc=%d\n",
+							chip->trkl_voltage, rc);
+			return rc;
+		}
+	}
+
+	if (chip->weak_current != 0) {
+		rc = pm_chg_iweak_set(chip, chip->weak_current);
+		if (rc) {
+			pr_err("Failed to set weak current to %dmA  rc=%d\n",
+							chip->weak_current, rc);
+			return rc;
+		}
+	}
+
+	rc = pm_chg_batt_cold_temp_config(chip, chip->cold_thr);
+	if (rc) {
+		pr_err("Failed to set cold config %d  rc=%d\n",
+						chip->cold_thr, rc);
+	}
+
+	rc = pm_chg_batt_hot_temp_config(chip, chip->hot_thr);
+	if (rc) {
+		pr_err("Failed to set hot config %d  rc=%d\n",
+						chip->hot_thr, rc);
+	}
+
+	rc = pm_chg_led_src_config(chip, chip->led_src_config);
+	if (rc) {
+		pr_err("Failed to set charger LED src config %d  rc=%d\n",
+						chip->led_src_config, 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, 0xCE);
+		pm8xxx_writeb(chip->dev->parent, CHG_BUCK_CTRL_TEST3, 0xD8);
+
+		/* software workaround for correct battery_id detection */
+		pm8xxx_writeb(chip->dev->parent, PSI_TXRX_SAMPLE_DATA_0, 0xFF);
+		pm8xxx_writeb(chip->dev->parent, PSI_TXRX_SAMPLE_DATA_1, 0xFF);
+		pm8xxx_writeb(chip->dev->parent, PSI_TXRX_SAMPLE_DATA_2, 0xFF);
+		pm8xxx_writeb(chip->dev->parent, PSI_TXRX_SAMPLE_DATA_3, 0xFF);
+		pm8xxx_writeb(chip->dev->parent, PSI_CONFIG_STATUS, 0x0D);
+		udelay(100);
+		pm8xxx_writeb(chip->dev->parent, PSI_CONFIG_STATUS, 0x0C);
+	}
+
+	/* Workarounds for die 3.0 */
+	if (pm8xxx_get_revision(chip->dev->parent) == PM8XXX_REVISION_8921_3p0)
+		pm8xxx_writeb(chip->dev->parent, CHG_BUCK_CTRL_TEST3, 0xAC);
+
+	/* Enable isub_fine resolution AICL for PM8917 */
+	if (pm8xxx_get_version(chip->dev->parent) == PM8XXX_VERSION_8917)
+		chip->iusb_fine_res = true;
+
+	pm8xxx_writeb(chip->dev->parent, CHG_BUCK_CTRL_TEST3, 0xD9);
+
+	/* Disable EOC FSM processing */
+	pm8xxx_writeb(chip->dev->parent, CHG_BUCK_CTRL_TEST3, 0x91);
+
+	pm8921_chg_force_19p2mhz_clk(chip);
+
+	rc = pm_chg_masked_write(chip, CHG_CNTRL, VREF_BATT_THERM_FORCE_ON,
+						VREF_BATT_THERM_FORCE_ON);
+	if (rc)
+		pr_err("Failed to Force Vref therm rc=%d\n", rc);
+
+	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_loop(void *data, u64 * val)
+{
+	u8 temp;
+
+	if (!the_chip) {
+		pr_err("%s called before init\n", __func__);
+		return -EINVAL;
+	}
+	temp = pm_chg_get_regulation_loop(the_chip);
+	*val = temp;
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(reg_loop_fops, get_reg_loop, NULL, "0x%02llx\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");
+
+enum {
+	BAT_WARM_ZONE,
+	BAT_COOL_ZONE,
+};
+static int get_warm_cool(void *data, u64 * val)
+{
+	if (!the_chip) {
+		pr_err("%s called before init\n", __func__);
+		return -EINVAL;
+	}
+	if ((int)data == BAT_WARM_ZONE)
+		*val = the_chip->is_bat_warm;
+	if ((int)data == BAT_COOL_ZONE)
+		*val = the_chip->is_bat_cool;
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(warm_cool_fops, get_warm_cool, NULL, "0x%lld\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);
+
+	debugfs_create_file("REGULATION_LOOP_CONTROL", 0644, chip->dent, NULL,
+			    &reg_loop_fops);
+
+	debugfs_create_file("BAT_WARM_ZONE", 0644, chip->dent,
+				(void *)BAT_WARM_ZONE, &warm_cool_fops);
+	debugfs_create_file("BAT_COOL_ZONE", 0644, chip->dent,
+				(void *)BAT_COOL_ZONE, &warm_cool_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 pm8921_charger_suspend_noirq(struct device *dev)
+{
+	int rc;
+	struct pm8921_chg_chip *chip = dev_get_drvdata(dev);
+
+	rc = pm_chg_masked_write(chip, CHG_CNTRL, VREF_BATT_THERM_FORCE_ON, 0);
+	if (rc)
+		pr_err("Failed to Force Vref therm off rc=%d\n", rc);
+	pm8921_chg_set_hw_clk_switching(chip);
+	return 0;
+}
+
+static int pm8921_charger_resume_noirq(struct device *dev)
+{
+	int rc;
+	struct pm8921_chg_chip *chip = dev_get_drvdata(dev);
+
+	pm8921_chg_force_19p2mhz_clk(chip);
+
+	rc = pm_chg_masked_write(chip, CHG_CNTRL, VREF_BATT_THERM_FORCE_ON,
+						VREF_BATT_THERM_FORCE_ON);
+	if (rc)
+		pr_err("Failed to Force Vref therm on rc=%d\n", rc);
+	return 0;
+}
+
+static int pm8921_charger_resume(struct device *dev)
+{
+	int rc;
+	struct pm8921_chg_chip *chip = dev_get_drvdata(dev);
+
+	if (!(chip->cool_temp_dc == INT_MIN && chip->warm_temp_dc == INT_MIN)
+		&& !(chip->keep_btm_on_suspend)) {
+		rc = pm8xxx_adc_btm_configure(&btm_config);
+		if (rc)
+			pr_err("couldn't reconfigure btm rc=%d\n", rc);
+
+		rc = pm8xxx_adc_btm_start();
+		if (rc)
+			pr_err("couldn't restart btm rc=%d\n", rc);
+	}
+	if (pm8921_chg_is_enabled(chip, LOOP_CHANGE_IRQ)) {
+		disable_irq_wake(chip->pmic_chg_irq[LOOP_CHANGE_IRQ]);
+		pm8921_chg_disable_irq(chip, LOOP_CHANGE_IRQ);
+	}
+	return 0;
+}
+
+static int pm8921_charger_suspend(struct device *dev)
+{
+	int rc;
+	struct pm8921_chg_chip *chip = dev_get_drvdata(dev);
+
+	if (!(chip->cool_temp_dc == INT_MIN && chip->warm_temp_dc == INT_MIN)
+		&& !(chip->keep_btm_on_suspend)) {
+		rc = pm8xxx_adc_btm_end();
+		if (rc)
+			pr_err("Failed to disable BTM on suspend rc=%d\n", rc);
+	}
+
+	if (is_usb_chg_plugged_in(chip)) {
+		pm8921_chg_enable_irq(chip, LOOP_CHANGE_IRQ);
+		enable_irq_wake(chip->pmic_chg_irq[LOOP_CHANGE_IRQ]);
+	}
+
+	return 0;
+}
+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->ttrkl_time = pdata->ttrkl_time;
+	chip->update_time = pdata->update_time;
+	chip->max_voltage_mv = pdata->max_voltage;
+	chip->min_voltage_mv = pdata->min_voltage;
+	chip->resume_voltage_delta = pdata->resume_voltage_delta;
+	chip->term_current = pdata->term_current;
+	chip->vbat_channel = pdata->charger_cdata.vbat_channel;
+	chip->batt_temp_channel = pdata->charger_cdata.batt_temp_channel;
+	chip->batt_id_channel = pdata->charger_cdata.batt_id_channel;
+	chip->batt_id_min = pdata->batt_id_min;
+	chip->batt_id_max = pdata->batt_id_max;
+	if (pdata->cool_temp != INT_MIN)
+		chip->cool_temp_dc = pdata->cool_temp * 10;
+	else
+		chip->cool_temp_dc = INT_MIN;
+
+	if (pdata->warm_temp != INT_MIN)
+		chip->warm_temp_dc = pdata->warm_temp * 10;
+	else
+		chip->warm_temp_dc = INT_MIN;
+
+	chip->temp_check_period = pdata->temp_check_period;
+	chip->max_bat_chg_current = pdata->max_bat_chg_current;
+	chip->cool_bat_chg_current = pdata->cool_bat_chg_current;
+	chip->warm_bat_chg_current = pdata->warm_bat_chg_current;
+	chip->cool_bat_voltage = pdata->cool_bat_voltage;
+	chip->warm_bat_voltage = pdata->warm_bat_voltage;
+	chip->keep_btm_on_suspend = pdata->keep_btm_on_suspend;
+	chip->trkl_voltage = pdata->trkl_voltage;
+	chip->weak_voltage = pdata->weak_voltage;
+	chip->trkl_current = pdata->trkl_current;
+	chip->weak_current = pdata->weak_current;
+	chip->vin_min = pdata->vin_min;
+	chip->thermal_mitigation = pdata->thermal_mitigation;
+	chip->thermal_levels = pdata->thermal_levels;
+
+	chip->cold_thr = pdata->cold_thr;
+	chip->hot_thr = pdata->hot_thr;
+	chip->rconn_mohm = pdata->rconn_mohm;
+	chip->led_src_config = pdata->led_src_config;
+
+	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_usb,
+	chip->usb_psy.num_properties = ARRAY_SIZE(pm_power_props_usb),
+	chip->usb_psy.get_property = pm_power_get_property_usb,
+
+	chip->dc_psy.name = "pm8921-dc",
+	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_mains,
+	chip->dc_psy.num_properties = ARRAY_SIZE(pm_power_props_mains),
+	chip->dc_psy.get_property = pm_power_get_property_mains,
+
+	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,
+	chip->batt_psy.external_power_changed = pm_batt_external_power_changed,
+	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_chip;
+	}
+
+	rc = power_supply_register(chip->dev, &chip->dc_psy);
+	if (rc < 0) {
+		pr_err("power_supply_register usb 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;
+	}
+
+	platform_set_drvdata(pdev, chip);
+	the_chip = chip;
+
+	wake_lock_init(&chip->eoc_wake_lock, WAKE_LOCK_SUSPEND, "pm8921_eoc");
+	INIT_DELAYED_WORK(&chip->eoc_work, eoc_worker);
+	INIT_DELAYED_WORK(&chip->vin_collapse_check_work,
+						vin_collapse_check_worker);
+	INIT_DELAYED_WORK(&chip->unplug_check_work, unplug_check_worker);
+
+	rc = request_irqs(chip, pdev);
+	if (rc) {
+		pr_err("couldn't register interrupts rc=%d\n", rc);
+		goto unregister_batt;
+	}
+
+	enable_irq_wake(chip->pmic_chg_irq[USBIN_VALID_IRQ]);
+	enable_irq_wake(chip->pmic_chg_irq[USBIN_OV_IRQ]);
+	enable_irq_wake(chip->pmic_chg_irq[USBIN_UV_IRQ]);
+	enable_irq_wake(chip->pmic_chg_irq[BAT_TEMP_OK_IRQ]);
+	enable_irq_wake(chip->pmic_chg_irq[VBATDET_LOW_IRQ]);
+	enable_irq_wake(chip->pmic_chg_irq[FASTCHG_IRQ]);
+	/*
+	 * if both the cool_temp_dc and warm_temp_dc are invalid device doesnt
+	 * care for jeita compliance
+	 */
+	if (!(chip->cool_temp_dc == INT_MIN && chip->warm_temp_dc == INT_MIN)) {
+		rc = configure_btm(chip);
+		if (rc) {
+			pr_err("couldn't register with btm rc=%d\n", rc);
+			goto free_irq;
+		}
+	}
+
+	create_debugfs_entries(chip);
+
+	INIT_WORK(&chip->bms_notify.work, bms_notify);
+	INIT_WORK(&chip->battery_id_valid_work, battery_id_valid);
+
+	/* determine what state the charger is in */
+	determine_initial_state(chip);
+
+	if (chip->update_time) {
+		INIT_DELAYED_WORK(&chip->update_heartbeat_work,
+							update_heartbeat);
+		schedule_delayed_work(&chip->update_heartbeat_work,
+				      round_jiffies_relative(msecs_to_jiffies
+							(chip->update_time)));
+	}
+	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 const struct dev_pm_ops pm8921_pm_ops = {
+	.suspend	= pm8921_charger_suspend,
+	.suspend_noirq  = pm8921_charger_suspend_noirq,
+	.resume_noirq   = pm8921_charger_resume_noirq,
+	.resume		= pm8921_charger_resume,
+};
+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,
+			.pm	= &pm8921_pm_ops,
+	},
+};
+
+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/pm8xxx-ccadc.c b/drivers/power/pm8xxx-ccadc.c
new file mode 100644
index 0000000..ce72a5b
--- /dev/null
+++ b/drivers/power/pm8xxx-ccadc.c
@@ -0,0 +1,734 @@
+/* Copyright (c) 2011-2012, 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/core.h>
+#include <linux/mfd/pm8xxx/ccadc.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/debugfs.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+
+#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 CCADC_FULLSCALE_TRIM1	0x34C
+#define CCADC_FULLSCALE_TRIM0	0x34D
+
+/* note : TRIM1 is the msb and TRIM0 is the lsb */
+#define ADC_ARB_SECP_CNTRL	0x190
+#define ADC_ARB_SECP_AMUX_CNTRL	0x191
+#define ADC_ARB_SECP_ANA_PARAM	0x192
+#define ADC_ARB_SECP_DIG_PARAM	0x193
+#define ADC_ARB_SECP_RSV	0x194
+#define ADC_ARB_SECP_DATA1	0x195
+#define ADC_ARB_SECP_DATA0	0x196
+
+#define ADC_ARB_BMS_CNTRL	0x18D
+
+#define START_CONV_BIT	BIT(7)
+#define EOC_CONV_BIT	BIT(6)
+#define SEL_CCADC_BIT	BIT(1)
+#define EN_ARB_BIT	BIT(0)
+
+#define CCADC_CALIB_DIG_PARAM	0xE3
+#define CCADC_CALIB_RSV_GND	0x40
+#define CCADC_CALIB_RSV_25MV	0x80
+#define CCADC_CALIB_ANA_PARAM	0x1B
+#define SAMPLE_COUNT		16
+#define ADC_WAIT_COUNT		10
+
+#define CCADC_MAX_25MV		30000
+#define CCADC_MIN_25MV		20000
+#define CCADC_MAX_0UV		-4000
+#define CCADC_MIN_0UV		-7000
+
+#define CCADC_INTRINSIC_OFFSET  0xC000
+
+struct pm8xxx_ccadc_chip {
+	struct device		*dev;
+	struct dentry		*dent;
+	u16			ccadc_offset;
+	int			ccadc_gain_uv;
+	unsigned int		revision;
+	int			eoc_irq;
+	int			r_sense;
+};
+
+static struct pm8xxx_ccadc_chip *the_chip;
+
+#ifdef DEBUG
+static s64 microvolt_to_ccadc_reading_v1(s64 uv)
+{
+	return div_s64(uv * CCADC_READING_RESOLUTION_D_V1,
+				CCADC_READING_RESOLUTION_N_V1);
+}
+
+static s64 microvolt_to_ccadc_reading_v2(s64 uv)
+{
+	return div_s64(uv * CCADC_READING_RESOLUTION_D_V2,
+				CCADC_READING_RESOLUTION_N_V2);
+}
+
+static s64 microvolt_to_ccadc_reading(struct pm8xxx_ccadc_chip *chip, s64 cc)
+{
+	/*
+	 * resolution (the value of a single bit) was changed after revision 2.0
+	 * for more accurate readings
+	 */
+	return (the_chip->revision < PM8XXX_REVISION_8921_2p0) ?
+				microvolt_to_ccadc_reading_v1((s64)cc) :
+				microvolt_to_ccadc_reading_v2((s64)cc);
+}
+#endif
+
+static int cc_adjust_for_offset(u16 raw)
+{
+	/* this has the intrinsic offset */
+	return (int)raw - the_chip->ccadc_offset;
+}
+
+#define GAIN_REFERENCE_UV 25000
+/*
+ * gain compensation for ccadc readings - common for vsense based and
+ * couloumb counter based readings
+ */
+s64 pm8xxx_cc_adjust_for_gain(s64 uv)
+{
+	if (the_chip == NULL || the_chip->ccadc_gain_uv == 0)
+		return uv;
+
+	return div_s64(uv * GAIN_REFERENCE_UV, the_chip->ccadc_gain_uv);
+}
+EXPORT_SYMBOL(pm8xxx_cc_adjust_for_gain);
+
+static int pm_ccadc_masked_write(struct pm8xxx_ccadc_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 REG_SBI_CONFIG		0x04F
+#define PAGE3_ENABLE_MASK	0x6
+static int calib_ccadc_enable_trim_access(struct pm8xxx_ccadc_chip *chip,
+								u8 *sbi_config)
+{
+	u8 reg;
+	int rc;
+
+	rc = pm8xxx_readb(chip->dev->parent, REG_SBI_CONFIG, sbi_config);
+	if (rc) {
+		pr_err("error = %d reading sbi config reg\n", rc);
+		return rc;
+	}
+
+	reg = *sbi_config | PAGE3_ENABLE_MASK;
+	return pm8xxx_writeb(chip->dev->parent, REG_SBI_CONFIG, reg);
+}
+
+static int calib_ccadc_restore_trim_access(struct pm8xxx_ccadc_chip *chip,
+							u8 sbi_config)
+{
+	return pm8xxx_writeb(chip->dev->parent, REG_SBI_CONFIG, sbi_config);
+}
+
+static int calib_ccadc_enable_arbiter(struct pm8xxx_ccadc_chip *chip)
+{
+	int rc;
+
+	/* enable Arbiter, must be sent twice */
+	rc = pm_ccadc_masked_write(chip, ADC_ARB_SECP_CNTRL,
+			SEL_CCADC_BIT | EN_ARB_BIT, SEL_CCADC_BIT | EN_ARB_BIT);
+	if (rc < 0) {
+		pr_err("error = %d enabling arbiter for offset\n", rc);
+		return rc;
+	}
+	rc = pm_ccadc_masked_write(chip, ADC_ARB_SECP_CNTRL,
+			SEL_CCADC_BIT | EN_ARB_BIT, SEL_CCADC_BIT | EN_ARB_BIT);
+	if (rc < 0) {
+		pr_err("error = %d writing ADC_ARB_SECP_CNTRL\n", rc);
+		return rc;
+	}
+	return 0;
+}
+
+static int calib_start_conv(struct pm8xxx_ccadc_chip *chip,
+					u16 *result)
+{
+	int rc, i;
+	u8 data_msb, data_lsb, reg;
+
+	/* Start conversion */
+	rc = pm_ccadc_masked_write(chip, ADC_ARB_SECP_CNTRL,
+					START_CONV_BIT, START_CONV_BIT);
+	if (rc < 0) {
+		pr_err("error = %d starting offset meas\n", rc);
+		return rc;
+	}
+
+	/* Wait for End of conversion */
+	for (i = 0; i < ADC_WAIT_COUNT; i++) {
+		rc = pm8xxx_readb(chip->dev->parent,
+					ADC_ARB_SECP_CNTRL, &reg);
+		if (rc < 0) {
+			pr_err("error = %d read eoc for offset\n", rc);
+			return rc;
+		}
+		if ((reg & (START_CONV_BIT | EOC_CONV_BIT)) != EOC_CONV_BIT)
+			msleep(20);
+		else
+			break;
+	}
+	if (i == ADC_WAIT_COUNT) {
+		pr_err("waited too long for offset eoc returning -EBUSY\n");
+		return -EBUSY;
+	}
+
+	rc = pm8xxx_readb(chip->dev->parent, ADC_ARB_SECP_DATA0, &data_lsb);
+	if (rc < 0) {
+		pr_err("error = %d reading offset lsb\n", rc);
+		return rc;
+	}
+
+	rc = pm8xxx_readb(chip->dev->parent, ADC_ARB_SECP_DATA1, &data_msb);
+	if (rc < 0) {
+		pr_err("error = %d reading offset msb\n", rc);
+		return rc;
+	}
+
+	*result = (data_msb << 8) | data_lsb;
+	return 0;
+}
+
+static int calib_ccadc_read_trim(struct pm8xxx_ccadc_chip *chip,
+					int addr, u8 *data_msb, u8 *data_lsb)
+{
+	int rc;
+	u8 sbi_config;
+
+	calib_ccadc_enable_trim_access(chip, &sbi_config);
+	rc = pm8xxx_readb(chip->dev->parent, addr, data_msb);
+	if (rc < 0) {
+		pr_err("error = %d read msb\n", rc);
+		return rc;
+	}
+	rc = pm8xxx_readb(chip->dev->parent, addr + 1, data_lsb);
+	if (rc < 0) {
+		pr_err("error = %d read lsb\n", rc);
+		return rc;
+	}
+	calib_ccadc_restore_trim_access(chip, sbi_config);
+	return 0;
+}
+
+static void calib_ccadc_read_offset_and_gain(struct pm8xxx_ccadc_chip *chip,
+						int *gain, u16 *offset)
+{
+	u8 data_msb;
+	u8 data_lsb;
+	int rc;
+
+	rc = calib_ccadc_read_trim(chip, CCADC_FULLSCALE_TRIM1,
+						&data_msb, &data_lsb);
+	*gain = (data_msb << 8) | data_lsb;
+
+	rc = calib_ccadc_read_trim(chip, CCADC_OFFSET_TRIM1,
+						&data_msb, &data_lsb);
+	*offset = (data_msb << 8) | data_lsb;
+
+	pr_debug("raw gain trim = 0x%x offset trim =0x%x\n", *gain, *offset);
+	*gain = pm8xxx_ccadc_reading_to_microvolt(chip->revision,
+							(s64)*gain - *offset);
+	pr_debug("gain uv = %duV offset=0x%x\n", *gain, *offset);
+}
+
+#define CCADC_PROGRAM_TRIM_COUNT	2
+#define ADC_ARB_BMS_CNTRL_CCADC_SHIFT	4
+#define ADC_ARB_BMS_CNTRL_CONV_MASK	0x03
+#define BMS_CONV_IN_PROGRESS		0x2
+
+static int calib_ccadc_program_trim(struct pm8xxx_ccadc_chip *chip,
+					int addr, u8 data_msb, u8 data_lsb,
+					int wait)
+{
+	int i, rc, loop;
+	u8 cntrl, sbi_config;
+	bool in_progress = 0;
+
+	loop = wait ? CCADC_PROGRAM_TRIM_COUNT : 0;
+
+	calib_ccadc_enable_trim_access(chip, &sbi_config);
+
+	for (i = 0; i < loop; i++) {
+		rc = pm8xxx_readb(chip->dev->parent, ADC_ARB_BMS_CNTRL, &cntrl);
+		if (rc < 0) {
+			pr_err("error = %d reading ADC_ARB_BMS_CNTRL\n", rc);
+			return rc;
+		}
+
+		/* break if a ccadc conversion is not happening */
+		in_progress = (((cntrl >> ADC_ARB_BMS_CNTRL_CCADC_SHIFT)
+			& ADC_ARB_BMS_CNTRL_CONV_MASK) == BMS_CONV_IN_PROGRESS);
+
+		if (!in_progress)
+			break;
+	}
+
+	if (in_progress) {
+		pr_debug("conv in progress cannot write trim,returing EBUSY\n");
+		return -EBUSY;
+	}
+
+	rc = pm8xxx_writeb(chip->dev->parent, addr, data_msb);
+	if (rc < 0) {
+		pr_err("error = %d write msb = 0x%x\n", rc, data_msb);
+		return rc;
+	}
+	rc = pm8xxx_writeb(chip->dev->parent, addr + 1, data_lsb);
+	if (rc < 0) {
+		pr_err("error = %d write lsb = 0x%x\n", rc, data_lsb);
+		return rc;
+	}
+	calib_ccadc_restore_trim_access(chip, sbi_config);
+	return 0;
+}
+
+void pm8xxx_calib_ccadc(void)
+{
+	u8 data_msb, data_lsb, sec_cntrl;
+	int result_offset, result_gain;
+	u16 result;
+	int i, rc;
+
+	rc = pm8xxx_readb(the_chip->dev->parent,
+					ADC_ARB_SECP_CNTRL, &sec_cntrl);
+	if (rc < 0) {
+		pr_err("error = %d reading ADC_ARB_SECP_CNTRL\n", rc);
+		return;
+	}
+
+	rc = calib_ccadc_enable_arbiter(the_chip);
+	if (rc < 0) {
+		pr_err("error = %d enabling arbiter for offset\n", rc);
+		goto bail;
+	}
+
+	/*
+	 * Set decimation ratio to 4k, lower ratio may be used in order to speed
+	 * up, pending verification through bench
+	 */
+	rc = pm8xxx_writeb(the_chip->dev->parent, ADC_ARB_SECP_DIG_PARAM,
+							CCADC_CALIB_DIG_PARAM);
+	if (rc < 0) {
+		pr_err("error = %d writing ADC_ARB_SECP_DIG_PARAM\n", rc);
+		goto bail;
+	}
+
+	result_offset = 0;
+	for (i = 0; i < SAMPLE_COUNT; i++) {
+		/* Short analog inputs to CCADC internally to ground */
+		rc = pm8xxx_writeb(the_chip->dev->parent, ADC_ARB_SECP_RSV,
+							CCADC_CALIB_RSV_GND);
+		if (rc < 0) {
+			pr_err("error = %d selecting gnd voltage\n", rc);
+			goto bail;
+		}
+
+		/* Enable CCADC */
+		rc = pm8xxx_writeb(the_chip->dev->parent,
+				ADC_ARB_SECP_ANA_PARAM, CCADC_CALIB_ANA_PARAM);
+		if (rc < 0) {
+			pr_err("error = %d enabling ccadc\n", rc);
+			goto bail;
+		}
+
+		rc = calib_start_conv(the_chip, &result);
+		if (rc < 0) {
+			pr_err("error = %d for zero volt measurement\n", rc);
+			goto bail;
+		}
+
+		result_offset += result;
+	}
+
+	result_offset = result_offset / SAMPLE_COUNT;
+
+
+	pr_debug("offset result_offset = 0x%x, voltage = %llduV\n",
+			result_offset,
+			pm8xxx_ccadc_reading_to_microvolt(the_chip->revision,
+			((s64)result_offset - CCADC_INTRINSIC_OFFSET)));
+
+	the_chip->ccadc_offset = result_offset;
+	data_msb = the_chip->ccadc_offset >> 8;
+	data_lsb = the_chip->ccadc_offset;
+
+	rc = calib_ccadc_program_trim(the_chip, CCADC_OFFSET_TRIM1,
+						data_msb, data_lsb, 1);
+	if (rc) {
+		pr_debug("error = %d programming offset trim 0x%02x 0x%02x\n",
+					rc, data_msb, data_lsb);
+		/* enable the interrupt and write it when it fires */
+		enable_irq(the_chip->eoc_irq);
+	}
+
+	rc = calib_ccadc_enable_arbiter(the_chip);
+	if (rc < 0) {
+		pr_err("error = %d enabling arbiter for gain\n", rc);
+		goto bail;
+	}
+
+	/*
+	 * Set decimation ratio to 4k, lower ratio may be used in order to speed
+	 * up, pending verification through bench
+	 */
+	rc = pm8xxx_writeb(the_chip->dev->parent, ADC_ARB_SECP_DIG_PARAM,
+							CCADC_CALIB_DIG_PARAM);
+	if (rc < 0) {
+		pr_err("error = %d enabling decimation ration for gain\n", rc);
+		goto bail;
+	}
+
+	result_gain = 0;
+	for (i = 0; i < SAMPLE_COUNT; i++) {
+		rc = pm8xxx_writeb(the_chip->dev->parent,
+					ADC_ARB_SECP_RSV, CCADC_CALIB_RSV_25MV);
+		if (rc < 0) {
+			pr_err("error = %d selecting 25mV for gain\n", rc);
+			goto bail;
+		}
+
+		/* Enable CCADC */
+		rc = pm8xxx_writeb(the_chip->dev->parent,
+			ADC_ARB_SECP_ANA_PARAM, CCADC_CALIB_ANA_PARAM);
+		if (rc < 0) {
+			pr_err("error = %d enabling ccadc\n", rc);
+			goto bail;
+		}
+
+		rc = calib_start_conv(the_chip, &result);
+		if (rc < 0) {
+			pr_err("error = %d for adc reading 25mV\n", rc);
+			goto bail;
+		}
+
+		result_gain += result;
+	}
+	result_gain = result_gain / SAMPLE_COUNT;
+
+	/*
+	 * result_offset includes INTRINSIC OFFSET
+	 * the_chip->ccadc_gain_uv will be the actual voltage
+	 * measured for 25000UV
+	 */
+	the_chip->ccadc_gain_uv = pm8xxx_ccadc_reading_to_microvolt(
+				the_chip->revision,
+				((s64)result_gain - result_offset));
+
+	pr_debug("gain result_gain = 0x%x, voltage = %d microVolts\n",
+					result_gain, the_chip->ccadc_gain_uv);
+
+	data_msb = result_gain >> 8;
+	data_lsb = result_gain;
+	rc = calib_ccadc_program_trim(the_chip, CCADC_FULLSCALE_TRIM1,
+						data_msb, data_lsb, 0);
+	if (rc)
+		pr_debug("error = %d programming gain trim\n", rc);
+bail:
+	pm8xxx_writeb(the_chip->dev->parent, ADC_ARB_SECP_CNTRL, sec_cntrl);
+}
+EXPORT_SYMBOL(pm8xxx_calib_ccadc);
+
+static irqreturn_t pm8921_bms_ccadc_eoc_handler(int irq, void *data)
+{
+	u8 data_msb, data_lsb;
+	struct pm8xxx_ccadc_chip *chip = data;
+	int rc;
+
+	pr_debug("irq = %d triggered\n", irq);
+	data_msb = chip->ccadc_offset >> 8;
+	data_lsb = chip->ccadc_offset;
+
+	rc = calib_ccadc_program_trim(chip, CCADC_OFFSET_TRIM1,
+						data_msb, data_lsb, 0);
+	disable_irq_nosync(chip->eoc_irq);
+
+	return IRQ_HANDLED;
+}
+
+#define CCADC_IBAT_DIG_PARAM	0xA3
+#define CCADC_IBAT_RSV		0x10
+#define CCADC_IBAT_ANA_PARAM	0x1A
+static int ccadc_get_rsense_voltage(int *voltage_uv)
+{
+	u16 raw;
+	int result;
+	int rc = 0;
+
+	rc = calib_ccadc_enable_arbiter(the_chip);
+	if (rc < 0) {
+		pr_err("error = %d enabling arbiter for offset\n", rc);
+		return rc;
+	}
+
+	rc = pm8xxx_writeb(the_chip->dev->parent, ADC_ARB_SECP_DIG_PARAM,
+							CCADC_IBAT_DIG_PARAM);
+	if (rc < 0) {
+		pr_err("error = %d writing ADC_ARB_SECP_DIG_PARAM\n", rc);
+		return rc;
+	}
+
+	rc = pm8xxx_writeb(the_chip->dev->parent, ADC_ARB_SECP_RSV,
+						CCADC_IBAT_RSV);
+	if (rc < 0) {
+		pr_err("error = %d selecting rsense\n", rc);
+		return rc;
+	}
+
+	rc = pm8xxx_writeb(the_chip->dev->parent,
+			ADC_ARB_SECP_ANA_PARAM, CCADC_IBAT_ANA_PARAM);
+	if (rc < 0) {
+		pr_err("error = %d enabling ccadc\n", rc);
+		return rc;
+	}
+
+	rc = calib_start_conv(the_chip, &raw);
+	if (rc < 0) {
+		pr_err("error = %d for zero volt measurement\n", rc);
+		return rc;
+	}
+
+	pr_debug("Vsense raw = 0x%x\n", raw);
+	result = cc_adjust_for_offset(raw);
+	pr_debug("Vsense after offset raw = 0x%x offset=0x%x\n",
+					result,
+					the_chip->ccadc_offset);
+	*voltage_uv = pm8xxx_ccadc_reading_to_microvolt(the_chip->revision,
+			((s64)result));
+	pr_debug("Vsense before gain of %d = %d uV\n", the_chip->ccadc_gain_uv,
+					*voltage_uv);
+	*voltage_uv = pm8xxx_cc_adjust_for_gain(*voltage_uv);
+
+	pr_debug("Vsense = %d uV\n", *voltage_uv);
+	return 0;
+}
+
+int pm8xxx_ccadc_get_battery_current(int *bat_current_ua)
+{
+	int voltage_uv = 0, rc;
+
+	rc = ccadc_get_rsense_voltage(&voltage_uv);
+	if (rc) {
+		pr_err("cant get voltage across rsense rc = %d\n", rc);
+		return rc;
+	}
+
+	*bat_current_ua = voltage_uv * 1000/the_chip->r_sense;
+	/*
+	 * ccadc reads +ve current when the battery is charging
+	 * We need to return -ve if the battery is charging
+	 */
+	*bat_current_ua = -1 * (*bat_current_ua);
+	pr_debug("bat current = %d ma\n", *bat_current_ua);
+	return 0;
+}
+EXPORT_SYMBOL(pm8xxx_ccadc_get_battery_current);
+
+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 int get_calc(void *data, u64 * val)
+{
+	int ibat, rc;
+
+	rc = pm8xxx_ccadc_get_battery_current(&ibat);
+	*val = ibat;
+	return rc;
+}
+DEFINE_SIMPLE_ATTRIBUTE(calc_fops, get_calc, NULL, "%lld\n");
+
+static void create_debugfs_entries(struct pm8xxx_ccadc_chip *chip)
+{
+	chip->dent = debugfs_create_dir("pm8xxx-ccadc", NULL);
+
+	if (IS_ERR(chip->dent)) {
+		pr_err("ccadc couldnt create debugfs dir\n");
+		return;
+	}
+
+	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("CCADC_FULLSCALE_TRIM1", 0644, chip->dent,
+			(void *)CCADC_FULLSCALE_TRIM1, &reg_fops);
+	debugfs_create_file("CCADC_FULLSCALE_TRIM0", 0644, chip->dent,
+			(void *)CCADC_FULLSCALE_TRIM0, &reg_fops);
+
+	debugfs_create_file("show_ibatt", 0644, chip->dent,
+				(void *)0, &calc_fops);
+}
+
+static int __devinit pm8xxx_ccadc_probe(struct platform_device *pdev)
+{
+	int rc = 0;
+	struct pm8xxx_ccadc_chip *chip;
+	struct resource *res;
+	const struct pm8xxx_ccadc_platform_data *pdata
+				= pdev->dev.platform_data;
+
+	if (!pdata) {
+		pr_err("missing platform data\n");
+		return -EINVAL;
+	}
+	res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+			"PM8921_BMS_CCADC_EOC");
+	if  (!res) {
+		pr_err("failed to get irq\n");
+		return -EINVAL;
+	}
+
+	chip = kzalloc(sizeof(struct pm8xxx_ccadc_chip), GFP_KERNEL);
+	if (!chip) {
+		pr_err("Cannot allocate pm_bms_chip\n");
+		return -ENOMEM;
+	}
+	chip->dev = &pdev->dev;
+	chip->revision = pm8xxx_get_revision(chip->dev->parent);
+	chip->eoc_irq = res->start;
+	chip->r_sense = pdata->r_sense;
+
+	calib_ccadc_read_offset_and_gain(chip,
+					&chip->ccadc_gain_uv,
+					&chip->ccadc_offset);
+	rc = request_irq(chip->eoc_irq,
+			pm8921_bms_ccadc_eoc_handler, IRQF_TRIGGER_RISING,
+			"bms_eoc_ccadc", chip);
+	if (rc) {
+		pr_err("failed to request %d irq rc= %d\n", chip->eoc_irq, rc);
+		goto free_chip;
+	}
+	disable_irq_nosync(chip->eoc_irq);
+
+	platform_set_drvdata(pdev, chip);
+	the_chip = chip;
+
+	create_debugfs_entries(chip);
+
+	return 0;
+
+free_chip:
+	kfree(chip);
+	return rc;
+}
+
+static int __devexit pm8xxx_ccadc_remove(struct platform_device *pdev)
+{
+	struct pm8xxx_ccadc_chip *chip = platform_get_drvdata(pdev);
+
+	debugfs_remove_recursive(chip->dent);
+	the_chip = NULL;
+	kfree(chip);
+	return 0;
+}
+
+static struct platform_driver pm8xxx_ccadc_driver = {
+	.probe	= pm8xxx_ccadc_probe,
+	.remove	= __devexit_p(pm8xxx_ccadc_remove),
+	.driver	= {
+		.name	= PM8XXX_CCADC_DEV_NAME,
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init pm8xxx_ccadc_init(void)
+{
+	return platform_driver_register(&pm8xxx_ccadc_driver);
+}
+
+static void __exit pm8xxx_ccadc_exit(void)
+{
+	platform_driver_unregister(&pm8xxx_ccadc_driver);
+}
+
+module_init(pm8xxx_ccadc_init);
+module_exit(pm8xxx_ccadc_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PMIC8XXX ccadc driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:" PM8XXX_CCADC_DEV_NAME);
diff --git a/drivers/power/pmic8058-charger.c b/drivers/power/pmic8058-charger.c
new file mode 100644
index 0000000..70b5d59
--- /dev/null
+++ b/drivers/power/pmic8058-charger.c
@@ -0,0 +1,2047 @@
+/* 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/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/mfd/pm8xxx/core.h>
+#include <linux/mfd/pmic8058.h>
+#include <linux/pmic8058-charger.h>
+#include <linux/mfd/pm8xxx/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 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 struct pmic8058_charger_data chg_data;
+
+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 = pm8xxx_batt_alarm_threshold_set(
+			PM8XXX_BATT_ALARM_LOWER_COMPARATOR, resume_mv);
+	if (!rc)
+		rc = pm8xxx_batt_alarm_threshold_set(
+			PM8XXX_BATT_ALARM_UPPER_COMPARATOR, 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 ret;
+
+	ret = pm8xxx_read_irq_stat(pm8058_chg.dev->parent, irq);
+	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;
+
+	pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_CNTRL, &temp);
+	dev_dbg(pm8058_chg.dev, "PM8058_CHG_CNTRL = 0x%x\n", temp);
+	pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_CNTRL_2, &temp);
+	dev_dbg(pm8058_chg.dev, "PM8058_CHG_CNTRL_2 = 0x%x\n", temp);
+	pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_VMAX_SEL, &temp);
+	dev_dbg(pm8058_chg.dev, "PM8058_CHG_VMAX_SEL = 0x%x\n", temp);
+	pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_VBAT_DET, &temp);
+	dev_dbg(pm8058_chg.dev, "PM8058_CHG_VBAT_DET = 0x%x\n", temp);
+	pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_IMAX, &temp);
+	dev_dbg(pm8058_chg.dev, "PM8058_CHG_IMAX = 0x%x\n", temp);
+	pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_TRICKLE, &temp);
+	dev_dbg(pm8058_chg.dev, "PM8058_CHG_TRICKLE = 0x%x\n", temp);
+	pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_ITERM, &temp);
+	dev_dbg(pm8058_chg.dev, "PM8058_CHG_ITERM = 0x%x\n", temp);
+	pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_TTRKL_MAX, &temp);
+	dev_dbg(pm8058_chg.dev, "PM8058_CHG_TTRKL_MAX = 0x%x\n", temp);
+	pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_TCHG_MAX, &temp);
+	dev_dbg(pm8058_chg.dev, "PM8058_CHG_TCHG_MAX = 0x%x\n", temp);
+	pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_TEMP_THRESH, &temp);
+	dev_dbg(pm8058_chg.dev, "PM8058_CHG_TEMP_THRESH = 0x%x\n", temp);
+	pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_TEMP_REG, &temp);
+	dev_dbg(pm8058_chg.dev, "PM8058_CHG_TEMP_REG = 0x%x\n", temp);
+	pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_PULSE, &temp);
+	dev_dbg(pm8058_chg.dev, "PM8058_CHG_PULSE = 0x%x\n", temp);
+
+	pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_STATUS_CLEAR_IRQ_1,
+		    &temp);
+	dev_dbg(pm8058_chg.dev, "PM8058_CHG_STATUS_CLEAR_IRQ_1 = 0x%x\n", temp);
+	pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_STATUS_CLEAR_IRQ_3,
+		    &temp);
+	dev_dbg(pm8058_chg.dev, "PM8058_CHG_STATUS_CLEAR_IRQ_3 = 0x%x\n", temp);
+	pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_STATUS_CLEAR_IRQ_10,
+		    &temp);
+	dev_dbg(pm8058_chg.dev, "PM8058_CHG_STATUS_CLEAR_IRQ_10 = 0x%x\n",
+		temp);
+	pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_STATUS_CLEAR_IRQ_11,
+		    &temp);
+	dev_dbg(pm8058_chg.dev, "PM8058_CHG_STATUS_CLEAR_IRQ_11 = 0x%x\n",
+		temp);
+
+	pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_MASK_IRQ_1, &temp);
+	dev_dbg(pm8058_chg.dev, "PM8058_CHG_MASK_IRQ_1 = 0x%x\n", temp);
+	pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_MASK_IRQ_3, &temp);
+	dev_dbg(pm8058_chg.dev, "PM8058_CHG_MASK_IRQ_3 = 0x%x\n", temp);
+	pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_MASK_IRQ_10, &temp);
+	dev_dbg(pm8058_chg.dev, "PM8058_CHG_MASK_IRQ_10 = 0x%x\n", temp);
+	pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_MASK_IRQ_11, &temp);
+	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 = pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_CNTRL, &temp);
+	if (ret)
+		return ret;
+	if (value)
+		temp |= BIT(CHG_USB_SUSPEND);
+	else
+		temp &= ~BIT(CHG_USB_SUSPEND);
+
+	return pm8xxx_writeb(pm8058_chg.dev->parent, PM8058_CHG_CNTRL, temp);
+}
+
+static int pm_chg_auto_disable(int value)
+{
+	u8 temp;
+	int ret;
+
+	ret = pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_CNTRL_2, &temp);
+	if (ret)
+		return ret;
+	if (value)
+		temp |= BIT(CHARGE_AUTO_DIS);
+	else
+		temp &= ~BIT(CHARGE_AUTO_DIS);
+
+	return pm8xxx_writeb(pm8058_chg.dev->parent, PM8058_CHG_CNTRL_2, temp);
+}
+
+static int pm_chg_batt_temp_disable(int value)
+{
+	u8 temp;
+	int ret;
+
+	ret = pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_CNTRL_2, &temp);
+	if (ret)
+		return ret;
+	if (value)
+		temp |= BIT(CHG_BATT_TEMP_DIS);
+	else
+		temp &= ~BIT(CHG_BATT_TEMP_DIS);
+
+	return pm8xxx_writeb(pm8058_chg.dev->parent, PM8058_CHG_CNTRL_2, temp);
+}
+
+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 pm8xxx_writeb(pm8058_chg.dev->parent, PM8058_CHG_VBAT_DET, temp);
+}
+
+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 pm8xxx_writeb(pm8058_chg.dev->parent, PM8058_CHG_IMAX, temp);
+}
+
+#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 pm8xxx_writeb(pm8058_chg.dev->parent, PM8058_CHG_VMAX_SEL, temp);
+}
+
+static int pm_chg_failed_clear(int value)
+{
+	u8 temp;
+	int ret;
+
+	ret = pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_CNTRL_2, &temp);
+	if (ret)
+		return ret;
+	if (value)
+		temp |= BIT(CHG_FAILED_CLEAR);
+	else
+		temp &= ~BIT(CHG_FAILED_CLEAR);
+	return pm8xxx_writeb(pm8058_chg.dev->parent, PM8058_CHG_CNTRL_2, temp);
+}
+
+static int pm_chg_iterm_set(int chg_current)
+{
+	u8 temp;
+
+	temp = (chg_current / PM8058_CHG_I_TERM_STEP_MA) - 1;
+	return pm8xxx_writeb(pm8058_chg.dev->parent, PM8058_CHG_ITERM, temp);
+}
+
+static int pm_chg_tchg_set(int minutes)
+{
+	u8 temp;
+
+	temp = (minutes >> PM8058_CHG_T_TCHG_SHIFT) - 1;
+	return pm8xxx_writeb(pm8058_chg.dev->parent, PM8058_CHG_TCHG_MAX, temp);
+}
+
+static int pm_chg_ttrkl_set(int minutes)
+{
+	u8 temp;
+
+	temp = minutes - 1;
+	return pm8xxx_writeb(pm8058_chg.dev->parent, PM8058_CHG_TTRKL_MAX,
+									temp);
+}
+
+static int pm_chg_enum_done_enable(int value)
+{
+	u8 temp;
+	int ret;
+
+	ret = pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_CNTRL_2, &temp);
+	if (ret)
+		return ret;
+	if (value)
+		temp |= BIT(ENUM_DONE);
+	else
+		temp &= ~BIT(ENUM_DONE);
+
+	return pm8xxx_writeb(pm8058_chg.dev->parent, PM8058_CHG_CNTRL_2, temp);
+}
+
+static uint32_t get_fsm_state(void)
+{
+	u8 temp;
+
+	temp = 0x00;
+	pm8xxx_writeb(pm8058_chg.dev->parent, PM8058_CHG_TEST_3, temp);
+	pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_TEST_3, &temp);
+	return (uint32_t)temp;
+}
+
+static int get_fsm_status(void *data, u64 * val)
+{
+	*val = get_fsm_state();
+	return 0;
+}
+
+enum pmic8058_chg_state pmic8058_get_fsm_state(void)
+{
+	if (!pm8058_chg.inited) {
+		pr_err("%s: called when not inited\n", __func__);
+		return -EINVAL;
+	}
+
+	return get_fsm_state();
+}
+
+static int pm_chg_disable(int value)
+{
+	u8 temp;
+	int ret;
+
+	ret = pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_CNTRL, &temp);
+	if (ret)
+		return ret;
+	if (value)
+		temp |= BIT(CHG_CHARGE_DIS);
+	else
+		temp &= ~BIT(CHG_CHARGE_DIS);
+
+	return pm8xxx_writeb(pm8058_chg.dev->parent, PM8058_CHG_CNTRL, temp);
+}
+
+static void pm8058_start_system_current(struct msm_hardware_charger *hw_chg,
+								int max_current)
+{
+	int ret = 0;
+
+	if (pm8058_chg.disabled)
+		return;
+
+	ret = pm_chg_imaxsel_set(max_current);
+	ret |= pm_chg_enum_done_enable(1);
+	ret |= pm_chg_disable(0);
+	if (ret)
+		pr_err("%s: failed to turn on system power err=%d",
+							__func__, ret);
+}
+
+static void pm8058_stop_system_current(struct msm_hardware_charger *hw_chg)
+{
+	int ret = 0;
+
+	ret = pm_chg_enum_done_enable(0);
+	ret |= pm_chg_disable(1);
+	if (ret)
+		pr_err("%s: failed to turn off system power err=%d",
+							__func__, ret);
+}
+
+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 PMIC8058_CHG_STATE_ATC:
+	case PMIC8058_CHG_STATE_FAST_CHG:
+	case PMIC8058_CHG_STATE_TRKL_CHG:
+		rc = pm8xxx_batt_alarm_disable(
+				PM8XXX_BATT_ALARM_UPPER_COMPARATOR);
+		if (!rc)
+			rc = pm8xxx_batt_alarm_disable(
+				PM8XXX_BATT_ALARM_LOWER_COMPARATOR);
+		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;
+
+	if (hw_chg->type == CHG_TYPE_AC && chg_data.max_source_current)
+		chg_current = chg_data.max_source_current;
+
+	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 = pm8xxx_readb(pm8058_chg.dev->parent,
+						PM8058_OVP_TEST_REG,
+						&old);
+			temp = old | BIT(FORCE_OVP_OFF);
+			ret = pm8xxx_writeb(pm8058_chg.dev->parent,
+						PM8058_OVP_TEST_REG,
+						temp);
+			temp = 0xFC;
+			ret = pm8xxx_writeb(pm8058_chg.dev->parent,
+					PM8058_CHG_TEST, temp);
+			/* 10 ms sleep is for the VCHG to discharge */
+			msleep(10);
+			temp = 0xF0;
+			ret = pm8xxx_writeb(pm8058_chg.dev->parent,
+						PM8058_CHG_TEST,
+						temp);
+			ret = pm8xxx_writeb(pm8058_chg.dev->parent,
+						PM8058_OVP_TEST_REG,
+						old);
+
+			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 = pm8xxx_readb(pm8058_chg.dev->parent,
+				PM8058_OVP_TEST_REG, &old);
+		temp = old | BIT(FORCE_OVP_OFF);
+		ret = pm8xxx_writeb(pm8058_chg.dev->parent,
+				PM8058_OVP_TEST_REG, temp);
+		temp = 0xFC;
+		ret = pm8xxx_writeb(pm8058_chg.dev->parent,
+				PM8058_CHG_TEST, temp);
+		/* 10 ms sleep is for the VCHG to discharge */
+		msleep(10);
+		temp = 0xF0;
+		ret = pm8xxx_writeb(pm8058_chg.dev->parent,
+				PM8058_CHG_TEST, temp);
+		ret = pm8xxx_writeb(pm8058_chg.dev->parent,
+				PM8058_OVP_TEST_REG, old);
+
+		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(AUTO_CHGDONE_IRQ);
+	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 (!pm8xxx_writeb(pm8058_chg.dev->parent, PM8058_CHG_TEST_3, temp)) {
+		pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_TEST_3, &temp);
+		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_irq(res->start,
+				  pm8058_chg_auto_chgdone_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_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_irq(res->start,
+				  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_irq(res->start,
+				  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_irq(res->start,
+				  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_irq(res->start,
+				  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_irq(res->start,
+				  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_irq(res->start,
+				  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 = pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_CNTRL, &temp);
+	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 = pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_CNTRL, &temp);
+	if (rc)
+		return rc;
+	if (on)
+		temp |= BIT(CHG_CHARGE_BAT);
+	else
+		temp &= ~BIT(CHG_CHARGE_BAT);
+
+	return pm8xxx_writeb(pm8058_chg.dev->parent, PM8058_CHG_CNTRL, temp);
+
+}
+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__);
+
+	/* 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);
+
+	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;
+
+	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;
+	pm8xxx_writeb(pm8058_chg.dev->parent, PM8058_CHG_TEST_2, temp);
+	temp = 0x84;
+	pm8xxx_writeb(pm8058_chg.dev->parent, PM8058_CHG_TEST_2, temp);
+	msleep(2);
+	temp = 0x80;
+	pm8xxx_writeb(pm8058_chg.dev->parent, PM8058_CHG_TEST_2, temp);
+	return 0;
+}
+
+static int get_reg(void *data, u64 * val)
+{
+	int i = (int)data;
+	int ret;
+	u8 temp;
+
+	ret = pm8xxx_readb(pm8058_chg.dev->parent, i, &temp);
+	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 = pm8xxx_writeb(pm8058_chg.dev->parent, i, temp);
+	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,
+	.start_system_current	= pm8058_start_system_current,
+	.stop_system_current	= pm8058_stop_system_current,
+};
+
+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 = pm8xxx_batt_alarm_disable(
+				PM8XXX_BATT_ALARM_UPPER_COMPARATOR);
+		if (!rc)
+			rc = pm8xxx_batt_alarm_disable(
+				PM8XXX_BATT_ALARM_LOWER_COMPARATOR);
+		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)
+{
+	int rc;
+	/* enable low comparator */
+	rc = pm8xxx_batt_alarm_disable(PM8XXX_BATT_ALARM_UPPER_COMPARATOR);
+	if (!rc)
+		return pm8xxx_batt_alarm_enable(
+				PM8XXX_BATT_ALARM_LOWER_COMPARATOR);
+
+	return rc;
+
+}
+
+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 |= pm8xxx_writeb(pm8058_chg.dev->parent, PM8058_CHG_TEST, temp);
+	ret |= pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_TEST, &old);
+	old = old & ~BIT(IGNORE_LL);
+	temp = 0x90  | (0xF & old);
+	ret |= pm8xxx_writeb(pm8058_chg.dev->parent, PM8058_CHG_TEST, temp);
+
+	return ret;
+}
+
+static int __devinit pm8058_charger_probe(struct platform_device *pdev)
+{
+	struct pmic8058_charger_data *pdata;
+	int rc = 0;
+
+	pm8058_chg.pdata = pdev->dev.platform_data;
+	pm8058_chg.dev = &pdev->dev;
+	pdata = (struct pmic8058_charger_data *) pm8058_chg.pdata;
+
+	if (pdata == NULL) {
+		pr_err("%s: pdata not present\n", __func__);
+		return -EINVAL;
+	}
+
+	if (pdata->charger_data_valid) {
+		usb_hw_chg.type = pdata->charger_type;
+		chg_data.charger_type = pdata->charger_type;
+		chg_data.max_source_current = pdata->max_source_current;
+	}
+
+	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 = pm8xxx_batt_alarm_disable(PM8XXX_BATT_ALARM_UPPER_COMPARATOR);
+	if (!rc)
+		rc = pm8xxx_batt_alarm_disable(
+			PM8XXX_BATT_ALARM_LOWER_COMPARATOR);
+	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 = pm8xxx_batt_alarm_threshold_set(
+			PM8XXX_BATT_ALARM_LOWER_COMPARATOR, resume_mv);
+	if (!rc)
+		rc = pm8xxx_batt_alarm_threshold_set(
+			PM8XXX_BATT_ALARM_UPPER_COMPARATOR, 4300);
+	if (rc) {
+		pr_err("%s: unable to set batt alarm threshold\n", __func__);
+		goto free_irq;
+	}
+
+	rc = pm8xxx_batt_alarm_hold_time_set(
+				PM8XXX_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 = pm8xxx_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 = pm8xxx_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 = pm8xxx_batt_alarm_disable(PM8XXX_BATT_ALARM_UPPER_COMPARATOR);
+	if (!rc)
+		rc = pm8xxx_batt_alarm_disable(
+			PM8XXX_BATT_ALARM_LOWER_COMPARATOR);
+	if (rc)
+		pr_err("%s: unable to set batt alarm state\n", __func__);
+
+	rc |= pm8xxx_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/power_supply_core.c b/drivers/power/power_supply_core.c
index f58d1805..be6ba04 100644
--- a/drivers/power/power_supply_core.c
+++ b/drivers/power/power_supply_core.c
@@ -25,6 +25,61 @@
 
 static struct device_type power_supply_dev_type;
 
+/**
+ * power_supply_set_current_limit - set current limit
+ * @psy:	the power supply to control
+ * @limit:	current limit in uA from the power supply.
+ *		0 will disable the power supply.
+ *
+ * This function will set a maximum supply current from a source
+ * and it will disable the charger when limit is 0.
+ */
+int power_supply_set_current_limit(struct power_supply *psy, int limit)
+{
+	const union power_supply_propval ret = {limit,};
+
+	if (psy->set_property)
+		return psy->set_property(psy, POWER_SUPPLY_PROP_CURRENT_MAX,
+								&ret);
+
+	return -ENXIO;
+}
+EXPORT_SYMBOL_GPL(power_supply_set_current_limit);
+
+/**
+ * power_supply_set_online - set online state of the power supply
+ * @psy:	the power supply to control
+ * @enable:	sets online property of power supply
+ */
+int power_supply_set_online(struct power_supply *psy, bool enable)
+{
+	const union power_supply_propval ret = {enable,};
+
+	if (psy->set_property)
+		return psy->set_property(psy, POWER_SUPPLY_PROP_ONLINE,
+								&ret);
+
+	return -ENXIO;
+}
+EXPORT_SYMBOL_GPL(power_supply_set_online);
+
+/**
+ * power_supply_set_charge_type - set charge type of the power supply
+ * @psy:	the power supply to control
+ * @enable:	sets charge type property of power supply
+ */
+int power_supply_set_charge_type(struct power_supply *psy, int charge_type)
+{
+	const union power_supply_propval ret = {charge_type,};
+
+	if (psy->set_property)
+		return psy->set_property(psy, POWER_SUPPLY_PROP_CHARGE_TYPE,
+								&ret);
+
+	return -ENXIO;
+}
+EXPORT_SYMBOL_GPL(power_supply_set_charge_type);
+
 static int __power_supply_changed_work(struct device *dev, void *data)
 {
 	struct power_supply *psy = (struct power_supply *)data;
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");
diff --git a/drivers/power/smb349.c b/drivers/power/smb349.c
new file mode 100644
index 0000000..4c07285
--- /dev/null
+++ b/drivers/power/smb349.c
@@ -0,0 +1,700 @@
+/* Copyright (c) 2012 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/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/smb349.h>
+#include <linux/power_supply.h>
+
+#define SMB349_MASK(BITS, POS)  ((unsigned char)(((1 << BITS) - 1) << POS))
+
+/* Register definitions */
+#define CHG_CURRENT_REG			0x00
+#define CHG_OTHER_CURRENT_REG	0x01
+#define VAR_FUNC_REG			0x02
+#define FLOAT_VOLTAGE_REG		0x03
+#define CHG_CTRL_REG			0x04
+#define STAT_TIMER_REG			0x05
+#define PIN_ENABLE_CTRL_REG		0x06
+#define THERM_CTRL_A_REG		0x07
+#define SYSOK_USB3_SELECT_REG	0x08
+#define CTRL_FUNCTIONS_REG		0x09
+#define OTG_TLIM_THERM_CNTRL_REG	0x0A
+#define HARD_SOFT_LIMIT_CELL_TEMP_MONITOR_REG 0x0B
+#define FAULT_IRQ_REG	0x0C
+#define STATUS_IRQ_REG	0x0D
+#define SYSOK_REG		0x0E
+#define CMD_A_REG		0x30
+#define CMD_B_REG		0x31
+#define CMD_C_REG		0x33
+#define IRQ_A_REG		0x35
+#define IRQ_B_REG		0x36
+#define IRQ_C_REG		0x37
+#define IRQ_D_REG		0x38
+#define IRQ_E_REG		0x39
+#define IRQ_F_REG		0x3A
+#define STATUS_A_REG	0x3B
+#define STATUS_B_REG	0x3C
+#define STATUS_C_REG	0x3D
+#define STATUS_D_REG	0x3E
+#define STATUS_E_REG	0x3F
+
+/* Status bits and masks */
+#define CHG_STATUS_MASK		SMB349_MASK(2, 1)
+#define CHG_ENABLE_STATUS_BIT		BIT(0)
+
+/* Control bits and masks */
+#define FAST_CHG_CURRENT_MASK			SMB349_MASK(4, 4)
+#define AC_INPUT_CURRENT_LIMIT_MASK		SMB349_MASK(4, 0)
+#define PRE_CHG_CURRENT_MASK			SMB349_MASK(3, 5)
+#define TERMINATION_CURRENT_MASK		SMB349_MASK(3, 2)
+#define PRE_CHG_TO_FAST_CHG_THRESH_MASK	SMB349_MASK(2, 6)
+#define FLOAT_VOLTAGE_MASK				SMB349_MASK(6, 0)
+#define CHG_ENABLE_BIT			BIT(1)
+#define VOLATILE_W_PERM_BIT		BIT(7)
+#define USB_SELECTION_BIT		BIT(1)
+#define SYSTEM_FET_ENABLE_BIT	BIT(7)
+#define AUTOMATIC_INPUT_CURR_LIMIT_BIT			BIT(4)
+#define AUTOMATIC_POWER_SOURCE_DETECTION_BIT	BIT(2)
+#define BATT_OV_END_CHG_BIT		BIT(1)
+#define VCHG_FUNCTION			BIT(0)
+#define CURR_TERM_END_CHG_BIT	BIT(6)
+
+struct smb349_struct {
+	struct			i2c_client *client;
+	bool			charging;
+	bool			present;
+	int			chg_current_ma;
+
+	int			en_n_gpio;
+	int			chg_susp_gpio;
+	struct dentry			*dent;
+	spinlock_t			lock;
+	struct work_struct			hwinit_work;
+
+	struct power_supply			dc_psy;
+};
+
+struct chg_ma_limit_entry {
+	int fast_chg_ma_limit;
+	int ac_input_ma_limit;
+	u8  chg_current_value;
+};
+
+static struct smb349_struct *the_smb349_chg;
+
+static int smb349_read_reg(struct i2c_client *client, int reg,
+				u8 *val)
+{
+	s32 ret;
+	struct smb349_struct *smb349_chg;
+
+	smb349_chg = i2c_get_clientdata(client);
+	ret = i2c_smbus_read_byte_data(smb349_chg->client, reg);
+	if (ret < 0) {
+		dev_err(&smb349_chg->client->dev,
+			"i2c read fail: can't read from %02x: %d\n", reg, ret);
+		return ret;
+	} else {
+		*val = ret;
+	}
+
+	return 0;
+}
+
+static int smb349_write_reg(struct i2c_client *client, int reg,
+						u8 val)
+{
+	s32 ret;
+	struct smb349_struct *smb349_chg;
+
+	smb349_chg = i2c_get_clientdata(client);
+	ret = i2c_smbus_write_byte_data(smb349_chg->client, reg, val);
+	if (ret < 0) {
+		dev_err(&smb349_chg->client->dev,
+			"i2c write fail: can't write %02x to %02x: %d\n",
+			val, reg, ret);
+		return ret;
+	}
+	return 0;
+}
+
+static int smb349_masked_write(struct i2c_client *client, int reg,
+		u8 mask, u8 val)
+{
+	s32 rc;
+	u8 temp;
+
+	rc = smb349_read_reg(client, reg, &temp);
+	if (rc) {
+		pr_err("smb349_read_reg failed: reg=%03X, rc=%d\n", reg, rc);
+		return rc;
+	}
+	temp &= ~mask;
+	temp |= val & mask;
+	rc = smb349_write_reg(client, reg, temp);
+	if (rc) {
+		pr_err("smb349_write failed: reg=%03X, rc=%d\n", reg, rc);
+		return rc;
+	}
+	return 0;
+}
+
+static enum power_supply_property pm_power_props[] = {
+	POWER_SUPPLY_PROP_ONLINE,
+	POWER_SUPPLY_PROP_CURRENT_MAX,
+	POWER_SUPPLY_PROP_CHARGE_TYPE,
+};
+
+static char *pm_power_supplied_to[] = {
+	"battery",
+};
+
+static int get_prop_charge_type(struct smb349_struct *smb349_chg)
+{
+	if (smb349_chg->charging)
+		return POWER_SUPPLY_CHARGE_TYPE_FAST;
+
+	return POWER_SUPPLY_CHARGE_TYPE_NONE;
+}
+
+static int pm_power_get_property(struct power_supply *psy,
+				  enum power_supply_property psp,
+				  union power_supply_propval *val)
+{
+	struct smb349_struct *smb349_chg = container_of(psy,
+						struct smb349_struct,
+						dc_psy);
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_CURRENT_MAX:
+		val->intval = smb349_chg->chg_current_ma;
+		break;
+	case POWER_SUPPLY_PROP_ONLINE:
+		val->intval = (int)smb349_chg->present;
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_TYPE:
+		val->intval = get_prop_charge_type(smb349_chg);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+#define SMB349_FAST_CHG_MIN_MA	1000
+#define SMB349_FAST_CHG_STEP_MA	200
+#define SMB349_FAST_CHG_MAX_MA	4000
+#define SMB349_FAST_CHG_SHIFT	4
+static int chg_current_set(struct smb349_struct *smb349_chg)
+{
+	u8 temp;
+
+	if ((smb349_chg->chg_current_ma < SMB349_FAST_CHG_MIN_MA) ||
+		(smb349_chg->chg_current_ma >  SMB349_FAST_CHG_MAX_MA)) {
+		pr_err("bad mA=%d asked to set\n", smb349_chg->chg_current_ma);
+		return -EINVAL;
+	}
+
+	temp = (smb349_chg->chg_current_ma - SMB349_FAST_CHG_MIN_MA)
+			/ SMB349_FAST_CHG_STEP_MA;
+
+	temp = temp << SMB349_FAST_CHG_SHIFT;
+	pr_debug("fastchg limit=%d setting %02x\n",
+				smb349_chg->chg_current_ma, temp);
+	return smb349_masked_write(smb349_chg->client, CHG_CURRENT_REG,
+			FAST_CHG_CURRENT_MASK, temp);
+}
+
+static int set_reg(void *data, u64 val)
+{
+	int addr = (int)data;
+	int ret;
+	u8 temp;
+
+	temp = (u16) val;
+	ret = smb349_write_reg(the_smb349_chg->client, addr, temp);
+
+	if (ret) {
+		pr_err("smb349_write_reg to %x value =%d errored = %d\n",
+			addr, temp, ret);
+		return -EAGAIN;
+	}
+	return 0;
+}
+static int get_reg(void *data, u64 *val)
+{
+	int addr = (int)data;
+	int ret;
+	u8 temp;
+
+	ret = smb349_read_reg(the_smb349_chg->client, addr, &temp);
+	if (ret) {
+		pr_err("smb349_read_reg to %x value =%d errored = %d\n",
+			addr, temp, ret);
+		return -EAGAIN;
+	}
+
+	*val = temp;
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(reg_fops, get_reg, set_reg, "0x%02llx\n");
+
+static void create_debugfs_entries(struct smb349_struct *smb349_chg)
+{
+	struct dentry *file;
+	smb349_chg->dent = debugfs_create_dir(SMB349_NAME, NULL);
+	if (IS_ERR(smb349_chg->dent)) {
+		pr_err("smb349 driver couldn't create debugfs dir\n");
+		return;
+	}
+
+	file = debugfs_create_file("CHG_CURRENT_REG", 0644, smb349_chg->dent,
+				(void *) CHG_CURRENT_REG, &reg_fops);
+	if (IS_ERR(file)) {
+		pr_err("smb349 driver couldn't create debugfs files\n");
+		return;
+	}
+	file = debugfs_create_file("CHG_OTHER_CURRENT_REG", 0644,
+		smb349_chg->dent, (void *) CHG_OTHER_CURRENT_REG, &reg_fops);
+	if (IS_ERR(file)) {
+		pr_err("smb349 driver couldn't create debugfs files\n");
+		return;
+	}
+	file = debugfs_create_file("VAR_FUNC_REG", 0644, smb349_chg->dent,
+				(void *) VAR_FUNC_REG, &reg_fops);
+	if (IS_ERR(file)) {
+		pr_err("smb349 driver couldn't create debugfs files\n");
+		return;
+	}
+	file = debugfs_create_file("FLOAT_VOLTAGE_REG", 0644, smb349_chg->dent,
+				(void *) FLOAT_VOLTAGE_REG, &reg_fops);
+	if (IS_ERR(file)) {
+		pr_err("smb349 driver couldn't create debugfs files\n");
+		return;
+	}
+	file = debugfs_create_file("CHG_CTRL_REG", 0644, smb349_chg->dent,
+				(void *) CHG_CTRL_REG, &reg_fops);
+	if (IS_ERR(file)) {
+		pr_err("smb349 driver couldn't create debugfs files\n");
+		return;
+	}
+	file = debugfs_create_file("STAT_TIMER_REG", 0644, smb349_chg->dent,
+				(void *) STAT_TIMER_REG, &reg_fops);
+	if (IS_ERR(file)) {
+		pr_err("smb349 driver couldn't create debugfs files\n");
+		return;
+	}
+	file = debugfs_create_file("PIN_ENABLE_CTRL_REG", 0644,
+		smb349_chg->dent, (void *) PIN_ENABLE_CTRL_REG, &reg_fops);
+	if (IS_ERR(file)) {
+		pr_err("smb349 driver couldn't create debugfs files\n");
+		return;
+	}
+	file = debugfs_create_file("PIN_ENABLE_CTRL_REG", 0644,
+		smb349_chg->dent, (void *) PIN_ENABLE_CTRL_REG, &reg_fops);
+	if (IS_ERR(file)) {
+		pr_err("smb349 driver couldn't create debugfs files\n");
+		return;
+	}
+	file = debugfs_create_file("PIN_ENABLE_CTRL_REG", 0644,
+		smb349_chg->dent, (void *) PIN_ENABLE_CTRL_REG, &reg_fops);
+	if (IS_ERR(file)) {
+		pr_err("smb349 driver couldn't create debugfs files\n");
+		return;
+	}
+	file = debugfs_create_file("THERM_CTRL_A_REG", 0644, smb349_chg->dent,
+				(void *) THERM_CTRL_A_REG, &reg_fops);
+	if (IS_ERR(file)) {
+		pr_err("smb349 driver couldn't create debugfs files\n");
+		return;
+	}
+	file = debugfs_create_file("SYSOK_USB3_SELECT_REG", 0644,
+		smb349_chg->dent, (void *) SYSOK_USB3_SELECT_REG, &reg_fops);
+	if (IS_ERR(file)) {
+		pr_err("smb349 driver couldn't create debugfs files\n");
+		return;
+	}
+	file = debugfs_create_file("CTRL_FUNCTIONS_REG", 0644,
+		smb349_chg->dent, (void *) CTRL_FUNCTIONS_REG, &reg_fops);
+	if (IS_ERR(file)) {
+		pr_err("smb349 driver couldn't create debugfs files\n");
+		return;
+	}
+	file = debugfs_create_file("OTG_TLIM_THERM_CNTRL_REG", 0644,
+		smb349_chg->dent, (void *) OTG_TLIM_THERM_CNTRL_REG, &reg_fops);
+	if (IS_ERR(file)) {
+		pr_err("smb349 driver couldn't create debugfs files\n");
+		return;
+	}
+	file = debugfs_create_file("HARD_SOFT_LIMIT_CELL_TEMP_MONITOR_REG",
+		0644, smb349_chg->dent,
+		(void *) HARD_SOFT_LIMIT_CELL_TEMP_MONITOR_REG, &reg_fops);
+	if (IS_ERR(file)) {
+		pr_err("smb349 driver couldn't create debugfs files\n");
+		return;
+	}
+	file = debugfs_create_file("SYSOK_REG", 0644, smb349_chg->dent,
+				(void *) SYSOK_REG, &reg_fops);
+	if (IS_ERR(file)) {
+		pr_err("smb349 driver couldn't create debugfs files\n");
+		return;
+	}
+	file = debugfs_create_file("CMD_A_REG", 0644, smb349_chg->dent,
+				(void *) CMD_A_REG, &reg_fops);
+	if (IS_ERR(file)) {
+		pr_err("smb349 driver couldn't create debugfs files\n");
+		return;
+	}
+	file = debugfs_create_file("CMD_B_REG", 0644, smb349_chg->dent,
+				(void *) CMD_B_REG, &reg_fops);
+	if (IS_ERR(file)) {
+		pr_err("smb349 driver couldn't create debugfs files\n");
+		return;
+	}
+	file = debugfs_create_file("CMD_C_REG", 0644, smb349_chg->dent,
+				(void *) CMD_C_REG, &reg_fops);
+	if (IS_ERR(file)) {
+		pr_err("smb349 driver couldn't create debugfs files\n");
+		return;
+	}
+	file = debugfs_create_file("STATUS_A_REG", 0644, smb349_chg->dent,
+				(void *) STATUS_A_REG, &reg_fops);
+	if (IS_ERR(file)) {
+		pr_err("smb349 driver couldn't create debugfs files\n");
+		return;
+	}
+	file = debugfs_create_file("STATUS_B_REG", 0644, smb349_chg->dent,
+				(void *) STATUS_B_REG, &reg_fops);
+	if (IS_ERR(file)) {
+		pr_err("smb349 driver couldn't create debugfs files\n");
+		return;
+	}
+	file = debugfs_create_file("STATUS_C_REG", 0644, smb349_chg->dent,
+				(void *) STATUS_C_REG, &reg_fops);
+	if (IS_ERR(file)) {
+		pr_err("smb349 driver couldn't create debugfs files\n");
+		return;
+	}
+	file = debugfs_create_file("STATUS_D_REG", 0644, smb349_chg->dent,
+				(void *) STATUS_D_REG, &reg_fops);
+	if (IS_ERR(file)) {
+		pr_err("smb349 driver couldn't create debugfs files\n");
+		return;
+	}
+	file = debugfs_create_file("STATUS_E_REG", 0644, smb349_chg->dent,
+				(void *) STATUS_E_REG, &reg_fops);
+	if (IS_ERR(file)) {
+		pr_err("smb349 driver couldn't create debugfs files\n");
+		return;
+	}
+}
+
+static void remove_debugfs_entries(struct smb349_struct *smb349_chg)
+{
+	if (smb349_chg->dent)
+		debugfs_remove_recursive(smb349_chg->dent);
+}
+
+static int smb349_hwinit(struct smb349_struct *smb349_chg)
+{
+	int ret;
+
+	ret = smb349_write_reg(smb349_chg->client, CMD_A_REG,
+			VOLATILE_W_PERM_BIT);
+	if (ret) {
+		pr_err("Failed to set VOLATILE_W_PERM_BIT rc=%d\n", ret);
+		return ret;
+	}
+
+	ret = smb349_masked_write(smb349_chg->client, CHG_CTRL_REG,
+				CURR_TERM_END_CHG_BIT, CURR_TERM_END_CHG_BIT);
+	if (ret) {
+		pr_err("Failed to set CURR_TERM_END_CHG_BIT rc=%d\n", ret);
+		return ret;
+	}
+
+	ret = chg_current_set(smb349_chg);
+	if (ret) {
+		pr_err("Failed to set FAST_CHG_CURRENT rc=%d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int smb349_stop_charging(struct smb349_struct *smb349_chg)
+{
+	unsigned long flags;
+
+	if (smb349_chg->charging)
+		gpio_set_value_cansleep(smb349_chg->en_n_gpio, 0);
+
+	spin_lock_irqsave(&smb349_chg->lock, flags);
+	pr_debug("stop charging %d\n", smb349_chg->charging);
+	smb349_chg->charging = 0;
+	spin_unlock_irqrestore(&smb349_chg->lock, flags);
+	power_supply_changed(&smb349_chg->dc_psy);
+	return 0;
+}
+
+static int smb349_start_charging(struct smb349_struct *smb349_chg)
+{
+	unsigned long flags;
+	int rc;
+
+	rc = 0;
+	if (!smb349_chg->charging) {
+		gpio_set_value_cansleep(smb349_chg->en_n_gpio, 1);
+		/*
+		 * Write non-default values, charger chip reloads from
+		 * non-volatile memory if it was in suspend mode
+		 *
+		 */
+		rc = schedule_work(&smb349_chg->hwinit_work);
+	}
+
+	spin_lock_irqsave(&smb349_chg->lock, flags);
+	pr_debug("start charging %d\n", smb349_chg->charging);
+	smb349_chg->charging = 1;
+	spin_unlock_irqrestore(&smb349_chg->lock, flags);
+	power_supply_changed(&smb349_chg->dc_psy);
+	return rc;
+}
+
+static int pm_power_set_property(struct power_supply *psy,
+				  enum power_supply_property psp,
+				  const union power_supply_propval *val)
+{
+	struct smb349_struct *smb349_chg = container_of(psy,
+						struct smb349_struct,
+						dc_psy);
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_ONLINE:
+		if (val->intval) {
+			smb349_chg->present = val->intval;
+		} else {
+			smb349_chg->present = 0;
+			if (smb349_chg->charging)
+				return smb349_stop_charging(smb349_chg);
+		}
+		break;
+	case POWER_SUPPLY_PROP_CURRENT_MAX:
+		if (val->intval) {
+			if (smb349_chg->chg_current_ma != val->intval)
+				return -EINVAL;
+		}
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_TYPE:
+		if (val->intval && smb349_chg->present) {
+			if (val->intval == POWER_SUPPLY_CHARGE_TYPE_FAST)
+				return smb349_start_charging(smb349_chg);
+			if (val->intval == POWER_SUPPLY_CHARGE_TYPE_NONE)
+				return smb349_stop_charging(smb349_chg);
+		} else {
+			return -EINVAL;
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+	power_supply_changed(&smb349_chg->dc_psy);
+	return 0;
+}
+
+static void hwinit_worker(struct work_struct *work)
+{
+	int ret;
+	struct smb349_struct *smb349_chg = container_of(work,
+				struct smb349_struct, hwinit_work);
+
+	ret = smb349_hwinit(smb349_chg);
+	if (ret)
+		pr_err("Failed to re-initilaze registers\n");
+}
+
+static int __devinit smb349_init_ext_chg(struct smb349_struct *smb349_chg)
+{
+	int ret;
+
+	smb349_chg->dc_psy.name = "dc";
+	smb349_chg->dc_psy.type = POWER_SUPPLY_TYPE_MAINS;
+	smb349_chg->dc_psy.supplied_to = pm_power_supplied_to;
+	smb349_chg->dc_psy.num_supplicants = ARRAY_SIZE(pm_power_supplied_to);
+	smb349_chg->dc_psy.properties = pm_power_props;
+	smb349_chg->dc_psy.num_properties = ARRAY_SIZE(pm_power_props);
+	smb349_chg->dc_psy.get_property = pm_power_get_property;
+	smb349_chg->dc_psy.set_property = pm_power_set_property;
+
+	ret = power_supply_register(&smb349_chg->client->dev,
+				&smb349_chg->dc_psy);
+	if (ret) {
+		pr_err("failed to register power_supply. ret=%d.\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int __devinit smb349_probe(struct i2c_client *client,
+				    const struct i2c_device_id *id)
+{
+	const struct smb349_platform_data *pdata;
+	struct smb349_struct *smb349_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;
+	}
+
+	smb349_chg = kzalloc(sizeof(*smb349_chg), GFP_KERNEL);
+	if (!smb349_chg) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	smb349_chg->client = client;
+	smb349_chg->chg_current_ma = pdata->chg_current_ma;
+	ret = gpio_request(pdata->chg_susp_gpio, "smb349_suspend");
+	if (ret) {
+		dev_err(&client->dev, "%s gpio_request failed for %d ret=%d\n",
+			__func__, pdata->chg_susp_gpio, ret);
+		goto free_smb349_chg;
+	}
+	smb349_chg->chg_susp_gpio = pdata->chg_susp_gpio;
+
+	ret = gpio_request(pdata->en_n_gpio, "smb349_charger_enable");
+	if (ret) {
+		dev_err(&client->dev, "%s gpio_request failed for %d ret=%d\n",
+			__func__, pdata->en_n_gpio, ret);
+		goto chg_susp_gpio_fail;
+	}
+	smb349_chg->en_n_gpio = pdata->en_n_gpio;
+
+	i2c_set_clientdata(client, smb349_chg);
+
+	ret = smb349_hwinit(smb349_chg);
+	if (ret)
+		goto free_smb349_chg;
+
+	ret = smb349_init_ext_chg(smb349_chg);
+	if (ret)
+		goto chg_en_gpio_fail;
+
+	the_smb349_chg = smb349_chg;
+
+	create_debugfs_entries(smb349_chg);
+	INIT_WORK(&smb349_chg->hwinit_work, hwinit_worker);
+
+	pr_info("OK connector present = %d\n", smb349_chg->present);
+	return 0;
+
+chg_en_gpio_fail:
+	gpio_free(smb349_chg->en_n_gpio);
+chg_susp_gpio_fail:
+	gpio_free(smb349_chg->chg_susp_gpio);
+free_smb349_chg:
+	kfree(smb349_chg);
+out:
+	return ret;
+}
+
+static int __devexit smb349_remove(struct i2c_client *client)
+{
+	const struct smb349_platform_data *pdata;
+	struct smb349_struct *smb349_chg = i2c_get_clientdata(client);
+
+	flush_work(&smb349_chg->hwinit_work);
+	pdata = client->dev.platform_data;
+	power_supply_unregister(&smb349_chg->dc_psy);
+	gpio_free(pdata->en_n_gpio);
+	gpio_free(pdata->chg_susp_gpio);
+	remove_debugfs_entries(smb349_chg);
+	kfree(smb349_chg);
+	return 0;
+}
+
+static int smb349_suspend(struct device *dev)
+{
+	struct smb349_struct *smb349_chg = dev_get_drvdata(dev);
+
+	pr_debug("suspend\n");
+	if (smb349_chg->charging)
+		return -EBUSY;
+	return 0;
+}
+
+static int smb349_resume(struct device *dev)
+{
+	pr_debug("resume\n");
+
+	return 0;
+}
+
+static const struct dev_pm_ops smb349_pm_ops = {
+	.suspend	= smb349_suspend,
+	.resume		= smb349_resume,
+};
+
+static const struct i2c_device_id smb349_id[] = {
+	{SMB349_NAME, 0},
+	{},
+};
+MODULE_DEVICE_TABLE(i2c, smb349_id);
+
+static struct i2c_driver smb349_driver = {
+	.driver	= {
+		   .name	= SMB349_NAME,
+		   .owner	= THIS_MODULE,
+		   .pm		= &smb349_pm_ops,
+	},
+	.probe		= smb349_probe,
+	.remove		= __devexit_p(smb349_remove),
+	.id_table	= smb349_id,
+};
+
+static int __init smb349_init(void)
+{
+	return i2c_add_driver(&smb349_driver);
+}
+module_init(smb349_init);
+
+static void __exit smb349_exit(void)
+{
+	return i2c_del_driver(&smb349_driver);
+}
+module_exit(smb349_exit);
+
+MODULE_DESCRIPTION("Driver for SMB349 charger chip");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("i2c:" SMB349_NAME);