Merge "mmc: card: Add long sequential write test to test-iosched"
diff --git a/Documentation/devicetree/bindings/power/bq28400-battery.txt b/Documentation/devicetree/bindings/power/bq28400-battery.txt
new file mode 100644
index 0000000..3879b4d
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/bq28400-battery.txt
@@ -0,0 +1,18 @@
+TI BQ28400 Battery Gas Gauge
+
+The bq28400 monitors the battery temperature, capacity, voltage, current etc.
+The device interface is I2C, its I2C slave 7-bit address is 0xb.
+The device is usually embedded inside the "smart battery" pack.
+
+node required properties:
+- compatible:	Must be "ti,bq28400-battery".
+- reg:		I2C Address must be 0xb.
+
+Example:
+	i2c@f9967000 {
+		battery@b {
+			compatible = "ti,bq28400-battery";
+			reg = <0xb>;
+		};
+	};
+
diff --git a/Documentation/devicetree/bindings/power/smb350.txt b/Documentation/devicetree/bindings/power/smb350.txt
new file mode 100644
index 0000000..6f21236
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/smb350.txt
@@ -0,0 +1,43 @@
+Summit smb350 battery charger
+
+The smb350 charger supports stack-cell battery charging.
+
+The smb350 interface is via I2C bus.
+The i2c slave 7-bit address is programmable at manufacture.
+
+Node required properties:
+- compatible:		Must be "summit,smb350-charger".
+- reg:			The device 7-bit I2C address.
+- summit,stat-gpio		gpio which smb350 STAT pin connects to.
+- summit,chg-en-n-gpio		gpio which control charging enable.
+- summit,chg-susp-n-gpio	gpio which control device shutdown
+- summit,chg-current-ma		charging current in milliamps.
+- summit,term-current-ma	charging termination current in milliamps.
+				valid values are 200/300/400/500/600/700.
+				A value of zero means no termination current.
+
+Example:
+	i2c@f9967000 {
+		cell-index = <0>;
+		compatible = "qcom,i2c-qup";
+		reg = <0Xf9967000 0x1000>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		reg-names = "qup_phys_addr";
+		interrupts = <0 105 0>;
+		interrupt-names = "qup_err_intr";
+		qcom,i2c-bus-freq = <100000>;
+		qcom,i2c-src-freq = <24000000>;
+		label = "blsp_11";
+
+		smb350-charger@2b {
+			compatible = "summit,smb350-charger";
+			reg = <0x2b>; /* 0x56/0x57 */
+			summit,stat-gpio = <&pm8941_gpios 30 0x00>;
+			summit,chg-en-n-gpio = <&pm8941_gpios 10 0x00>;
+			summit,chg-susp-n-gpio = <&pm8941_gpios 13 0x00>;
+			summit,chg-current-ma = <1600>;
+			summit,term-current-ma = <200>;
+		};
+	};
+
diff --git a/arch/arm/boot/dts/msm8974-liquid.dts b/arch/arm/boot/dts/msm8974-liquid.dts
index 2eacb46..265d7b9 100644
--- a/arch/arm/boot/dts/msm8974-liquid.dts
+++ b/arch/arm/boot/dts/msm8974-liquid.dts
@@ -27,6 +27,13 @@
 		status = "ok";
 	};
 
+	i2c@f9967000 {
+		battery@b {
+			compatible = "ti,bq28400-battery";
+			reg = <0xb>;
+		};
+	};
+
 	gpio_keys {
 		compatible = "gpio-keys";
 		input-name = "gpio-keys";
diff --git a/arch/arm/boot/dts/msm8974.dtsi b/arch/arm/boot/dts/msm8974.dtsi
index 3f51ec9..320afb6 100644
--- a/arch/arm/boot/dts/msm8974.dtsi
+++ b/arch/arm/boot/dts/msm8974.dtsi
@@ -564,10 +564,12 @@
 		};
 	};
 
-	i2c@f9967000 {
+	i2c@f9967000 { /* BLSP#11 */
 		cell-index = <0>;
 		compatible = "qcom,i2c-qup";
 		reg = <0Xf9967000 0x1000>;
+		#address-cells = <1>;
+		#size-cells = <0>;
 		reg-names = "qup_phys_addr";
 		interrupts = <0 105 0>;
 		interrupt-names = "qup_err_intr";
diff --git a/arch/arm/configs/msm8974_defconfig b/arch/arm/configs/msm8974_defconfig
index d8d2eae..c49ad93 100644
--- a/arch/arm/configs/msm8974_defconfig
+++ b/arch/arm/configs/msm8974_defconfig
@@ -275,6 +275,7 @@
 # CONFIG_BATTERY_MSM is not set
 CONFIG_QPNP_CHARGER=y
 CONFIG_QPNP_BMS=y
+CONFIG_BATTERY_BQ28400=y
 CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y
 CONFIG_SENSORS_QPNP_ADC_CURRENT=y
 CONFIG_THERMAL=y
diff --git a/arch/arm/include/asm/outercache.h b/arch/arm/include/asm/outercache.h
index 53426c6..12f71a1 100644
--- a/arch/arm/include/asm/outercache.h
+++ b/arch/arm/include/asm/outercache.h
@@ -92,6 +92,7 @@
 static inline void outer_flush_all(void) { }
 static inline void outer_inv_all(void) { }
 static inline void outer_disable(void) { }
+static inline void outer_resume(void) { }
 
 #endif
 
diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c
index cb9fc76..bb4da0f 100644
--- a/arch/arm/mm/cache-l2x0.c
+++ b/arch/arm/mm/cache-l2x0.c
@@ -38,6 +38,8 @@
 static unsigned int l2x0_ways;
 static unsigned long sync_reg_offset = L2X0_CACHE_SYNC;
 static void pl310_save(void);
+static void pl310_resume(void);
+static void l2x0_resume(void);
 
 static inline bool is_pl310_rev(int rev)
 {
@@ -378,15 +380,18 @@
 		sync_reg_offset = L2X0_DUMMY_REG;
 #endif
 		outer_cache.set_debug = pl310_set_debug;
+		outer_cache.resume = pl310_resume;
 		break;
 	case L2X0_CACHE_ID_PART_L210:
 		l2x0_ways = (aux >> 13) & 0xf;
 		type = "L210";
+		outer_cache.resume = l2x0_resume;
 		break;
 	default:
 		/* Assume unknown chips have 8 ways */
 		l2x0_ways = 8;
 		type = "L2x0 series";
+		outer_cache.resume = l2x0_resume;
 		break;
 	}
 
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 376750f..7b7e05e 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -343,6 +343,18 @@
 	  SMB349 be operated as a slave device via the power supply
 	  framework.
 
+config SMB350_CHARGER
+	tristate "smb350 charger"
+	depends on I2C
+	help
+	  Say Y to enable battery charging by SMB350 switching mode based
+	  external charger. The device supports stack-cell battery charging.
+	  The driver configures the device volatile parameters
+	  and the charger device works autonomously.
+	  The driver supports charger-enable and charger-suspend/resume.
+	  The driver reports the charger status via the power supply framework.
+	  A charger status change triggers an IRQ via the device STAT pin.
+
 config BATTERY_MSM_FAKE
 	tristate "Fake MSM battery"
 	depends on ARCH_MSM && BATTERY_MSM
@@ -388,6 +400,18 @@
 	help
 	  Say Y here to enable Test sysfs Interface for BQ27520 Drivers.
 
+config BATTERY_BQ28400
+	tristate "BQ28400 battery driver"
+	depends on I2C
+	default n
+	help
+	  Say Y here to enable support for batteries with BQ28400 (I2C) chips.
+	  The bq28400 Texas Instruments Inc device monitors the battery
+	  charging/discharging status via Rsens resistor, typically 10 mohm.
+	  It monitors the battery temperature via Thermistor.
+	  The device monitors the battery level (Relative-State-Of-Charge).
+	  The device is SBS compliant, providing battery info over I2C.
+
 config PM8921_CHARGER
 	tristate "PM8921 Charger driver"
 	depends on MFD_PM8921_CORE
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index 3521cfd..3e74f35 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -48,10 +48,12 @@
 obj-$(CONFIG_PM8058_CHARGER)    += pmic8058-charger.o
 obj-$(CONFIG_ISL9519_CHARGER)   += isl9519q.o
 obj-$(CONFIG_SMB349_CHARGER)   += smb349.o
+obj-$(CONFIG_SMB350_CHARGER)   += smb350_charger.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_BATTERY_BQ28400)	+= bq28400_battery.o
 obj-$(CONFIG_SMB137B_CHARGER)   += smb137b.o
 obj-$(CONFIG_PM8XXX_CCADC)	+= pm8xxx-ccadc.o
 obj-$(CONFIG_PM8921_BMS)	+= pm8921-bms.o
diff --git a/drivers/power/bq28400_battery.c b/drivers/power/bq28400_battery.c
new file mode 100644
index 0000000..39d52cb
--- /dev/null
+++ b/drivers/power/bq28400_battery.c
@@ -0,0 +1,917 @@
+/* Copyright (c) 2012 The Linux Foundation. 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.
+ *
+ */
+
+/*
+ * High Level description:
+ * http://www.ti.com/lit/ds/symlink/bq28400.pdf
+ * Thechnical Reference:
+ * http://www.ti.com/lit/ug/sluu431/sluu431.pdf
+ */
+
+#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/power_supply.h>
+#include <linux/bitops.h>
+#include <linux/regulator/consumer.h>
+#include <linux/printk.h>
+
+#define BQ28400_NAME "bq28400"
+#define BQ28400_REV  "1.0"
+
+/* SBS Commands (page 63)  */
+
+#define SBS_MANUFACTURER_ACCESS		0x00
+#define SBS_BATTERY_MODE		0x03
+#define SBS_TEMPERATURE			0x08
+#define SBS_VOLTAGE			0x09
+#define SBS_CURRENT			0x0A
+#define SBS_AVG_CURRENT			0x0B
+#define SBS_MAX_ERROR			0x0C
+#define SBS_RSOC			0x0D	/* Relative State Of Charge */
+#define SBS_REMAIN_CAPACITY		0x0F
+#define SBS_FULL_CAPACITY		0x10
+#define SBS_CHG_CURRENT			0x14
+#define SBS_CHG_VOLTAGE			0x15
+#define SBS_BATTERY_STATUS		0x16
+#define SBS_CYCLE_COUNT			0x17
+#define SBS_DESIGN_CAPACITY		0x18
+#define SBS_DESIGN_VOLTAGE		0x19
+#define SBS_SPEC_INFO			0x1A
+#define SBS_MANUFACTURE_DATE		0x1B
+#define SBS_SERIAL_NUMBER		0x1C
+#define SBS_MANUFACTURER_NAME		0x20
+#define SBS_DEVICE_NAME			0x21
+#define SBS_DEVICE_CHEMISTRY		0x22
+#define SBS_MANUFACTURER_DATA		0x23
+#define SBS_AUTHENTICATE		0x2F
+#define SBS_CELL_VOLTAGE1		0x3E
+#define SBS_CELL_VOLTAGE2		0x3F
+
+/* Extended SBS Commands (page 71)  */
+
+#define SBS_FET_CONTROL			0x46
+#define SBS_SAFETY_ALERT		0x50
+#define SBS_SAFETY_STATUS		0x51
+#define SBS_PE_ALERT			0x52
+#define SBS_PE_STATUS			0x53
+#define SBS_OPERATION_STATUS		0x54
+#define SBS_CHARGING_STATUS		0x55
+#define SBS_FET_STATUS			0x56
+#define SBS_PACK_VOLTAGE		0x5A
+#define SBS_TS0_TEMPERATURE		0x5E
+#define SBS_FULL_ACCESS_KEY		0x61
+#define SBS_PF_KEY			0x62
+#define SBS_AUTH_KEY3			0x63
+#define SBS_AUTH_KEY2			0x64
+#define SBS_AUTH_KEY1			0x65
+#define SBS_AUTH_KEY0			0x66
+#define SBS_MANUFACTURER_INFO		0x70
+#define SBS_SENSE_RESISTOR		0x71
+#define SBS_TEMP_RANGE			0x72
+
+/* SBS Sub-Commands (16 bits) */
+/* SBS_MANUFACTURER_ACCESS CMD */
+#define SUBCMD_DEVICE_TYPE		0x01
+#define SUBCMD_FIRMWARE_VERSION		0x02
+#define SUBCMD_HARDWARE_VERSION		0x03
+#define SUBCMD_DF_CHECKSUM		0x04
+#define SUBCMD_EDV			0x05
+#define SUBCMD_CHEMISTRY_ID		0x08
+
+/* SBS_CHARGING_STATUS */
+#define CHG_STATUS_BATTERY_DEPLETED	BIT(0)
+#define CHG_STATUS_OVERCHARGE		BIT(1)
+#define CHG_STATUS_OVERCHARGE_CURRENT	BIT(2)
+#define CHG_STATUS_OVERCHARGE_VOLTAGE	BIT(3)
+#define CHG_STATUS_CELL_BALANCING	BIT(6)
+#define CHG_STATUS_HOT_TEMP_CHARGING	BIT(8)
+#define CHG_STATUS_STD1_TEMP_CHARGING	BIT(9)
+#define CHG_STATUS_STD2_TEMP_CHARGING	BIT(10)
+#define CHG_STATUS_LOW_TEMP_CHARGING	BIT(11)
+#define CHG_STATUS_PRECHARGING_EXIT	BIT(13)
+#define CHG_STATUS_SUSPENDED		BIT(14)
+#define CHG_STATUS_DISABLED		BIT(15)
+
+/* SBS_FET_STATUS */
+#define FET_STATUS_DISCHARGE		BIT(1)
+#define FET_STATUS_CHARGE		BIT(2)
+#define FET_STATUS_PRECHARGE		BIT(3)
+
+/* SBS_BATTERY_STATUS */
+#define BAT_STATUS_SBS_ERROR		0x0F
+#define BAT_STATUS_EMPTY		BIT(4)
+#define BAT_STATUS_FULL			BIT(5)
+#define BAT_STATUS_DISCHARGING		BIT(6)
+#define BAT_STATUS_OVER_TEMPERATURE	BIT(12)
+#define BAT_STATUS_OVER_CHARGED		BIT(15)
+
+#define ZERO_DEGREE_CELSIUS_IN_TENTH_KELVIN   (-2731)
+#define BQ_TERMINATION_CURRENT_MA	200
+
+#define BQ_MAX_STR_LEN	32
+
+struct bq28400_device {
+	struct i2c_client	*client;
+	struct delayed_work	periodic_user_space_update_work;
+	struct dentry		*dent;
+	struct power_supply	batt_psy;
+	struct power_supply	*dc_psy;
+	bool			is_charging_enabled;
+};
+
+static struct bq28400_device *bq28400_dev;
+
+static enum power_supply_property pm_power_props[] = {
+	POWER_SUPPLY_PROP_STATUS,
+	POWER_SUPPLY_PROP_CHARGE_TYPE,
+	POWER_SUPPLY_PROP_PRESENT,
+	POWER_SUPPLY_PROP_TECHNOLOGY,
+	POWER_SUPPLY_PROP_VOLTAGE_NOW,
+	POWER_SUPPLY_PROP_CAPACITY,
+	POWER_SUPPLY_PROP_CURRENT_NOW,
+	POWER_SUPPLY_PROP_CURRENT_AVG,
+	POWER_SUPPLY_PROP_TEMP,
+	POWER_SUPPLY_PROP_CHARGE_FULL,
+	POWER_SUPPLY_PROP_CHARGE_NOW,
+	POWER_SUPPLY_PROP_MODEL_NAME,
+	POWER_SUPPLY_PROP_MANUFACTURER,
+};
+
+struct debug_reg {
+	char	*name;
+	u8	reg;
+	u16	subcmd;
+};
+
+#define BQ28400_DEBUG_REG(x) {#x, SBS_##x, 0}
+#define BQ28400_DEBUG_SUBREG(x, y) {#y, SBS_##x, SUBCMD_##y}
+
+/* Note: Some register can be read only in Unsealed mode */
+static struct debug_reg bq28400_debug_regs[] = {
+	BQ28400_DEBUG_REG(MANUFACTURER_ACCESS),
+	BQ28400_DEBUG_REG(BATTERY_MODE),
+	BQ28400_DEBUG_REG(TEMPERATURE),
+	BQ28400_DEBUG_REG(VOLTAGE),
+	BQ28400_DEBUG_REG(CURRENT),
+	BQ28400_DEBUG_REG(AVG_CURRENT),
+	BQ28400_DEBUG_REG(MAX_ERROR),
+	BQ28400_DEBUG_REG(RSOC),
+	BQ28400_DEBUG_REG(REMAIN_CAPACITY),
+	BQ28400_DEBUG_REG(FULL_CAPACITY),
+	BQ28400_DEBUG_REG(CHG_CURRENT),
+	BQ28400_DEBUG_REG(CHG_VOLTAGE),
+	BQ28400_DEBUG_REG(BATTERY_STATUS),
+	BQ28400_DEBUG_REG(CYCLE_COUNT),
+	BQ28400_DEBUG_REG(DESIGN_CAPACITY),
+	BQ28400_DEBUG_REG(DESIGN_VOLTAGE),
+	BQ28400_DEBUG_REG(SPEC_INFO),
+	BQ28400_DEBUG_REG(MANUFACTURE_DATE),
+	BQ28400_DEBUG_REG(SERIAL_NUMBER),
+	BQ28400_DEBUG_REG(MANUFACTURER_NAME),
+	BQ28400_DEBUG_REG(DEVICE_NAME),
+	BQ28400_DEBUG_REG(DEVICE_CHEMISTRY),
+	BQ28400_DEBUG_REG(MANUFACTURER_DATA),
+	BQ28400_DEBUG_REG(AUTHENTICATE),
+	BQ28400_DEBUG_REG(CELL_VOLTAGE1),
+	BQ28400_DEBUG_REG(CELL_VOLTAGE2),
+	BQ28400_DEBUG_REG(SAFETY_ALERT),
+	BQ28400_DEBUG_REG(SAFETY_STATUS),
+	BQ28400_DEBUG_REG(PE_ALERT),
+	BQ28400_DEBUG_REG(PE_STATUS),
+	BQ28400_DEBUG_REG(OPERATION_STATUS),
+	BQ28400_DEBUG_REG(CHARGING_STATUS),
+	BQ28400_DEBUG_REG(FET_STATUS),
+	BQ28400_DEBUG_REG(FULL_ACCESS_KEY),
+	BQ28400_DEBUG_REG(PF_KEY),
+	BQ28400_DEBUG_REG(MANUFACTURER_INFO),
+	BQ28400_DEBUG_REG(SENSE_RESISTOR),
+	BQ28400_DEBUG_REG(TEMP_RANGE),
+	BQ28400_DEBUG_SUBREG(MANUFACTURER_ACCESS, DEVICE_TYPE),
+	BQ28400_DEBUG_SUBREG(MANUFACTURER_ACCESS, FIRMWARE_VERSION),
+	BQ28400_DEBUG_SUBREG(MANUFACTURER_ACCESS, HARDWARE_VERSION),
+	BQ28400_DEBUG_SUBREG(MANUFACTURER_ACCESS, DF_CHECKSUM),
+	BQ28400_DEBUG_SUBREG(MANUFACTURER_ACCESS, EDV),
+	BQ28400_DEBUG_SUBREG(MANUFACTURER_ACCESS, CHEMISTRY_ID),
+};
+
+static int bq28400_read_reg(struct i2c_client *client, u8 reg)
+{
+	int val;
+
+	val = i2c_smbus_read_word_data(client, reg);
+	if (val < 0)
+		pr_err("i2c read fail. reg = 0x%x.ret = %d.\n", reg, val);
+	else
+		pr_debug("reg = 0x%02X.val = 0x%04X.\n", reg , val);
+
+	return val;
+}
+
+static int bq28400_write_reg(struct i2c_client *client, u8 reg, u16 val)
+{
+	int ret;
+
+	ret = i2c_smbus_write_word_data(client, reg, val);
+	if (ret < 0)
+		pr_err("i2c read fail. reg = 0x%x.val = 0x%x.ret = %d.\n",
+		       reg, val, ret);
+	else
+		pr_debug("reg = 0x%02X.val = 0x%02X.\n", reg , val);
+
+	return ret;
+}
+
+static int bq28400_read_subcmd(struct i2c_client *client, u8 reg, u16 subcmd)
+{
+	int ret;
+	u8 buf[4];
+	u16 val = 0;
+
+	buf[0] = reg;
+	buf[1] = subcmd & 0xFF;
+	buf[2] = (subcmd >> 8) & 0xFF;
+
+	/* Control sub-command */
+	ret = i2c_master_send(client, buf, 3);
+	if (ret < 0) {
+		pr_err("i2c tx fail. reg = 0x%x.ret = %d.\n", reg, ret);
+		return ret;
+	}
+	udelay(66);
+
+	/* Read Result of subcmd */
+	ret = i2c_master_send(client, buf, 1);
+	memset(buf, 0xAA, sizeof(buf));
+	ret = i2c_master_recv(client, buf, 2);
+	if (ret < 0) {
+		pr_err("i2c rx fail. reg = 0x%x.ret = %d.\n", reg, ret);
+		return ret;
+	}
+	val = (buf[1] << 8) + buf[0];
+
+	pr_debug("reg = 0x%02X.subcmd = 0x%x.val = 0x%04X.\n",
+		 reg , subcmd, val);
+
+	return val;
+}
+
+static int bq28400_read_block(struct i2c_client *client, u8 reg,
+			      u8 len, u8 *buf)
+{
+	int ret;
+	u32 val;
+
+	ret = i2c_smbus_read_i2c_block_data(client, reg, len, buf);
+	val = buf[0] + (buf[1] << 8) + (buf[2] << 16) + (buf[3] << 24);
+
+	if (ret < 0)
+		pr_err("i2c read fail. reg = 0x%x.ret = %d.\n", reg, ret);
+	else
+		pr_debug("reg = 0x%02X.val = 0x%04X.\n", reg , val);
+
+	return val;
+}
+
+/*
+ * Read a string from a device.
+ * Returns string length on success or error on failure (negative value).
+ */
+static int bq28400_read_string(struct i2c_client *client, u8 reg, char *str,
+			       u8 max_len)
+{
+	int ret;
+	int len;
+
+	ret = bq28400_read_block(client, reg, max_len, str);
+	if (ret < 0)
+		return ret;
+
+	len = str[0]; /* Actual length */
+	if (len > max_len - 2) { /* reduce len byte and null */
+		pr_err("len = %d invalid.\n", len);
+		return -EINVAL;
+	}
+
+	memcpy(&str[0], &str[1], len); /* Move sting to the start */
+	str[len] = 0; /* put NULL after actual size */
+
+	pr_debug("len = %d.str = %s.\n", len, str);
+
+	return len;
+}
+
+#define BQ28400_INVALID_TEMPERATURE	-999
+/*
+ * Return the battery temperature in tenths of degree Celsius
+ * Or -99.9 C if something fails.
+ */
+static int bq28400_read_temperature(struct i2c_client *client)
+{
+	int temp;
+
+	/* temperature resolution 0.1 Kelvin */
+	temp = bq28400_read_reg(client, SBS_TEMPERATURE);
+	if (temp < 0)
+		return BQ28400_INVALID_TEMPERATURE;
+
+	temp = temp + ZERO_DEGREE_CELSIUS_IN_TENTH_KELVIN;
+
+	pr_debug("temp = %d C\n", temp/10);
+
+	return temp;
+}
+
+/*
+ * Return the battery Voltage in milivolts 0..20 V
+ * Or < 0 if something fails.
+ */
+static int bq28400_read_voltage(struct i2c_client *client)
+{
+	int mvolt = 0;
+
+	mvolt = bq28400_read_reg(client, SBS_VOLTAGE);
+	if (mvolt < 0)
+		return mvolt;
+
+	pr_debug("volt = %d mV.\n", mvolt);
+
+	return mvolt;
+}
+
+/*
+ * Return the battery Current in miliamps
+ * Or 0 if something fails.
+ * Positive current indicates charging
+ * Negative current indicates discharging.
+ * Current-now is calculated every second.
+ */
+static int bq28400_read_current(struct i2c_client *client)
+{
+	s16 current_ma = 0;
+
+	current_ma = bq28400_read_reg(client, SBS_CURRENT);
+
+	pr_debug("current = %d mA.\n", current_ma);
+
+	return current_ma;
+}
+
+/*
+ * Return the Average battery Current in miliamps
+ * Or 0 if something fails.
+ * Positive current indicates charging
+ * Negative current indicates discharging.
+ * Average Current is the rolling 1 minute average current.
+ */
+static int bq28400_read_avg_current(struct i2c_client *client)
+{
+	s16 current_ma = 0;
+
+	current_ma = bq28400_read_reg(client, SBS_AVG_CURRENT);
+
+	pr_debug("avg_current=%d mA.\n", current_ma);
+
+	return current_ma;
+}
+
+/*
+ * Return the battery Relative-State-Of-Charge 0..100 %
+ * Or 0 if something fails.
+ */
+static int bq28400_read_rsoc(struct i2c_client *client)
+{
+	int percentage = 0;
+
+	/* This register is only 1 byte */
+	percentage = i2c_smbus_read_byte_data(client, SBS_RSOC);
+
+	if (percentage < 0)
+		return 0;
+
+	pr_debug("percentage = %d.\n", percentage);
+
+	return percentage;
+}
+
+/*
+ * Return the battery Capacity in mAh.
+ * Or 0 if something fails.
+ */
+static int bq28400_read_full_capacity(struct i2c_client *client)
+{
+	int capacity = 0;
+
+	capacity = bq28400_read_reg(client, SBS_FULL_CAPACITY);
+	if (capacity < 0)
+		return 0;
+
+	pr_debug("full-capacity = %d mAh.\n", capacity);
+
+	return capacity;
+}
+
+/*
+ * Return the battery Capacity in mAh.
+ * Or 0 if something fails.
+ */
+static int bq28400_read_remain_capacity(struct i2c_client *client)
+{
+	int capacity = 0;
+
+	capacity = bq28400_read_reg(client, SBS_REMAIN_CAPACITY);
+	if (capacity < 0)
+		return 0;
+
+	pr_debug("remain-capacity = %d mAh.\n", capacity);
+
+	return capacity;
+}
+
+static int bq28400_enable_charging(struct bq28400_device *bq28400_dev,
+				   bool enable)
+{
+	int ret;
+	static bool is_charging_enabled;
+
+	if (bq28400_dev->dc_psy == NULL) {
+		bq28400_dev->dc_psy = power_supply_get_by_name("dc");
+		if (bq28400_dev->dc_psy == NULL) {
+			pr_err("fail to get dc-psy.\n");
+			return -ENODEV;
+		}
+	}
+
+	if (is_charging_enabled == enable) {
+		pr_debug("Charging enable already = %d.\n", enable);
+		return 0;
+	}
+
+	ret = power_supply_set_online(bq28400_dev->dc_psy, enable);
+	if (ret < 0) {
+		pr_err("fail to set dc-psy online to %d.\n", enable);
+		return ret;
+	}
+
+	is_charging_enabled = enable;
+
+	pr_debug("Charging enable = %d.\n", enable);
+
+	return 0;
+}
+
+static int bq28400_get_prop_status(struct i2c_client *client)
+{
+	int status = POWER_SUPPLY_STATUS_UNKNOWN;
+	int rsoc;
+	s16 current_ma = 0;
+	u16 battery_status;
+
+	battery_status = bq28400_read_reg(client, SBS_BATTERY_STATUS);
+
+	if (battery_status & BAT_STATUS_EMPTY)
+		pr_debug("Battery report Empty.\n");
+
+	/* Battery may report FULL before rsoc is 100%
+	 * for protection and cell-balancing.
+	 * The FULL report may remain when rsoc drops from 100%.
+	 */
+	if (battery_status & BAT_STATUS_FULL) {
+		pr_debug("Battery report Full.\n");
+		bq28400_enable_charging(bq28400_dev, false);
+		return POWER_SUPPLY_STATUS_FULL;
+	}
+
+	rsoc = bq28400_read_rsoc(client);
+	current_ma = bq28400_read_current(client);
+
+	if (rsoc == 100) {
+		bq28400_enable_charging(bq28400_dev, false);
+		pr_debug("Full.\n");
+		return POWER_SUPPLY_STATUS_FULL;
+	}
+
+	/*
+	* Positive current indicates charging
+	* Negative current indicates discharging.
+	* Charging is stopped at termination-current.
+	*/
+	if (current_ma < 0) {
+		bq28400_enable_charging(bq28400_dev, true);
+		pr_debug("Discharging.\n");
+		status = POWER_SUPPLY_STATUS_DISCHARGING;
+	} else if (current_ma > BQ_TERMINATION_CURRENT_MA) {
+		pr_debug("Charging.\n");
+		status = POWER_SUPPLY_STATUS_CHARGING;
+	} else {
+		pr_debug("Not Charging.\n");
+		status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+	}
+
+	return status;
+}
+
+static int bq28400_get_prop_charge_type(struct i2c_client *client)
+{
+	u16 battery_status;
+	u16 chg_status;
+	u16 fet_status;
+
+	battery_status = bq28400_read_reg(client, SBS_BATTERY_STATUS);
+	chg_status = bq28400_read_reg(client, SBS_CHARGING_STATUS);
+	fet_status = bq28400_read_reg(client, SBS_FET_STATUS);
+
+	if (battery_status & BAT_STATUS_DISCHARGING) {
+		pr_debug("Discharging.\n");
+		return POWER_SUPPLY_CHARGE_TYPE_NONE;
+	}
+
+	if (fet_status & FET_STATUS_PRECHARGE) {
+		pr_debug("Pre-Charging.\n");
+		return POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
+	}
+
+	if (chg_status & CHG_STATUS_HOT_TEMP_CHARGING) {
+		pr_debug("Hot-Temp-Charging.\n");
+		return POWER_SUPPLY_CHARGE_TYPE_FAST;
+	}
+
+	if (chg_status & CHG_STATUS_LOW_TEMP_CHARGING) {
+		pr_debug("Low-Temp-Charging.\n");
+		return POWER_SUPPLY_CHARGE_TYPE_FAST;
+	}
+
+	if (chg_status & CHG_STATUS_STD1_TEMP_CHARGING) {
+		pr_debug("STD1-Temp-Charging.\n");
+		return POWER_SUPPLY_CHARGE_TYPE_FAST;
+	}
+
+	if (chg_status & CHG_STATUS_STD2_TEMP_CHARGING) {
+		pr_debug("STD2-Temp-Charging.\n");
+		return POWER_SUPPLY_CHARGE_TYPE_FAST;
+	}
+
+	if (chg_status & CHG_STATUS_BATTERY_DEPLETED)
+		pr_debug("battery_depleted.\n");
+
+	if (chg_status & CHG_STATUS_CELL_BALANCING)
+		pr_debug("cell_balancing.\n");
+
+	if (chg_status & CHG_STATUS_OVERCHARGE) {
+		pr_err("overcharge fault.\n");
+		return POWER_SUPPLY_CHARGE_TYPE_NONE;
+	}
+
+	if (chg_status & CHG_STATUS_SUSPENDED) {
+		pr_info("Suspended.\n");
+		return POWER_SUPPLY_CHARGE_TYPE_NONE;
+	}
+
+	if (chg_status & CHG_STATUS_DISABLED) {
+		pr_info("Disabled.\n");
+		return POWER_SUPPLY_CHARGE_TYPE_NONE;
+	}
+
+	return POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
+}
+
+static bool bq28400_get_prop_present(struct i2c_client *client)
+{
+	int val;
+
+	val = bq28400_read_reg(client, SBS_BATTERY_STATUS);
+
+	/* If the bq28400 is inside the battery pack
+	 * then when battery is removed the i2c transfer will fail.
+	 */
+
+	if (val < 0)
+		return false;
+
+	/* TODO - support when bq28400 is not embedded in battery pack */
+
+	return true;
+}
+
+/*
+ * User sapce read the battery info.
+ * Get data online via I2C from the battery gauge.
+ */
+static int bq28400_get_property(struct power_supply *psy,
+				  enum power_supply_property psp,
+				  union power_supply_propval *val)
+{
+	int ret = 0;
+	struct bq28400_device *dev = container_of(psy,
+						  struct bq28400_device,
+						  batt_psy);
+	struct i2c_client *client = dev->client;
+	static char str[BQ_MAX_STR_LEN];
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_STATUS:
+		val->intval = bq28400_get_prop_status(client);
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_TYPE:
+		val->intval = bq28400_get_prop_charge_type(client);
+		break;
+	case POWER_SUPPLY_PROP_PRESENT:
+		val->intval = bq28400_get_prop_present(client);
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+		val->intval = bq28400_read_voltage(client);
+		val->intval *= 1000; /* mV to uV */
+		break;
+	case POWER_SUPPLY_PROP_CAPACITY:
+		val->intval = bq28400_read_rsoc(client);
+		break;
+	case POWER_SUPPLY_PROP_CURRENT_NOW:
+		/* Positive current indicates drawing */
+		val->intval = -bq28400_read_current(client);
+		val->intval *= 1000; /* mA to uA */
+		break;
+	case POWER_SUPPLY_PROP_CURRENT_AVG:
+		/* Positive current indicates drawing */
+		val->intval = -bq28400_read_avg_current(client);
+		val->intval *= 1000; /* mA to uA */
+		break;
+	case POWER_SUPPLY_PROP_TEMP:
+		val->intval = bq28400_read_temperature(client);
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_FULL:
+		val->intval = bq28400_read_full_capacity(client);
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_NOW:
+		val->intval = bq28400_read_remain_capacity(client);
+		break;
+	case POWER_SUPPLY_PROP_TECHNOLOGY:
+		val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
+		break;
+	case POWER_SUPPLY_PROP_MODEL_NAME:
+		bq28400_read_string(client, SBS_DEVICE_NAME, str, 20);
+		val->strval = str;
+		break;
+	case POWER_SUPPLY_PROP_MANUFACTURER:
+		bq28400_read_string(client, SBS_MANUFACTURER_NAME, str, 20);
+		val->strval = str;
+		break;
+	default:
+		pr_err(" psp %d Not supoprted.\n", psp);
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static int bq28400_set_reg(void *data, u64 val)
+{
+	struct debug_reg *dbg = data;
+	u8 reg = dbg->reg;
+	int ret;
+	struct i2c_client *client = bq28400_dev->client;
+
+	ret = bq28400_write_reg(client, reg, val);
+
+	return ret;
+}
+
+static int bq28400_get_reg(void *data, u64 *val)
+{
+	struct debug_reg *dbg = data;
+	u8 reg = dbg->reg;
+	u16 subcmd = dbg->subcmd;
+	int ret;
+	struct i2c_client *client = bq28400_dev->client;
+
+	if (subcmd)
+		ret = bq28400_read_subcmd(client, reg, subcmd);
+	else
+		ret = bq28400_read_reg(client, reg);
+	if (ret < 0)
+		return ret;
+
+	*val = ret;
+
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(reg_fops, bq28400_get_reg, bq28400_set_reg,
+			"0x%04llx\n");
+
+static int bq28400_create_debugfs_entries(struct bq28400_device *bq28400_dev)
+{
+	int i;
+
+	bq28400_dev->dent = debugfs_create_dir(BQ28400_NAME, NULL);
+	if (IS_ERR(bq28400_dev->dent)) {
+		pr_err("bq28400 driver couldn't create debugfs dir\n");
+		return -EFAULT;
+	}
+
+	for (i = 0 ; i < ARRAY_SIZE(bq28400_debug_regs) ; i++) {
+		char *name = bq28400_debug_regs[i].name;
+		struct dentry *file;
+		void *data = &bq28400_debug_regs[i];
+
+		file = debugfs_create_file(name, 0644, bq28400_dev->dent,
+					   data, &reg_fops);
+		if (IS_ERR(file)) {
+			pr_err("debugfs_create_file %s failed.\n", name);
+			return -EFAULT;
+		}
+	}
+
+	return 0;
+}
+
+static int bq28400_set_property(struct power_supply *psy,
+				  enum power_supply_property psp,
+				  const union power_supply_propval *val)
+{
+	pr_debug("psp = %d.val = %d.\n", psp, val->intval);
+
+	return -EINVAL;
+}
+
+static void bq28400_external_power_changed(struct power_supply *psy)
+{
+	pr_debug("Notify power_supply_changed.\n");
+	/* Update LEDs and notify uevents */
+	power_supply_changed(&bq28400_dev->batt_psy);
+}
+
+static int __devinit bq28400_register_psy(struct bq28400_device *bq28400_dev)
+{
+	int ret;
+
+	bq28400_dev->batt_psy.name = "battery";
+	bq28400_dev->batt_psy.type = POWER_SUPPLY_TYPE_BATTERY;
+	bq28400_dev->batt_psy.num_supplicants = 0;
+	bq28400_dev->batt_psy.properties = pm_power_props;
+	bq28400_dev->batt_psy.num_properties = ARRAY_SIZE(pm_power_props);
+	bq28400_dev->batt_psy.get_property = bq28400_get_property;
+	bq28400_dev->batt_psy.set_property = bq28400_set_property;
+	bq28400_dev->batt_psy.external_power_changed =
+		bq28400_external_power_changed;
+
+	ret = power_supply_register(&bq28400_dev->client->dev,
+				&bq28400_dev->batt_psy);
+	if (ret) {
+		pr_err("failed to register power_supply. ret=%d.\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+/**
+ * Update userspace every 1 minute.
+ * Normally it takes more than 120 minutes (two hours) to
+ * charge/discahrge the battery,
+ * so updating every 1 minute should be enough for 1% change
+ * detection.
+ * Any immidiate change detected by the DC charger is notified
+ * by the bq28400_external_power_changed callback, which notify
+ * the user space.
+ */
+static void bq28400_periodic_user_space_update_worker(struct work_struct *work)
+{
+	u32 delay_msec = 60*1000;
+
+	pr_debug("Notify user space.\n");
+
+	/* Notify user space via kobject_uevent change notification */
+	power_supply_changed(&bq28400_dev->batt_psy);
+
+	schedule_delayed_work(&bq28400_dev->periodic_user_space_update_work,
+			      round_jiffies_relative(msecs_to_jiffies
+						     (delay_msec)));
+}
+
+static int __devinit bq28400_probe(struct i2c_client *client,
+				   const struct i2c_device_id *id)
+{
+	int ret = 0;
+
+	if (!i2c_check_functionality(client->adapter,
+				I2C_FUNC_SMBUS_BYTE_DATA)) {
+		pr_err(" i2c func fail.\n");
+		return -EIO;
+	}
+
+	bq28400_dev = kzalloc(sizeof(*bq28400_dev), GFP_KERNEL);
+	if (!bq28400_dev) {
+		pr_err(" alloc fail.\n");
+		return -ENOMEM;
+	}
+
+	bq28400_dev->client = client;
+	i2c_set_clientdata(client, bq28400_dev);
+
+	ret = bq28400_register_psy(bq28400_dev);
+	if (ret) {
+		pr_err(" bq28400_register_psy fail.\n");
+		goto err_register_psy;
+	}
+
+	ret = bq28400_create_debugfs_entries(bq28400_dev);
+	if (ret) {
+		pr_err(" bq28400_create_debugfs_entries fail.\n");
+		goto err_debugfs;
+	}
+
+	INIT_DELAYED_WORK(&bq28400_dev->periodic_user_space_update_work,
+			  bq28400_periodic_user_space_update_worker);
+
+	schedule_delayed_work(&bq28400_dev->periodic_user_space_update_work,
+			      msecs_to_jiffies(1000));
+
+	pr_info("Device is ready.\n");
+
+	return 0;
+
+err_debugfs:
+	if (bq28400_dev->dent)
+		debugfs_remove_recursive(bq28400_dev->dent);
+	power_supply_unregister(&bq28400_dev->batt_psy);
+err_register_psy:
+	kfree(bq28400_dev);
+	bq28400_dev = NULL;
+
+	pr_info("FAIL.\n");
+
+	return ret;
+}
+
+static int __devexit bq28400_remove(struct i2c_client *client)
+{
+	struct bq28400_device *bq28400_dev = i2c_get_clientdata(client);
+
+	power_supply_unregister(&bq28400_dev->batt_psy);
+	if (bq28400_dev->dent)
+		debugfs_remove_recursive(bq28400_dev->dent);
+	kfree(bq28400_dev);
+	bq28400_dev = NULL;
+
+	return 0;
+}
+
+static const struct of_device_id bq28400_match[] = {
+	{ .compatible = "ti,bq28400-battery", },
+	{ },
+	};
+
+static const struct i2c_device_id bq28400_id[] = {
+	{BQ28400_NAME, 0},
+	{},
+};
+
+MODULE_DEVICE_TABLE(i2c, bq28400_id);
+
+static struct i2c_driver bq28400_driver = {
+	.driver	= {
+		.name	= BQ28400_NAME,
+		.owner	= THIS_MODULE,
+		.of_match_table = of_match_ptr(bq28400_match),
+	},
+	.probe		= bq28400_probe,
+	.remove		= __devexit_p(bq28400_remove),
+	.id_table	= bq28400_id,
+};
+
+static int __init bq28400_init(void)
+{
+	pr_info(" bq28400 driver rev %s.\n", BQ28400_REV);
+
+	return i2c_add_driver(&bq28400_driver);
+}
+module_init(bq28400_init);
+
+static void __exit bq28400_exit(void)
+{
+	return i2c_del_driver(&bq28400_driver);
+}
+module_exit(bq28400_exit);
+
+MODULE_DESCRIPTION("Driver for BQ28400 charger chip");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("i2c:" BQ28400_NAME);
diff --git a/drivers/power/smb350_charger.c b/drivers/power/smb350_charger.c
new file mode 100644
index 0000000..93e208c
--- /dev/null
+++ b/drivers/power/smb350_charger.c
@@ -0,0 +1,865 @@
+/* Copyright (c) 2012 The Linux Foundation. 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/power_supply.h>
+#include <linux/i2c/smb350.h>
+#include <linux/bitops.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/printk.h>
+
+/* Register definitions */
+#define CHG_CURRENT_REG			0x00	/* Non-Volatile + mirror */
+#define CHG_OTHER_CURRENT_REG		0x01	/* Non-Volatile + mirror */
+#define VAR_FUNC_REG			0x02	/* Non-Volatile + mirror */
+#define FLOAT_VOLTAGE_REG		0x03	/* Non-Volatile + mirror */
+#define CHG_CTRL_REG			0x04	/* Non-Volatile + mirror */
+#define STAT_TIMER_REG			0x05	/* Non-Volatile + mirror */
+#define PIN_ENABLE_CTRL_REG		0x06	/* Non-Volatile + mirror */
+#define THERM_CTRL_A_REG		0x07	/* Non-Volatile + mirror */
+#define SYSOK_USB3_SELECT_REG		0x08	/* Non-Volatile + mirror */
+#define CTRL_FUNCTIONS_REG		0x09	/* Non-Volatile + mirror */
+#define OTG_TLIM_THERM_CNTRL_REG	0x0A	/* Non-Volatile + mirror */
+#define TEMP_MONITOR_REG		0x0B	/* Non-Volatile + mirror */
+#define FAULT_IRQ_REG			0x0C	/* Non-Volatile */
+#define IRQ_ENABLE_REG			0x0D	/* Non-Volatile */
+#define SYSOK_REG			0x0E	/* Non-Volatile + mirror */
+
+#define AUTO_INPUT_VOLT_DETECT_REG	0x10	/* Non-Volatile Read-Only */
+#define STATUS_IRQ_REG			0x11	/* Non-Volatile Read-Only */
+#define I2C_SLAVE_ADDR_REG		0x12	/* Non-Volatile Read-Only */
+
+#define CMD_A_REG			0x30	/* Volatile Read-Write */
+#define CMD_B_REG			0x31	/* Volatile Read-Write */
+#define CMD_C_REG			0x33	/* Volatile Read-Write */
+
+#define IRQ_STATUS_A_REG		0x35	/* Volatile Read-Only */
+#define IRQ_STATUS_B_REG		0x36	/* Volatile Read-Only */
+#define IRQ_STATUS_C_REG		0x37	/* Volatile Read-Only */
+#define IRQ_STATUS_D_REG		0x38	/* Volatile Read-Only */
+#define IRQ_STATUS_E_REG		0x39	/* Volatile Read-Only */
+#define IRQ_STATUS_F_REG		0x3A	/* Volatile Read-Only */
+
+#define STATUS_A_REG			0x3B	/* Volatile Read-Only */
+#define STATUS_B_REG			0x3D	/* Volatile Read-Only */
+/* Note: STATUS_C_REG was removed from SMB349 to SMB350 */
+#define STATUS_D_REG			0x3E	/* Volatile Read-Only */
+#define STATUS_E_REG			0x3F	/* Volatile Read-Only */
+
+#define IRQ_STATUS_NUM (IRQ_STATUS_F_REG - IRQ_STATUS_A_REG + 1)
+
+/* Status bits and masks */
+#define SMB350_MASK(BITS, POS)		((u8)(((1 << BITS) - 1) << POS))
+#define FAST_CHG_CURRENT_MASK		SMB350_MASK(4, 4)
+
+#define SMB350_FAST_CHG_MIN_MA		1000
+#define SMB350_FAST_CHG_STEP_MA		200
+#define SMB350_FAST_CHG_MAX_MA		3600
+
+#define TERM_CURRENT_MASK		SMB350_MASK(3, 2)
+
+#define SMB350_TERM_CUR_MIN_MA		200
+#define SMB350_TERM_CUR_STEP_MA		100
+#define SMB350_TERM_CUR_MAX_MA		700
+
+#define CMD_A_VOLATILE_WR_PERM		BIT(7)
+#define CHG_CTRL_CURR_TERM_END_CHG	BIT(6)
+
+enum smb350_chg_status {
+	SMB_CHG_STATUS_NONE		= 0,
+	SMB_CHG_STATUS_PRE_CHARGE	= 1,
+	SMB_CHG_STATUS_FAST_CHARGE	= 2,
+	SMB_CHG_STATUS_TAPER_CHARGE	= 3,
+};
+
+static const char * const smb350_chg_status[] = {
+	"none",
+	"pre-charge",
+	"fast-charge",
+	"taper-charge"
+};
+
+struct smb350_device {
+	/* setup */
+	int			chg_current_ma;
+	int			term_current_ma;
+	int			chg_en_n_gpio;
+	int			chg_susp_n_gpio;
+	int			stat_gpio;
+	int			irq;
+	/* internal */
+	enum smb350_chg_status	chg_status;
+	struct i2c_client	*client;
+	struct delayed_work	irq_work;
+	struct dentry		*dent;
+	struct wake_lock	chg_wake_lock;
+	struct power_supply	dc_psy;
+};
+
+static struct smb350_device *smb350_dev;
+
+struct debug_reg {
+	char	*name;
+	u8	reg;
+};
+
+#define SMB350_DEBUG_REG(x) {#x, x##_REG}
+
+static struct debug_reg smb350_debug_regs[] = {
+	SMB350_DEBUG_REG(CHG_CURRENT),
+	SMB350_DEBUG_REG(CHG_OTHER_CURRENT),
+	SMB350_DEBUG_REG(VAR_FUNC),
+	SMB350_DEBUG_REG(FLOAT_VOLTAGE),
+	SMB350_DEBUG_REG(CHG_CTRL),
+	SMB350_DEBUG_REG(STAT_TIMER),
+	SMB350_DEBUG_REG(PIN_ENABLE_CTRL),
+	SMB350_DEBUG_REG(THERM_CTRL_A),
+	SMB350_DEBUG_REG(SYSOK_USB3_SELECT),
+	SMB350_DEBUG_REG(CTRL_FUNCTIONS),
+	SMB350_DEBUG_REG(OTG_TLIM_THERM_CNTRL),
+	SMB350_DEBUG_REG(TEMP_MONITOR),
+	SMB350_DEBUG_REG(FAULT_IRQ),
+	SMB350_DEBUG_REG(IRQ_ENABLE),
+	SMB350_DEBUG_REG(SYSOK),
+	SMB350_DEBUG_REG(AUTO_INPUT_VOLT_DETECT),
+	SMB350_DEBUG_REG(STATUS_IRQ),
+	SMB350_DEBUG_REG(I2C_SLAVE_ADDR),
+	SMB350_DEBUG_REG(CMD_A),
+	SMB350_DEBUG_REG(CMD_B),
+	SMB350_DEBUG_REG(CMD_C),
+	SMB350_DEBUG_REG(IRQ_STATUS_A),
+	SMB350_DEBUG_REG(IRQ_STATUS_B),
+	SMB350_DEBUG_REG(IRQ_STATUS_C),
+	SMB350_DEBUG_REG(IRQ_STATUS_D),
+	SMB350_DEBUG_REG(IRQ_STATUS_E),
+	SMB350_DEBUG_REG(IRQ_STATUS_F),
+	SMB350_DEBUG_REG(STATUS_A),
+	SMB350_DEBUG_REG(STATUS_B),
+	SMB350_DEBUG_REG(STATUS_D),
+	SMB350_DEBUG_REG(STATUS_E),
+};
+
+/*
+ * Read 8-bit register value. return negative value on error.
+ */
+static int smb350_read_reg(struct i2c_client *client, u8 reg)
+{
+	int val;
+
+	val = i2c_smbus_read_byte_data(client, reg);
+	if (val < 0)
+		pr_err("i2c read fail. reg=0x%x.ret=%d.\n", reg, val);
+	else
+		pr_debug("reg=0x%02X.val=0x%02X.\n", reg , val);
+
+	return val;
+}
+
+/*
+ * Write 8-bit register value. return negative value on error.
+ */
+static int smb350_write_reg(struct i2c_client *client, u8 reg, u8 val)
+{
+	int ret;
+
+	ret = i2c_smbus_write_byte_data(client, reg, val);
+	if (ret < 0)
+		pr_err("i2c read fail. reg=0x%x.val=0x%x.ret=%d.\n",
+		       reg, val, ret);
+	else
+		pr_debug("reg=0x%02X.val=0x%02X.\n", reg , val);
+
+	return ret;
+}
+
+static int smb350_masked_write(struct i2c_client *client, int reg, u8 mask,
+			       u8 val)
+{
+	int ret;
+	int temp;
+	int shift = find_first_bit((unsigned long *) &mask, 8);
+
+	temp = smb350_read_reg(client, reg);
+	if (temp < 0)
+		return temp;
+
+	temp &= ~mask;
+	temp |= (val << shift) & mask;
+	ret = smb350_write_reg(client, reg, temp);
+
+	return ret;
+}
+
+#define SMB350_FLOAT_VOLT_BASE_MV 6920
+#define SMB350_FLOAT_VOLT_STEP_MV   40
+#define SMB350_FLOAT_VOLT_MAX_MV  (6920 + 0x2F * 40)
+
+/* Fast-to-Taper charging volatge */
+static int smb350_get_float_voltage(struct i2c_client *client)
+{
+	u16 val = smb350_read_reg(client, STATUS_A_REG);
+
+	val = SMB350_FLOAT_VOLT_BASE_MV +
+		((val & 0x2F) * SMB350_FLOAT_VOLT_STEP_MV);
+
+	return val;
+}
+
+static bool smb350_is_dc_present(struct i2c_client *client)
+{
+	u16 irq_status_f = smb350_read_reg(client, IRQ_STATUS_F_REG);
+	bool power_ok = irq_status_f & 0x01;
+
+	/* Power-ok , IRQ_STATUS_F_REG bit#0 */
+	if (power_ok)
+		pr_debug("DC is present.\n");
+	else
+		pr_debug("DC is missing.\n");
+
+	return power_ok;
+}
+
+static bool smb350_is_charging(struct i2c_client *client)
+{
+	int val;
+	bool is_charging;
+
+	val = smb350_read_reg(client, STATUS_B_REG);
+	if (val < 0)
+		return false;
+
+	val = (val >> 1) & 0x3;
+
+	is_charging = (val != 0);
+
+	return is_charging;
+}
+
+static int smb350_get_prop_charge_type(struct smb350_device *dev)
+{
+	int status_b;
+	enum smb350_chg_status status;
+	int chg_type = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
+	bool chg_enabled;
+	bool charger_err;
+	struct i2c_client *client = dev->client;
+
+	status_b = smb350_read_reg(client, STATUS_B_REG);
+	if (status_b < 0) {
+		pr_err("failed to read STATUS_B_REG.\n");
+		return POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
+	}
+
+	chg_enabled = (bool) (status_b & 0x01);
+	charger_err = (bool) (status_b & (1<<6));
+
+	if (!chg_enabled) {
+		pr_warn("Charging not enabled.\n");
+		return POWER_SUPPLY_CHARGE_TYPE_NONE;
+	}
+
+	if (charger_err) {
+		pr_warn("Charger error detected.\n");
+		return POWER_SUPPLY_CHARGE_TYPE_NONE;
+	}
+
+	status = (status_b >> 1) & 0x3;
+
+	if (status == SMB_CHG_STATUS_NONE)
+		chg_type = POWER_SUPPLY_CHARGE_TYPE_NONE;
+	else if (status == SMB_CHG_STATUS_FAST_CHARGE) /* constant current */
+		chg_type = POWER_SUPPLY_CHARGE_TYPE_FAST;
+	else if (status == SMB_CHG_STATUS_TAPER_CHARGE) /* constant voltage */
+		chg_type = POWER_SUPPLY_CHARGE_TYPE_FAST;
+	else if (status == SMB_CHG_STATUS_PRE_CHARGE)
+		chg_type = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
+
+	pr_debug("smb-chg-status=%d=%s.\n", status, smb350_chg_status[status]);
+
+	if (dev->chg_status != status) { /* Status changed */
+		if (status == SMB_CHG_STATUS_NONE) {
+			pr_debug("Charging stopped.\n");
+			wake_unlock(&dev->chg_wake_lock);
+		} else {
+			pr_debug("Charging started.\n");
+			wake_lock(&dev->chg_wake_lock);
+		}
+	}
+
+	dev->chg_status = status;
+
+	return chg_type;
+}
+
+static void smb350_enable_charging(struct smb350_device *dev, bool enable)
+{
+	int val = !enable; /* active low */
+
+	pr_debug("enable=%d.\n", enable);
+
+	gpio_set_value_cansleep(dev->chg_en_n_gpio, val);
+}
+
+/* When the status bit of a certain condition is read,
+ * the corresponding IRQ signal is cleared.
+ */
+static int smb350_clear_irq(struct i2c_client *client)
+{
+	int ret;
+
+	ret = smb350_read_reg(client, IRQ_STATUS_A_REG);
+	if (ret < 0)
+		return ret;
+	ret = smb350_read_reg(client, IRQ_STATUS_B_REG);
+	if (ret < 0)
+		return ret;
+	ret = smb350_read_reg(client, IRQ_STATUS_C_REG);
+	if (ret < 0)
+		return ret;
+	ret = smb350_read_reg(client, IRQ_STATUS_D_REG);
+	if (ret < 0)
+		return ret;
+	ret = smb350_read_reg(client, IRQ_STATUS_E_REG);
+	if (ret < 0)
+		return ret;
+	ret = smb350_read_reg(client, IRQ_STATUS_F_REG);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+/*
+ * Do the IRQ work from a thread context rather than interrupt context.
+ * Read status registers to clear interrupt source.
+ * Notify the power-supply driver about change detected.
+ * Relevant events for start/stop charging:
+ * 1. DC insert/remove
+ * 2. End-Of-Charging
+ * 3. Battery insert/remove
+ * 4. Temperture too hot/cold
+ * 5. Charging timeout expired.
+ */
+static void smb350_irq_worker(struct work_struct *work)
+{
+	int ret = 0;
+	struct smb350_device *dev =
+		container_of(work, struct smb350_device, irq_work.work);
+
+	ret = smb350_clear_irq(dev->client);
+	if (ret == 0) { /* Cleared ok */
+		/* Notify Battery-psy about status changed */
+		pr_debug("Notify power_supply_changed.\n");
+		power_supply_changed(&dev->dc_psy);
+	}
+}
+
+/*
+ * The STAT pin is low when charging and high when not charging.
+ * When the smb350 start/stop charging the STAT pin triggers an interrupt.
+ * Interrupt is triggered on both rising or falling edge.
+ */
+static irqreturn_t smb350_irq(int irq, void *dev_id)
+{
+	struct smb350_device *dev = dev_id;
+
+	pr_debug("\n");
+
+	/* I2C transfers API should not run in interrupt context */
+	schedule_delayed_work(&dev->irq_work, msecs_to_jiffies(100));
+
+	return IRQ_HANDLED;
+}
+
+static enum power_supply_property pm_power_props[] = {
+	/* real time */
+	POWER_SUPPLY_PROP_PRESENT,
+	POWER_SUPPLY_PROP_ONLINE,
+	POWER_SUPPLY_PROP_CHARGE_TYPE,
+	POWER_SUPPLY_PROP_VOLTAGE_NOW,
+	/* fixed */
+	POWER_SUPPLY_PROP_MANUFACTURER,
+	POWER_SUPPLY_PROP_MODEL_NAME,
+	POWER_SUPPLY_PROP_CURRENT_MAX,
+};
+
+static char *pm_power_supplied_to[] = {
+	"battery",
+};
+
+static int smb350_get_property(struct power_supply *psy,
+			       enum power_supply_property psp,
+			       union power_supply_propval *val)
+{
+	int ret = 0;
+	struct smb350_device *dev = container_of(psy,
+						 struct smb350_device,
+						 dc_psy);
+	struct i2c_client *client = dev->client;
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_PRESENT:
+		val->intval = smb350_is_dc_present(client);
+		break;
+	case POWER_SUPPLY_PROP_ONLINE:
+		val->intval = smb350_is_charging(client);
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_TYPE:
+		val->intval = smb350_get_prop_charge_type(dev);
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+		val->intval = smb350_get_float_voltage(client);
+		val->intval *= 1000; /* mV to uV */
+		break;
+	case POWER_SUPPLY_PROP_MODEL_NAME:
+		val->strval = SMB350_NAME;
+		break;
+	case POWER_SUPPLY_PROP_MANUFACTURER:
+		val->strval = "Summit Microelectronics";
+		break;
+	case POWER_SUPPLY_PROP_CURRENT_MAX:
+		val->intval = dev->chg_current_ma;
+		break;
+	default:
+		pr_err("Invalid prop = %d.\n", psp);
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static int smb350_set_property(struct power_supply *psy,
+			       enum power_supply_property psp,
+			       const union power_supply_propval *val)
+{
+	int ret = 0;
+	struct smb350_device *dev =
+		container_of(psy, struct smb350_device, dc_psy);
+
+	switch (psp) {
+	/*
+	 *  Allow a smart battery to Start/Stop charging.
+	 *  i.e. when End-Of-Charging detected.
+	 *  The SMB350 can be configured to terminate charging
+	 *  when charge-current reaching Termination-Current.
+	 */
+	case POWER_SUPPLY_PROP_ONLINE:
+		smb350_enable_charging(dev, val->intval);
+		break;
+	default:
+		pr_err("Invalid prop = %d.\n", psp);
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static int smb350_set_chg_current(struct i2c_client *client, int current_ma)
+{
+	int ret;
+	u8 temp;
+
+	if ((current_ma < SMB350_FAST_CHG_MIN_MA) ||
+	    (current_ma >  SMB350_FAST_CHG_MAX_MA)) {
+		pr_err("invalid current %d mA.\n", current_ma);
+		return -EINVAL;
+	}
+
+	temp = (current_ma - SMB350_FAST_CHG_MIN_MA) / SMB350_FAST_CHG_STEP_MA;
+
+	pr_debug("fast-chg-current=%d mA setting %02x\n", current_ma, temp);
+
+	ret = smb350_masked_write(client, CHG_CURRENT_REG,
+				  FAST_CHG_CURRENT_MASK, temp);
+
+	return ret;
+}
+
+static int smb350_set_term_current(struct i2c_client *client, int current_ma)
+{
+	int ret;
+	u8 temp;
+
+	if ((current_ma < SMB350_TERM_CUR_MIN_MA) ||
+	    (current_ma >  SMB350_TERM_CUR_MAX_MA)) {
+		pr_err("invalid current %d mA to set\n", current_ma);
+		return -EINVAL;
+	}
+
+	temp = (current_ma - SMB350_TERM_CUR_MIN_MA) / SMB350_TERM_CUR_STEP_MA;
+
+	pr_debug("term-current=%d mA setting %02x\n", current_ma, temp);
+
+	ret = smb350_masked_write(client, CHG_OTHER_CURRENT_REG,
+				  TERM_CURRENT_MASK, temp);
+
+	return ret;
+}
+
+static int smb350_set_reg(void *data, u64 val)
+{
+	u32 addr = (u32) data;
+	int ret;
+	struct i2c_client *client = smb350_dev->client;
+
+	ret = smb350_write_reg(client, addr, (u8) val);
+
+	return ret;
+}
+
+static int smb350_get_reg(void *data, u64 *val)
+{
+	u32 addr = (u32) data;
+	int ret;
+	struct i2c_client *client = smb350_dev->client;
+
+	ret = smb350_read_reg(client, addr);
+	if (ret < 0)
+		return ret;
+
+	*val = ret;
+
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(reg_fops, smb350_get_reg, smb350_set_reg, "0x%02llx\n");
+
+static int smb350_create_debugfs_entries(struct smb350_device *dev)
+{
+	int i;
+	dev->dent = debugfs_create_dir(SMB350_NAME, NULL);
+	if (IS_ERR(dev->dent)) {
+		pr_err("smb350 driver couldn't create debugfs dir\n");
+		return -EFAULT;
+	}
+
+	for (i = 0 ; i < ARRAY_SIZE(smb350_debug_regs) ; i++) {
+		char *name = smb350_debug_regs[i].name;
+		u32 reg = smb350_debug_regs[i].reg;
+		struct dentry *file;
+
+		file = debugfs_create_file(name, 0644, dev->dent,
+					(void *) reg, &reg_fops);
+		if (IS_ERR(file)) {
+			pr_err("debugfs_create_file %s failed.\n", name);
+			return -EFAULT;
+		}
+	}
+
+	return 0;
+}
+
+static int smb350_set_volatile_params(struct smb350_device *dev)
+{
+	int ret;
+	struct i2c_client *client = dev->client;
+
+	pr_debug("\n");
+
+	ret = smb350_write_reg(client, CMD_A_REG, CMD_A_VOLATILE_WR_PERM);
+	if (ret) {
+		pr_err("Failed to set VOLATILE_WR_PERM ret=%d\n", ret);
+		return ret;
+	}
+
+	/* Disable SMB350 pulse-IRQ mechanism,
+	 * we use interrupts based on charging-status-transition
+	 */
+	/* Enable STATUS output (regardless of IRQ-pulses) */
+	smb350_masked_write(client, CMD_A_REG, BIT(0), 0);
+
+	/* Disable LED blinking - avoid periodic irq */
+	smb350_masked_write(client, PIN_ENABLE_CTRL_REG, BIT(7), 0);
+
+	/* Disable Failure SMB-IRQ */
+	ret = smb350_write_reg(client, FAULT_IRQ_REG, 0x00);
+	if (ret) {
+		pr_err("Failed to set FAULT_IRQ_REG ret=%d\n", ret);
+		return ret;
+	}
+
+	/* Disable Event IRQ */
+	ret = smb350_write_reg(client, IRQ_ENABLE_REG, 0x00);
+	if (ret) {
+		pr_err("Failed to set IRQ_ENABLE_REG ret=%d\n", ret);
+		return ret;
+	}
+
+	/* Enable charging/not-charging status output via STAT pin */
+	smb350_masked_write(client, STAT_TIMER_REG, BIT(5), 0);
+
+	/* Disable Automatic Recharge */
+	smb350_masked_write(client, CHG_CTRL_REG, BIT(7), 1);
+
+	/* Set fast-charge current */
+	ret = smb350_set_chg_current(client, dev->chg_current_ma);
+	if (ret) {
+		pr_err("Failed to set FAST_CHG_CURRENT ret=%d\n", ret);
+		return ret;
+	}
+
+	if (dev->term_current_ma > 0) {
+		/* Enable Current Termination */
+		smb350_masked_write(client, CHG_CTRL_REG, BIT(6), 0);
+
+		/* Set Termination current */
+		smb350_set_term_current(client, dev->term_current_ma);
+	} else {
+		/* Disable Current Termination */
+		smb350_masked_write(client, CHG_CTRL_REG, BIT(6), 1);
+	}
+
+	return 0;
+}
+
+static int __devinit smb350_register_psy(struct smb350_device *dev)
+{
+	int ret;
+
+	dev->dc_psy.name = "dc";
+	dev->dc_psy.type = POWER_SUPPLY_TYPE_MAINS;
+	dev->dc_psy.supplied_to = pm_power_supplied_to;
+	dev->dc_psy.num_supplicants = ARRAY_SIZE(pm_power_supplied_to);
+	dev->dc_psy.properties = pm_power_props;
+	dev->dc_psy.num_properties = ARRAY_SIZE(pm_power_props);
+	dev->dc_psy.get_property = smb350_get_property;
+	dev->dc_psy.set_property = smb350_set_property;
+
+	ret = power_supply_register(&dev->client->dev,
+				&dev->dc_psy);
+	if (ret) {
+		pr_err("failed to register power_supply. ret=%d.\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int __devinit smb350_probe(struct i2c_client *client,
+				  const struct i2c_device_id *id)
+{
+	int ret = 0;
+	const struct smb350_platform_data *pdata;
+	struct device_node *dev_node = client->dev.of_node;
+	struct smb350_device *dev;
+
+	/* STAT pin change on start/stop charging */
+	u32 irq_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;
+
+	if (!i2c_check_functionality(client->adapter,
+				I2C_FUNC_SMBUS_BYTE_DATA)) {
+		pr_err("i2c func fail.\n");
+		return -EIO;
+	}
+
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev) {
+		pr_err("alloc fail.\n");
+		return -ENOMEM;
+	}
+
+	smb350_dev = dev;
+	dev->client = client;
+
+	if (dev_node) {
+		dev->chg_en_n_gpio =
+			of_get_named_gpio(dev_node, "summit,chg-en-n-gpio", 0);
+		pr_debug("chg_en_n_gpio = %d.\n", dev->chg_en_n_gpio);
+
+		dev->chg_susp_n_gpio =
+			of_get_named_gpio(dev_node,
+					  "summit,chg-susp-n-gpio", 0);
+		pr_debug("chg_susp_n_gpio = %d.\n", dev->chg_susp_n_gpio);
+
+		dev->stat_gpio =
+			of_get_named_gpio(dev_node, "summit,stat-gpio", 0);
+		pr_debug("stat_gpio = %d.\n", dev->stat_gpio);
+
+		ret = of_property_read_u32(dev_node, "summit,chg-current-ma",
+					   &(dev->chg_current_ma));
+		pr_debug("chg_current_ma = %d.\n", dev->chg_current_ma);
+		if (ret) {
+			pr_err("Unable to read chg_current.\n");
+			return ret;
+		}
+		ret = of_property_read_u32(dev_node, "summit,term-current-ma",
+					   &(dev->term_current_ma));
+		pr_debug("term_current_ma = %d.\n", dev->term_current_ma);
+		if (ret) {
+			pr_err("Unable to read term_current_ma.\n");
+			return ret;
+		}
+	} else {
+		pdata = client->dev.platform_data;
+
+		if (pdata == NULL) {
+			pr_err("no platform data.\n");
+			return -EINVAL;
+		}
+
+		dev->chg_en_n_gpio = pdata->chg_en_n_gpio;
+		dev->chg_susp_n_gpio = pdata->chg_susp_n_gpio;
+		dev->stat_gpio = pdata->stat_gpio;
+
+		dev->chg_current_ma = pdata->chg_current_ma;
+		dev->term_current_ma = pdata->term_current_ma;
+	}
+
+	ret = gpio_request(dev->stat_gpio, "smb350_stat");
+	if (ret) {
+		pr_err("gpio_request failed for %d ret=%d\n",
+		       dev->stat_gpio, ret);
+		goto err_stat_gpio;
+	}
+	dev->irq = gpio_to_irq(dev->stat_gpio);
+	pr_debug("irq#=%d.\n", dev->irq);
+
+	ret = gpio_request(dev->chg_susp_n_gpio, "smb350_suspend");
+	if (ret) {
+		pr_err("gpio_request failed for %d ret=%d\n",
+			dev->chg_susp_n_gpio, ret);
+		goto err_susp_gpio;
+	}
+
+	ret = gpio_request(dev->chg_en_n_gpio, "smb350_charger_enable");
+	if (ret) {
+		pr_err("gpio_request failed for %d ret=%d\n",
+			dev->chg_en_n_gpio, ret);
+		goto err_en_gpio;
+	}
+
+	i2c_set_clientdata(client, dev);
+
+	pr_debug("set charge-enable + not suspend.\n");
+	gpio_set_value_cansleep(dev->chg_en_n_gpio, 1);	/* Disable */
+	msleep(100);
+	gpio_set_value_cansleep(dev->chg_susp_n_gpio, 1); /* Normal */
+	msleep(100); /* Allow the device to exist shutdown */
+
+	smb350_read_reg(client, I2C_SLAVE_ADDR_REG);
+
+	ret = smb350_set_volatile_params(dev);
+	if (ret)
+		goto err_set_params;
+
+	ret = smb350_register_psy(dev);
+	if (ret)
+		goto err_set_params;
+
+	ret = smb350_create_debugfs_entries(dev);
+	if (ret)
+		goto err_debugfs;
+
+	INIT_DELAYED_WORK(&dev->irq_work, smb350_irq_worker);
+	wake_lock_init(&dev->chg_wake_lock,
+		       WAKE_LOCK_SUSPEND, SMB350_NAME);
+
+	ret = request_irq(dev->irq, smb350_irq, irq_flags,
+			  "smb350_irq", dev);
+	if (ret) {
+		pr_err("request_irq %d failed.ret=%d\n", dev->irq, ret);
+		goto err_irq;
+	}
+
+	smb350_enable_charging(dev, true);
+
+	return 0;
+
+err_irq:
+err_debugfs:
+	if (dev->dent)
+		debugfs_remove_recursive(dev->dent);
+err_set_params:
+	gpio_free(dev->chg_en_n_gpio);
+err_en_gpio:
+	gpio_free(dev->chg_susp_n_gpio);
+err_susp_gpio:
+	gpio_free(dev->stat_gpio);
+err_stat_gpio:
+	kfree(smb350_dev);
+	smb350_dev = NULL;
+
+	pr_info("FAIL.\n");
+
+	return ret;
+}
+
+static int __devexit smb350_remove(struct i2c_client *client)
+{
+	struct smb350_device *dev = i2c_get_clientdata(client);
+
+	power_supply_unregister(&dev->dc_psy);
+	gpio_free(dev->chg_en_n_gpio);
+	gpio_free(dev->chg_susp_n_gpio);
+	if (dev->stat_gpio)
+		gpio_free(dev->stat_gpio);
+	if (dev->irq)
+		free_irq(dev->irq, dev);
+	if (dev->dent)
+		debugfs_remove_recursive(dev->dent);
+	kfree(smb350_dev);
+	smb350_dev = NULL;
+
+	return 0;
+}
+
+static const struct i2c_device_id smb350_id[] = {
+	{SMB350_NAME, 0},
+	{},
+};
+MODULE_DEVICE_TABLE(i2c, smb350_id);
+
+static const struct of_device_id smb350_match[] = {
+	{ .compatible = "summit,smb350-charger", },
+	{ },
+};
+
+static struct i2c_driver smb350_driver = {
+	.driver	= {
+			.name	= SMB350_NAME,
+			.owner	= THIS_MODULE,
+			.of_match_table = of_match_ptr(smb350_match),
+	},
+	.probe		= smb350_probe,
+	.remove		= __devexit_p(smb350_remove),
+	.id_table	= smb350_id,
+};
+
+static int __init smb350_init(void)
+{
+	return i2c_add_driver(&smb350_driver);
+}
+module_init(smb350_init);
+
+static void __exit smb350_exit(void)
+{
+	return i2c_del_driver(&smb350_driver);
+}
+module_exit(smb350_exit);
+
+MODULE_DESCRIPTION("Driver for SMB350 charger chip");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("i2c:" SMB350_NAME);
diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c
index a4c7131..92cbe6f 100644
--- a/drivers/usb/otg/msm_otg.c
+++ b/drivers/usb/otg/msm_otg.c
@@ -844,9 +844,14 @@
 		motg->caps & ALLOW_LPM_ON_DEV_SUSPEND;
 	dcp = motg->chg_type == USB_DCP_CHARGER;
 
-	/* charging detection in progress due to cable plug-in */
-	if (test_bit(B_SESS_VLD, &motg->inputs) && !device_bus_suspend &&
-		!dcp) {
+	/*
+	 * Abort suspend when,
+	 * 1. charging detection in progress due to cable plug-in
+	 * 2. host mode activation in progress due to Micro-A cable insertion
+	 */
+
+	if ((test_bit(B_SESS_VLD, &motg->inputs) && !device_bus_suspend &&
+		!dcp) || test_bit(A_BUS_REQ, &motg->inputs)) {
 		enable_irq(motg->irq);
 		return -EBUSY;
 	}
diff --git a/include/linux/i2c/smb350.h b/include/linux/i2c/smb350.h
new file mode 100644
index 0000000..5bb5cec
--- /dev/null
+++ b/include/linux/i2c/smb350.h
@@ -0,0 +1,35 @@
+/* Copyright (c) 2012 The Linux Foundation. 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.
+ *
+ */
+#ifndef __SMB350_H__
+#define __SMB350_H__
+
+#define SMB350_NAME		"smb350"
+
+/**
+ * struct smb350_platform_data
+ * structure to pass board specific information to the smb137b charger driver
+ * @chg_current_ma:	maximum fast charge current in mA
+ * @term_current_ma:	charge termination current in mA
+ * @chg_en_n_gpio:	gpio to enable or disable charging
+ * @chg_susp_n_gpio:	put active low to allow chip to suspend and disable I2C
+ * @stat_gpio:		STAT pin, active low, '0' when charging.
+ */
+struct smb350_platform_data {
+	int chg_en_n_gpio;
+	int chg_susp_n_gpio;
+	int chg_current_ma;
+	int term_current_ma;
+	int stat_gpio;
+};
+
+#endif