diff mbox

[4/4] power_supply: bq24261 charger driver

Message ID 1391490780-6141-5-git-send-email-jenny.tc@intel.com (mailing list archive)
State New, archived
Headers show

Commit Message

Jenny TC Feb. 4, 2014, 5:13 a.m. UTC
This patch introduces BQ24261 charger driver. The driver makes use of power
supply charging driver to setup charging. So the driver does hardware
abstraction and handles h/w specific corner cases. The charging logic resides
with power supply charging driver

Signed-off-by: Jenny TC <jenny.tc@intel.com>
---
 drivers/power/Kconfig                 |   10 +
 drivers/power/Makefile                |    1 +
 drivers/power/bq24261-charger.c       | 1358 +++++++++++++++++++++++++++++++++
 include/linux/power/bq24261-charger.h |   25 +
 4 files changed, 1394 insertions(+)
 create mode 100644 drivers/power/bq24261-charger.c
 create mode 100644 include/linux/power/bq24261-charger.h

Comments

Pavel Machek Feb. 4, 2014, 11:36 a.m. UTC | #1
Hi!


> +#define DEV_MANUFACTURER "TI"
> +#define DEV_MANUFACTURER_NAME_SIZE 4

This is unneccessarily complicated for no reason. You copy "TI" to
struct, just so that ou can return pointer to the field on
get_property.

What about simply returning "TI" from get_property, without defines
and copying?

> +#define BQ24261_MIN_CV 3500
> +#define BQ24261_MAX_CV 4440

Other defines use uV as an unit :-(.

> +static void lookup_regval(u16 tbl[][2], size_t size, u16 in_val, u8 *out_val)
> +{
> +	int i;
> +
> +	for (i = 1; i < size; ++i)
> +		if (in_val < tbl[i][0])
> +			break;
> +
> +	*out_val = (u8) tbl[i - 1][1];
> +}

Umm. Could we simply return the value?

> +static void bq24261_cc_to_reg(int cc, u8 *reg_val)
> +{
> +
> +	cc = cc < BQ24261_MAX_CC ? cc : BQ24261_MAX_CC;
> +	cc = cc - BQ24261_MIN_CC;

clamp_t?

> +	*reg_val = cc > 0 ? ((cc/100) << 3) & 0xFF : 0;
> +}

Just return the value?

> +static void bq24261_cv_to_reg(int cv, u8 *reg_val)
> +{
> +	int val;
> +
> +	val = clamp_t(int, cv, BQ24261_MIN_CV, BQ24261_MAX_CV);
> +	*reg_val =
> +		(((val - BQ24261_MIN_CV) / BQ24261_CV_DIV)
> +			<< BQ24261_CV_BIT_POS);
> +}

Not sure if the defines really make it more readable. It should be
consistent with the above/below functions...

> +static inline void bq24261_iterm_to_reg(int iterm, u8 *regval)
> +{
> +	iterm = iterm < BQ24261_MAX_ITERM ? iterm : BQ24261_MAX_ITERM;
> +	iterm = iterm - BQ24261_MIN_ITERM;

clamp_t?

> +	*regval = iterm > 0 ? (iterm/50) & 0xFF : 0;
> +}

Just return the value.

> +static inline void bq24261_sfty_tmr_to_reg(int tmr, u8 *regval)
> +{
> +	return lookup_regval(bq24261_sfty_tmr, ARRAY_SIZE(bq24261_sfty_tmr),
> +			     tmr, regval);
> +}

Just return the value... returning void values with explicit return is
"interesting".

> +	/* If status is fault, wait for READY before enabling the charging */
> +
> +	if (!is_ready) {
> +		ret = wait_event_timeout(chip->wait_ready,
> +			(chip->chrgr_stat != BQ24261_CHRGR_STAT_READY),
> +				HZ);
> +		dev_info(&chip->client->dev,
> +			"chrgr_stat=%x\n", chip->chrgr_stat);
> +		if (ret == 0) {
> +			dev_err(&chip->client->dev,
> +				"Waiting for Charger Ready Failed.Enabling charging anyway\n");
> +		}
> +	}

So charger has a problem, and we force it on, anyway? Also put space
after ".".

> +static inline int bq24261_set_cv(struct bq24261_charger *chip, int cv)
> +{
> +	int bat_volt;
> +	int ret;
> +	u8 reg_val;
> +	u8 vindpm_val = 0x0;
> +
> +	/*
> +	* Setting VINDPM value as per the battery voltage
> +	*  VBatt           Vindpm     Register Setting
> +	*  < 3.7v           4.2v       0x0 (default)
> +	*  3.71v - 3.96v    4.36v      0x2
> +	*  > 3.96v          4.6v       0x5
> +	*/
> +	ret = get_battery_voltage(&bat_volt);
> +	if (ret) {
> +		dev_err(&chip->client->dev,
> +			"Error getting battery voltage!!\n");
> +	} else {

You forget the error value and continue anyway.

> +static inline void resume_charging(struct bq24261_charger *chip)
> +{
> +
> +	if (chip->is_charger_enabled)
> +		bq24261_enable_charger(chip, true);
> +	if (chip->inlmt)
> +		bq24261_set_inlmt(chip, chip->inlmt);
> +	if (chip->cc)
> +		bq24261_set_cc(chip, chip->cc);
> +	if (chip->cv)
> +		bq24261_set_cv(chip, chip->cv);
> +	if (chip->is_charging_enabled)
> +		bq24261_enable_charging(chip, true);

What about some error checking?

Is it wise to enable charging when setting voltage failed?

> +static inline bool is_bq24261_enabled(struct bq24261_charger *chip)
> +{
> +	if (chip->cable_type == PSY_CHARGER_CABLE_TYPE_NONE)
> +		return false;
> +	else if (!chip->is_charger_enabled)
> +		return false;

Kill the else.

> +static inline int get_battery_voltage(int *volt)
> +{
> +	struct power_supply *psy;
> +	union power_supply_propval val;
> +	int ret;
> +
> +	psy = get_psy_battery();
> +	if (!psy)
> +		return -EINVAL;

Hmm. Does this assume just one battery in the system?

Is it good idea? Older machines contain main and memory backup
batteries. Newer machines contain keyboard and display battery....


									Pavel
Jenny TC Feb. 20, 2014, 5:03 a.m. UTC | #2
On Tue, Feb 04, 2014 at 12:36:21PM +0100, Pavel Machek wrote:
> > +#define BQ24261_MIN_CV 3500
> > +#define BQ24261_MAX_CV 4440
> 
> Other defines use uV as an unit :-(.

uV is used if the value is read from psy class. For register configurations
uses mV. Will change the name to reflect mV

> > +	/* If status is fault, wait for READY before enabling the charging */
> > +
> > +	if (!is_ready) {
> > +		ret = wait_event_timeout(chip->wait_ready,
> > +			(chip->chrgr_stat != BQ24261_CHRGR_STAT_READY),
> > +				HZ);
> > +		dev_info(&chip->client->dev,
> > +			"chrgr_stat=%x\n", chip->chrgr_stat);
> > +		if (ret == 0) {
> > +			dev_err(&chip->client->dev,
> > +				"Waiting for Charger Ready Failed.Enabling charging anyway\n");
> > +		}
> > +	}
> 
> So charger has a problem, and we force it on, anyway? Also put space
> after ".".

Yes, sometimes charger does not give ready, so we force.
> 
> > +static inline int bq24261_set_cv(struct bq24261_charger *chip, int cv)
> > +{
> > +	int bat_volt;
> > +	int ret;
> > +	u8 reg_val;
> > +	u8 vindpm_val = 0x0;
> > +
> > +	/*
> > +	* Setting VINDPM value as per the battery voltage
> > +	*  VBatt           Vindpm     Register Setting
> > +	*  < 3.7v           4.2v       0x0 (default)
> > +	*  3.71v - 3.96v    4.36v      0x2
> > +	*  > 3.96v          4.6v       0x5
> > +	*/
> > +	ret = get_battery_voltage(&bat_volt);
> > +	if (ret) {
> > +		dev_err(&chip->client->dev,
> > +			"Error getting battery voltage!!\n");
> > +	} else {
> 
> You forget the error value and continue anyway.

On error, throw the error and program default VINDPM value.

-Jenny
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Pavel Machek Feb. 21, 2014, 2:44 p.m. UTC | #3
Hi!

> > > +static inline int bq24261_set_cv(struct bq24261_charger *chip, int cv)
> > > +{
> > > +	int bat_volt;
> > > +	int ret;
> > > +	u8 reg_val;
> > > +	u8 vindpm_val = 0x0;
> > > +
> > > +	/*
> > > +	* Setting VINDPM value as per the battery voltage
> > > +	*  VBatt           Vindpm     Register Setting
> > > +	*  < 3.7v           4.2v       0x0 (default)
> > > +	*  3.71v - 3.96v    4.36v      0x2
> > > +	*  > 3.96v          4.6v       0x5
> > > +	*/
> > > +	ret = get_battery_voltage(&bat_volt);
> > > +	if (ret) {
> > > +		dev_err(&chip->client->dev,
> > > +			"Error getting battery voltage!!\n");
> > > +	} else {
> > 
> > You forget the error value and continue anyway.
> 
> On error, throw the error and program default VINDPM value.

Is it good idea to attempt charging when we can't read battery
voltage?
									Pavel
Jenny TC Feb. 25, 2014, 11:51 a.m. UTC | #4
On Fri, Feb 21, 2014 at 03:44:00PM +0100, Pavel Machek wrote:
> Hi!
> 
> > > > +static inline int bq24261_set_cv(struct bq24261_charger *chip, int cv)
> > > > +{
> > > > +	int bat_volt;
> > > > +	int ret;
> > > > +	u8 reg_val;
> > > > +	u8 vindpm_val = 0x0;
> > > > +
> > > > +	/*
> > > > +	* Setting VINDPM value as per the battery voltage
> > > > +	*  VBatt           Vindpm     Register Setting
> > > > +	*  < 3.7v           4.2v       0x0 (default)
> > > > +	*  3.71v - 3.96v    4.36v      0x2
> > > > +	*  > 3.96v          4.6v       0x5
> > > > +	*/
> > > > +	ret = get_battery_voltage(&bat_volt);
> > > > +	if (ret) {
> > > > +		dev_err(&chip->client->dev,
> > > > +			"Error getting battery voltage!!\n");
> > > > +	} else {
> > > 
> > > You forget the error value and continue anyway.
> > 
> > On error, throw the error and program default VINDPM value.
> 
> Is it good idea to attempt charging when we can't read battery
> voltage?

This function decides the VINDPM setting and doesn't enable charging.
VINDPM setting is used to ensure minimum input voltage and thereby allow to
charge with low power charging source. If the voltage read fails, then the
default VINDPM value 0x0 will be programmed and the input voltage may go down
as low as 4.2V. The charging/not charging decision is taken by power supply
charger driver and not by the chip driver. The worst case impact would be that
charging may happen with a low charge current at high battery voltages,
but doesn't compromise safety at all. 

-Jenny
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 913ec36..a1c2780 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -409,6 +409,16 @@  config BATTERY_GOLDFISH
 	  Say Y to enable support for the battery and AC power in the
 	  Goldfish emulator.
 
+config CHARGER_BQ24261
+	tristate "BQ24261 charger driver"
+	select POWER_SUPPLY_CHARGER
+	depends on I2C
+	help
+	  Say Y to include support for BQ24261 Charger driver. This driver
+	  makes use of power supply charging driver. So the driver gives
+	  the charger hardware abstraction only. Charging logic is abstracted
+	  in the power supply charging driver.
+
 source "drivers/power/reset/Kconfig"
 
 endif # POWER_SUPPLY
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index 77535fd..9dde895 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -59,4 +59,5 @@  obj-$(CONFIG_CHARGER_BQ24735)	+= bq24735-charger.o
 obj-$(CONFIG_POWER_AVS)		+= avs/
 obj-$(CONFIG_CHARGER_SMB347)	+= smb347-charger.o
 obj-$(CONFIG_CHARGER_TPS65090)	+= tps65090-charger.o
+obj-$(CONFIG_CHARGER_BQ24261)	+= bq24261-charger.o
 obj-$(CONFIG_POWER_RESET)	+= reset/
diff --git a/drivers/power/bq24261-charger.c b/drivers/power/bq24261-charger.c
new file mode 100644
index 0000000..a87d1cc
--- /dev/null
+++ b/drivers/power/bq24261-charger.c
@@ -0,0 +1,1358 @@ 
+/*
+ * bq24261-charger.c - BQ24261 Charger I2C client driver
+ *
+ * Copyright (C) 2011 Intel Corporation
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * 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.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ * Author: Jenny TC <jenny.tc@intel.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/power_supply.h>
+#include <linux/power/power_supply_charger.h>
+#include <linux/power/bq24261-charger.h>
+#include <linux/sched.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/usb/otg.h>
+#include <linux/version.h>
+
+
+#define DEV_NAME "bq24261_charger"
+#define DEV_MANUFACTURER "TI"
+#define MODEL_NAME_SIZE 8
+#define DEV_MANUFACTURER_NAME_SIZE 4
+
+#define EXCEPTION_MONITOR_DELAY (60 * HZ)
+#define WDT_RESET_DELAY (15 * HZ)
+
+/* BQ24261 registers */
+#define BQ24261_STAT_CTRL0_ADDR		0x00
+#define BQ24261_CTRL_ADDR		0x01
+#define BQ24261_BATT_VOL_CTRL_ADDR	0x02
+#define BQ24261_VENDOR_REV_ADDR		0x03
+#define BQ24261_TERM_FCC_ADDR		0x04
+#define BQ24261_VINDPM_STAT_ADDR	0x05
+#define BQ24261_ST_NTC_MON_ADDR		0x06
+
+#define BQ24261_RESET_MASK		(0x01 << 7)
+#define BQ24261_RESET_ENABLE		(0x01 << 7)
+
+#define BQ24261_FAULT_MASK		0x07
+#define BQ24261_STAT_MASK		(0x03 << 4)
+#define BQ24261_BOOST_MASK		(0x01 << 6)
+#define BQ24261_TMR_RST_MASK		(0x01 << 7)
+#define BQ24261_TMR_RST			(0x01 << 7)
+
+#define BQ24261_ENABLE_BOOST		(0x01 << 6)
+
+#define BQ24261_VOVP			0x01
+#define BQ24261_LOW_SUPPLY		0x02
+#define BQ24261_THERMAL_SHUTDOWN	0x03
+#define BQ24261_BATT_TEMP_FAULT		0x04
+#define BQ24261_TIMER_FAULT		0x05
+#define BQ24261_BATT_OVP		0x06
+#define BQ24261_NO_BATTERY		0x07
+#define BQ24261_STAT_READY		0x00
+
+#define BQ24261_STAT_CHRG_PRGRSS	(0x01 << 4)
+#define BQ24261_STAT_CHRG_DONE		(0x02 << 4)
+#define BQ24261_STAT_FAULT		(0x03 << 4)
+
+#define BQ24261_CE_MASK			(0x01 << 1)
+#define BQ24261_CE_DISABLE		(0x01 << 1)
+
+#define BQ24261_HZ_MASK			(0x01)
+#define BQ24261_HZ_ENABLE		(0x01)
+
+#define BQ24261_ICHRG_MASK		(0x1F << 3)
+#define BQ24261_MIN_CC 500 /* 500mA */
+#define BQ24261_MAX_CC	3000 /* 3A */
+
+#define BQ24261_ITERM_MASK		(0x03)
+#define BQ24261_MIN_ITERM 50 /* 50 mA */
+#define BQ24261_MAX_ITERM 300 /* 300mA */
+
+#define BQ24261_VBREG_MASK		(0x3F << 2)
+
+#define BQ24261_INLMT_MASK		(0x03 << 4)
+#define BQ24261_INLMT_100		0x00
+#define BQ24261_INLMT_150		(0x01 << 4)
+#define BQ24261_INLMT_500		(0x02 << 4)
+#define BQ24261_INLMT_900		(0x03 << 4)
+#define BQ24261_INLMT_1500		(0x04 << 4)
+#define BQ24261_INLMT_2500		(0x06 << 4)
+
+#define BQ24261_TE_MASK			(0x01 << 2)
+#define BQ24261_TE_ENABLE		(0x01 << 2)
+#define BQ24261_STAT_ENABLE_MASK	(0x01 << 3)
+#define BQ24261_STAT_ENABLE		(0x01 << 3)
+
+#define BQ24261_VENDOR_MASK		(0x07 << 5)
+#define BQ24261_VENDOR			(0x02 << 5)
+#define BQ24261_REV_MASK		(0x07)
+#define BQ24261_REV			(0x02)
+#define BQ24260_REV			(0x01)
+
+#define BQ24261_TS_MASK			(0x01 << 3)
+#define BQ24261_TS_ENABLED		(0x01 << 3)
+#define BQ24261_BOOST_ILIM_MASK		(0x01 << 4)
+#define BQ24261_BOOST_ILIM_500ma	(0x0)
+#define BQ24261_BOOST_ILIM_1A		(0x01 << 4)
+
+#define BQ24261_SAFETY_TIMER_MASK	(0x03 << 5)
+#define BQ24261_SAFETY_TIMER_40MIN	0x00
+#define BQ24261_SAFETY_TIMER_6HR	(0x01 << 5)
+#define BQ24261_SAFETY_TIMER_9HR	(0x02 << 5)
+#define BQ24261_SAFETY_TIMER_DISABLED	(0x03 << 5)
+
+/* 1% above voltage max design to report over voltage */
+#define BQ24261_OVP_MULTIPLIER			1010
+#define BQ24261_OVP_RECOVER_MULTIPLIER		990
+#define BQ24261_DEF_BAT_VOLT_MAX_DESIGN		4200000
+
+/* Settings for Voltage / DPPM Register (05) */
+#define BQ24261_VBATT_LEVEL1		3700000
+#define BQ24261_VBATT_LEVEL2		3960000
+#define BQ24261_VINDPM_MASK		(0x07)
+#define BQ24261_VINDPM_320MV		(0x01 << 2)
+#define BQ24261_VINDPM_160MV		(0x01 << 1)
+#define BQ24261_VINDPM_80MV		(0x01 << 0)
+#define BQ24261_CD_STATUS_MASK		(0x01 << 3)
+#define BQ24261_DPM_EN_MASK		(0x01 << 4)
+#define BQ24261_DPM_EN_FORCE		(0x01 << 4)
+#define BQ24261_LOW_CHG_MASK		(0x01 << 5)
+#define BQ24261_LOW_CHG_EN		(0x01 << 5)
+#define BQ24261_LOW_CHG_DIS		(~BQ24261_LOW_CHG_EN)
+#define BQ24261_DPM_STAT_MASK		(0x01 << 6)
+#define BQ24261_MINSYS_STAT_MASK	(0x01 << 7)
+
+#define BQ24261_MIN_CC			500
+
+static u16 bq24261_sfty_tmr[][2] = {
+	{0, BQ24261_SAFETY_TIMER_DISABLED}
+	,
+	{40, BQ24261_SAFETY_TIMER_40MIN}
+	,
+	{360, BQ24261_SAFETY_TIMER_6HR}
+	,
+	{540, BQ24261_SAFETY_TIMER_9HR}
+	,
+};
+
+
+static u16 bq24261_inlmt[][2] = {
+	{100, BQ24261_INLMT_100}
+	,
+	{150, BQ24261_INLMT_150}
+	,
+	{500, BQ24261_INLMT_500}
+	,
+	{900, BQ24261_INLMT_900}
+	,
+	{1500, BQ24261_INLMT_1500}
+	,
+	{2500, BQ24261_INLMT_2500}
+	,
+};
+
+#define BQ24261_MIN_CV 3500
+#define BQ24261_MAX_CV 4440
+#define BQ24261_CV_DIV 20
+#define BQ24261_CV_BIT_POS 2
+
+static enum power_supply_property bq24261_usb_props[] = {
+	POWER_SUPPLY_PROP_PRESENT,
+	POWER_SUPPLY_PROP_ONLINE,
+	POWER_SUPPLY_PROP_TYPE,
+	POWER_SUPPLY_PROP_HEALTH,
+	POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
+	POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
+	POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
+	POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
+	POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
+	POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT,
+	POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT,
+	POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX,
+	POWER_SUPPLY_PROP_MODEL_NAME,
+	POWER_SUPPLY_PROP_MANUFACTURER,
+	POWER_SUPPLY_PROP_TEMP_MAX,
+	POWER_SUPPLY_PROP_TEMP_MIN,
+};
+
+enum bq24261_chrgr_stat {
+	BQ24261_CHRGR_STAT_UNKNOWN,
+	BQ24261_CHRGR_STAT_READY,
+	BQ24261_CHRGR_STAT_CHARGING,
+	BQ24261_CHRGR_STAT_BAT_FULL,
+	BQ24261_CHRGR_STAT_FAULT,
+};
+
+struct bq24261_charger {
+
+	struct mutex lock;
+	struct i2c_client *client;
+	struct bq24261_plat_data *pdata;
+	struct power_supply psy_usb;
+	struct power_supply_charger psyc_usb;
+	struct delayed_work notify_work;
+	struct delayed_work low_supply_fault_work;
+	struct delayed_work exception_mon_work;
+	struct list_head irq_queue;
+	wait_queue_head_t wait_ready;
+
+	int chrgr_health;
+	int bat_health;
+	int cc;
+	int cv;
+	int inlmt;
+	int max_cc;
+	int max_cv;
+	int iterm;
+	int cable_type;
+	int cntl_state;
+	int max_temp;
+	int min_temp;
+	enum bq24261_chrgr_stat chrgr_stat;
+	bool online;
+	bool present;
+	bool is_charging_enabled;
+	bool is_charger_enabled;
+	bool is_vsys_on;
+	bool is_hw_chrg_term;
+	char model_name[MODEL_NAME_SIZE];
+	char manufacturer[DEV_MANUFACTURER_NAME_SIZE];
+};
+
+enum bq2426x_model_num {
+	BQ24260 = 0,
+	BQ24261,
+};
+
+struct bq2426x_model {
+	char model_name[MODEL_NAME_SIZE];
+	enum bq2426x_model_num model;
+};
+
+static struct bq2426x_model bq24261_model_name[] = {
+	{ "bq24260", BQ24260 },
+	{ "bq24261", BQ24261 },
+};
+
+struct i2c_client *bq24261_client;
+static inline int get_battery_voltage(int *volt);
+static int bq24261_handle_irq(struct bq24261_charger *chip, u8 stat_reg);
+static inline int bq24261_set_iterm(struct bq24261_charger *chip, int iterm);
+static inline int bq24261_enable_hw_charge_term(struct bq24261_charger *chip,
+		bool val);
+
+enum power_supply_type get_power_supply_type(
+		enum psy_charger_cable_type cable)
+{
+
+	switch (cable) {
+
+	case PSY_CHARGER_CABLE_TYPE_USB_DCP:
+		return POWER_SUPPLY_TYPE_USB_DCP;
+	case PSY_CHARGER_CABLE_TYPE_USB_CDP:
+		return POWER_SUPPLY_TYPE_USB_CDP;
+	case PSY_CHARGER_CABLE_TYPE_USB_ACA:
+	case PSY_CHARGER_CABLE_TYPE_ACA_DOCK:
+		return POWER_SUPPLY_TYPE_USB_ACA;
+	case PSY_CHARGER_CABLE_TYPE_AC:
+		return POWER_SUPPLY_TYPE_MAINS;
+	case PSY_CHARGER_CABLE_TYPE_NONE:
+	case PSY_CHARGER_CABLE_TYPE_USB_SDP:
+		return POWER_SUPPLY_TYPE_USB;
+	default:
+		return POWER_SUPPLY_TYPE_UNKNOWN;
+	}
+
+	return POWER_SUPPLY_TYPE_USB;
+}
+
+static void lookup_regval(u16 tbl[][2], size_t size, u16 in_val, u8 *out_val)
+{
+	int i;
+
+	for (i = 1; i < size; ++i)
+		if (in_val < tbl[i][0])
+			break;
+
+	*out_val = (u8) tbl[i - 1][1];
+}
+
+static void bq24261_cc_to_reg(int cc, u8 *reg_val)
+{
+
+	cc = cc < BQ24261_MAX_CC ? cc : BQ24261_MAX_CC;
+	cc = cc - BQ24261_MIN_CC;
+	*reg_val = cc > 0 ? ((cc/100) << 3) & 0xFF : 0;
+}
+
+static void bq24261_cv_to_reg(int cv, u8 *reg_val)
+{
+	int val;
+
+	val = clamp_t(int, cv, BQ24261_MIN_CV, BQ24261_MAX_CV);
+	*reg_val =
+		(((val - BQ24261_MIN_CV) / BQ24261_CV_DIV)
+			<< BQ24261_CV_BIT_POS);
+}
+
+static void bq24261_inlmt_to_reg(int inlmt, u8 *regval)
+{
+	return lookup_regval(bq24261_inlmt, ARRAY_SIZE(bq24261_inlmt),
+			     inlmt, regval);
+}
+
+static inline void bq24261_iterm_to_reg(int iterm, u8 *regval)
+{
+	iterm = iterm < BQ24261_MAX_ITERM ? iterm : BQ24261_MAX_ITERM;
+	iterm = iterm - BQ24261_MIN_ITERM;
+
+	*regval = iterm > 0 ? (iterm/50) & 0xFF : 0;
+}
+
+static inline void bq24261_sfty_tmr_to_reg(int tmr, u8 *regval)
+{
+	return lookup_regval(bq24261_sfty_tmr, ARRAY_SIZE(bq24261_sfty_tmr),
+			     tmr, regval);
+}
+
+static inline int bq24261_read_reg(struct i2c_client *client, u8 reg)
+{
+	int ret;
+
+	ret = i2c_smbus_read_byte_data(client, reg);
+	if (ret < 0)
+		dev_err(&client->dev, "Error(%d) in reading reg %d\n", ret,
+			reg);
+
+	return ret;
+}
+
+static inline int bq24261_write_reg(struct i2c_client *client, u8 reg, u8 data)
+{
+	int ret;
+
+	ret = i2c_smbus_write_byte_data(client, reg, data);
+	if (ret < 0)
+		dev_err(&client->dev, "Error(%d) in writing %d to reg %d\n",
+			ret, data, reg);
+
+	return ret;
+}
+
+static inline int bq24261_read_modify_reg(struct i2c_client *client, u8 reg,
+					  u8 mask, u8 val)
+{
+	int ret;
+
+	ret = bq24261_read_reg(client, reg);
+	if (ret < 0)
+		return ret;
+	ret = (ret & ~mask) | (mask & val);
+	return bq24261_write_reg(client, reg, ret);
+}
+
+static inline int bq24261_tmr_ntc_init(struct bq24261_charger *chip)
+{
+	u8 reg_val;
+	int ret;
+
+	bq24261_sfty_tmr_to_reg(chip->pdata->safety_timer, &reg_val);
+
+	if (chip->pdata->is_ts_enabled)
+		reg_val |= BQ24261_TS_ENABLED;
+
+	ret = bq24261_read_modify_reg(chip->client, BQ24261_ST_NTC_MON_ADDR,
+			BQ24261_TS_MASK|BQ24261_SAFETY_TIMER_MASK|
+			BQ24261_BOOST_ILIM_MASK, reg_val);
+
+	return ret;
+}
+
+static inline int bq24261_enable_charging(
+	struct bq24261_charger *chip, bool val)
+{
+	int ret;
+	u8 reg_val;
+	bool is_ready;
+
+	ret = bq24261_read_reg(chip->client,
+					BQ24261_STAT_CTRL0_ADDR);
+	if (ret < 0) {
+		dev_err(&chip->client->dev,
+			"Error(%d) in reading BQ24261_STAT_CTRL0_ADDR\n", ret);
+	}
+
+	is_ready =  (ret & BQ24261_STAT_MASK) != BQ24261_STAT_FAULT;
+
+	/* If status is fault, wait for READY before enabling the charging */
+
+	if (!is_ready) {
+		ret = wait_event_timeout(chip->wait_ready,
+			(chip->chrgr_stat != BQ24261_CHRGR_STAT_READY),
+				HZ);
+		dev_info(&chip->client->dev,
+			"chrgr_stat=%x\n", chip->chrgr_stat);
+		if (ret == 0) {
+			dev_err(&chip->client->dev,
+				"Waiting for Charger Ready Failed.Enabling charging anyway\n");
+		}
+	}
+
+	if (val) {
+		reg_val = (~BQ24261_CE_DISABLE & BQ24261_CE_MASK);
+		if (chip->is_hw_chrg_term)
+			reg_val |= BQ24261_TE_ENABLE;
+	} else {
+		reg_val = BQ24261_CE_DISABLE;
+	}
+
+	reg_val |=  BQ24261_STAT_ENABLE;
+
+	ret = bq24261_read_modify_reg(chip->client, BQ24261_CTRL_ADDR,
+		       BQ24261_STAT_ENABLE_MASK|BQ24261_RESET_MASK|
+				BQ24261_CE_MASK|BQ24261_TE_MASK,
+					reg_val);
+	if (ret || !val) {
+		cancel_delayed_work_sync(&chip->notify_work);
+		return ret;
+	}
+
+	bq24261_set_iterm(chip, chip->iterm);
+	schedule_delayed_work(&chip->notify_work, 0);
+	bq24261_enable_hw_charge_term(chip, true);
+	return bq24261_tmr_ntc_init(chip);
+}
+
+static inline int bq24261_reset_timer(struct bq24261_charger *chip)
+{
+	return bq24261_read_modify_reg(chip->client, BQ24261_STAT_CTRL0_ADDR,
+			BQ24261_TMR_RST_MASK, BQ24261_TMR_RST);
+}
+
+static inline int bq24261_enable_charger(
+	struct bq24261_charger *chip, int val)
+{
+
+	u8 reg_val;
+	int ret;
+
+	reg_val = val ? (~BQ24261_HZ_ENABLE & BQ24261_HZ_MASK)  :
+			BQ24261_HZ_ENABLE;
+
+	ret = bq24261_read_modify_reg(chip->client, BQ24261_CTRL_ADDR,
+		       BQ24261_HZ_MASK|BQ24261_RESET_MASK, reg_val);
+	if (ret)
+		return ret;
+
+	return bq24261_reset_timer(chip);
+}
+
+static inline int bq24261_set_cc(struct bq24261_charger *chip, int cc)
+{
+	u8 reg_val;
+	int ret;
+
+	dev_dbg(&chip->client->dev, "cc=%d\n", cc);
+
+	if (cc && (cc < BQ24261_MIN_CC)) {
+		dev_dbg(&chip->client->dev, "Set LOW_CHG bit\n");
+		reg_val = BQ24261_LOW_CHG_EN;
+		ret = bq24261_read_modify_reg(chip->client,
+				BQ24261_VINDPM_STAT_ADDR,
+				BQ24261_LOW_CHG_MASK, reg_val);
+	} else {
+		dev_dbg(&chip->client->dev, "Clear LOW_CHG bit\n");
+		reg_val = BQ24261_LOW_CHG_DIS;
+		ret = bq24261_read_modify_reg(chip->client,
+				BQ24261_VINDPM_STAT_ADDR,
+				BQ24261_LOW_CHG_MASK, reg_val);
+	}
+
+	bq24261_cc_to_reg(cc, &reg_val);
+
+	return bq24261_read_modify_reg(chip->client, BQ24261_TERM_FCC_ADDR,
+			BQ24261_ICHRG_MASK, reg_val);
+}
+
+static inline int bq24261_set_cv(struct bq24261_charger *chip, int cv)
+{
+	int bat_volt;
+	int ret;
+	u8 reg_val;
+	u8 vindpm_val = 0x0;
+
+	/*
+	* Setting VINDPM value as per the battery voltage
+	*  VBatt           Vindpm     Register Setting
+	*  < 3.7v           4.2v       0x0 (default)
+	*  3.71v - 3.96v    4.36v      0x2
+	*  > 3.96v          4.6v       0x5
+	*/
+	ret = get_battery_voltage(&bat_volt);
+	if (ret) {
+		dev_err(&chip->client->dev,
+			"Error getting battery voltage!!\n");
+	} else {
+		if (bat_volt > BQ24261_VBATT_LEVEL2)
+			vindpm_val =
+				(BQ24261_VINDPM_320MV | BQ24261_VINDPM_80MV);
+		else if (bat_volt > BQ24261_VBATT_LEVEL1)
+			vindpm_val = BQ24261_VINDPM_160MV;
+	}
+
+	ret = bq24261_read_modify_reg(chip->client,
+			BQ24261_VINDPM_STAT_ADDR,
+			BQ24261_VINDPM_MASK,
+			vindpm_val);
+	if (ret) {
+		dev_err(&chip->client->dev,
+			"Error setting VINDPM setting!!\n");
+		return ret;
+	}
+
+
+	bq24261_cv_to_reg(cv, &reg_val);
+
+	return bq24261_read_modify_reg(chip->client, BQ24261_BATT_VOL_CTRL_ADDR,
+				       BQ24261_VBREG_MASK, reg_val);
+}
+
+static inline int bq24261_set_inlmt(struct bq24261_charger *chip, int inlmt)
+{
+	u8 reg_val;
+
+	bq24261_inlmt_to_reg(inlmt, &reg_val);
+
+	return bq24261_read_modify_reg(chip->client, BQ24261_CTRL_ADDR,
+		       BQ24261_RESET_MASK|BQ24261_INLMT_MASK, reg_val);
+
+}
+
+static inline void resume_charging(struct bq24261_charger *chip)
+{
+
+	if (chip->is_charger_enabled)
+		bq24261_enable_charger(chip, true);
+	if (chip->inlmt)
+		bq24261_set_inlmt(chip, chip->inlmt);
+	if (chip->cc)
+		bq24261_set_cc(chip, chip->cc);
+	if (chip->cv)
+		bq24261_set_cv(chip, chip->cv);
+	if (chip->is_charging_enabled)
+		bq24261_enable_charging(chip, true);
+}
+
+static inline int bq24261_set_iterm(struct bq24261_charger *chip, int iterm)
+{
+	u8 reg_val;
+
+	bq24261_iterm_to_reg(iterm, &reg_val);
+
+	return bq24261_read_modify_reg(chip->client, BQ24261_TERM_FCC_ADDR,
+				       BQ24261_ITERM_MASK, reg_val);
+}
+
+static inline int bq24261_enable_hw_charge_term(
+	struct bq24261_charger *chip, bool val)
+{
+	u8 data;
+	int ret;
+
+	data = val ? BQ24261_TE_ENABLE : (~BQ24261_TE_ENABLE & BQ24261_TE_MASK);
+
+
+	ret = bq24261_read_modify_reg(chip->client, BQ24261_CTRL_ADDR,
+			       BQ24261_RESET_MASK|BQ24261_TE_MASK, data);
+
+	if (ret)
+		return ret;
+
+	chip->is_hw_chrg_term = val ? true : false;
+
+	return ret;
+}
+
+static inline bool bq24261_is_vsys_on(struct bq24261_charger *chip)
+{
+	int ret;
+	struct i2c_client *client = chip->client;
+
+	ret = bq24261_read_reg(client, BQ24261_CTRL_ADDR);
+	if (ret < 0) {
+		dev_err(&client->dev,
+			"Error(%d) in reading BQ24261_CTRL_ADDR\n", ret);
+		return false;
+	}
+
+	if (((ret & BQ24261_HZ_MASK) == BQ24261_HZ_ENABLE) &&
+			chip->is_charger_enabled) {
+		dev_err(&client->dev, "Charger in Hi Z Mode\n");
+		return false;
+	}
+
+	ret = bq24261_read_reg(client, BQ24261_VINDPM_STAT_ADDR);
+	if (ret < 0) {
+		dev_err(&client->dev,
+			"Error(%d) in reading BQ24261_VINDPM_STAT_ADDR\n", ret);
+		return false;
+	}
+
+	if (ret & BQ24261_CD_STATUS_MASK) {
+		dev_err(&client->dev, "CD line asserted\n");
+		return false;
+	}
+
+	return true;
+}
+
+static inline bool is_bq24261_enabled(struct bq24261_charger *chip)
+{
+	if (chip->cable_type == PSY_CHARGER_CABLE_TYPE_NONE)
+		return false;
+	else if (!chip->is_charger_enabled)
+		return false;
+	/*
+	* BQ24261 gives interrupt only on stop/resume charging.
+	* If charging is already stopped, we need to query the hardware
+	* to see charger is still active and can supply vsys or not.
+	*/
+	else if ((chip->chrgr_stat == BQ24261_CHRGR_STAT_FAULT) ||
+		 (!chip->is_charging_enabled))
+		return bq24261_is_vsys_on(chip);
+
+	return chip->is_vsys_on;
+}
+
+static int bq24261_usb_psyc_set_property(struct power_supply_charger *psyc,
+				    enum power_supply_charger_property pscp,
+				    const union power_supply_propval *val)
+{
+	struct bq24261_charger *chip;
+	int ret = 0;
+
+	chip = container_of(psyc, struct bq24261_charger, psyc_usb);
+
+	mutex_lock(&chip->lock);
+	dev_dbg(&chip->client->dev, "%s: prop=%d, val=%d\n",
+			__func__, pscp, val->intval);
+
+	switch (pscp) {
+
+	case POWER_SUPPLY_CHARGER_PROP_ENABLE_CHARGING:
+		ret = bq24261_enable_charging(chip, val->intval);
+
+		if (ret)
+			dev_err(&chip->client->dev,
+				"Error(%d) in %s charging", ret,
+				(val->intval ? "enable" : "disable"));
+		else
+			chip->is_charging_enabled = val->intval;
+
+		break;
+	case POWER_SUPPLY_CHARGER_PROP_ENABLE_CHARGER:
+		/* Don't enable the charger unless over voltage is recovered */
+
+		if (chip->bat_health != POWER_SUPPLY_HEALTH_OVERVOLTAGE) {
+			ret = bq24261_enable_charger(chip, val->intval);
+
+			if (ret)
+				dev_err(&chip->client->dev,
+					"Error(%d) in %s charger", ret,
+					(val->intval ? "enable" : "disable"));
+			else
+				chip->is_charger_enabled = val->intval;
+		} else {
+			dev_info(&chip->client->dev, "Battery Over Voltage. Charger will be disabled\n");
+		}
+		break;
+	case POWER_SUPPLY_CHARGER_PROP_CABLE_TYPE:
+		chip->cable_type = val->intval;
+		chip->psy_usb.type = get_power_supply_type(chip->cable_type);
+		if (chip->cable_type != PSY_CHARGER_CABLE_TYPE_NONE) {
+			chip->chrgr_health = POWER_SUPPLY_HEALTH_GOOD;
+			chip->chrgr_stat = BQ24261_CHRGR_STAT_UNKNOWN;
+
+			/*
+			* Adding this processing in order to check
+			* for any faults during connect
+			*/
+
+			ret = bq24261_read_reg(chip->client,
+						BQ24261_STAT_CTRL0_ADDR);
+			if (ret < 0)
+				dev_err(&chip->client->dev, "Error (%d) in reading status register(0x00)\n",
+						ret);
+			else
+				bq24261_handle_irq(chip, ret);
+		} else {
+			chip->chrgr_stat = BQ24261_CHRGR_STAT_UNKNOWN;
+			chip->chrgr_health = POWER_SUPPLY_HEALTH_UNKNOWN;
+			cancel_delayed_work_sync(&chip->low_supply_fault_work);
+		}
+		break;
+	case POWER_SUPPLY_CHARGER_PROP_RESET_WDT:
+		bq24261_reset_timer(chip);
+		break;
+	default:
+		ret = -ENODATA;
+	}
+
+	mutex_unlock(&chip->lock);
+	return ret;
+}
+
+static int bq24261_usb_set_property(struct power_supply *psy,
+				    enum power_supply_property psp,
+				    const union power_supply_propval *val)
+{
+	struct bq24261_charger *chip;
+	int ret = 0;
+
+	chip = container_of(psy, struct bq24261_charger, psy_usb);
+
+	mutex_lock(&chip->lock);
+
+	switch (psp) {
+
+	case POWER_SUPPLY_PROP_PRESENT:
+		chip->present = val->intval;
+		break;
+	case POWER_SUPPLY_PROP_ONLINE:
+		chip->online = val->intval;
+		break;
+	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+		ret = bq24261_set_cc(chip, val->intval);
+		if (!ret)
+			chip->cc = val->intval;
+		break;
+	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
+		ret = bq24261_set_cv(chip, val->intval);
+		if (!ret)
+			chip->cv = val->intval;
+		break;
+	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
+		chip->max_cc = val->intval;
+		break;
+	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
+		chip->max_cv = val->intval;
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
+		ret = bq24261_set_iterm(chip, val->intval);
+		if (!ret)
+			chip->iterm = val->intval;
+		break;
+	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
+		ret = bq24261_set_inlmt(chip, val->intval);
+		if (!ret)
+			chip->inlmt = val->intval;
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
+		chip->cntl_state = val->intval;
+		break;
+	case POWER_SUPPLY_PROP_TEMP_MAX:
+		chip->max_temp = val->intval;
+		break;
+	case POWER_SUPPLY_PROP_TEMP_MIN:
+		chip->min_temp = val->intval;
+		break;
+	default:
+		ret = -ENODATA;
+	}
+
+	mutex_unlock(&chip->lock);
+	return ret;
+}
+
+static int bq24261_usb_psyc_get_property(struct power_supply_charger *psyc,
+				    enum power_supply_charger_property pscp,
+				    union power_supply_propval *val)
+{
+	struct bq24261_charger *chip;
+
+	chip = container_of(psyc, struct bq24261_charger, psyc_usb);
+
+	mutex_lock(&chip->lock);
+
+	switch (pscp) {
+	case POWER_SUPPLY_CHARGER_PROP_CABLE_TYPE:
+		val->intval = chip->cable_type;
+		break;
+	case POWER_SUPPLY_CHARGER_PROP_ENABLE_CHARGING:
+		val->intval = (chip->is_charging_enabled &&
+			(chip->chrgr_stat == BQ24261_CHRGR_STAT_CHARGING));
+
+		break;
+	case POWER_SUPPLY_CHARGER_PROP_ENABLE_CHARGER:
+		val->intval = is_bq24261_enabled(chip);
+		break;
+	default:
+		mutex_unlock(&chip->lock);
+		return -EINVAL;
+	}
+
+	mutex_unlock(&chip->lock);
+	return 0;
+}
+
+static int bq24261_usb_get_property(struct power_supply *psy,
+				    enum power_supply_property psp,
+				    union power_supply_propval *val)
+{
+	struct bq24261_charger *chip;
+
+	chip  = container_of(psy, struct bq24261_charger, psy_usb);
+
+	mutex_lock(&chip->lock);
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_PRESENT:
+		val->intval = chip->present;
+		break;
+	case POWER_SUPPLY_PROP_ONLINE:
+		val->intval = chip->online;
+		break;
+	case POWER_SUPPLY_PROP_HEALTH:
+		val->intval = chip->chrgr_health;
+		break;
+	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
+		val->intval = chip->max_cc;
+		break;
+	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
+		val->intval = chip->max_cv;
+		break;
+	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+		val->intval = chip->cc;
+		break;
+	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
+		val->intval = chip->cv;
+		break;
+	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
+		val->intval = chip->inlmt;
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
+		val->intval = chip->iterm;
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
+		val->intval = chip->cntl_state;
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX:
+		val->intval = chip->pdata->num_throttle_states;
+		break;
+	case POWER_SUPPLY_PROP_MODEL_NAME:
+		val->strval = chip->model_name;
+		break;
+	case POWER_SUPPLY_PROP_MANUFACTURER:
+		val->strval = chip->manufacturer;
+		break;
+	case POWER_SUPPLY_PROP_TEMP_MAX:
+		val->intval = chip->max_temp;
+		break;
+	case POWER_SUPPLY_PROP_TEMP_MIN:
+		val->intval = chip->min_temp;
+		break;
+	default:
+		mutex_unlock(&chip->lock);
+		return -EINVAL;
+	}
+
+	mutex_unlock(&chip->lock);
+	return 0;
+}
+
+static inline struct power_supply *get_psy_battery(void)
+{
+	struct class_dev_iter iter;
+	struct device *dev;
+	static struct power_supply *pst;
+
+	class_dev_iter_init(&iter, power_supply_class, NULL, NULL);
+	while ((dev = class_dev_iter_next(&iter))) {
+		pst = (struct power_supply *)dev_get_drvdata(dev);
+		if (pst->type == POWER_SUPPLY_TYPE_BATTERY) {
+			class_dev_iter_exit(&iter);
+			return pst;
+		}
+	}
+	class_dev_iter_exit(&iter);
+
+	return NULL;
+}
+
+static inline int get_battery_voltage(int *volt)
+{
+	struct power_supply *psy;
+	union power_supply_propval val;
+	int ret;
+
+	psy = get_psy_battery();
+	if (!psy)
+		return -EINVAL;
+
+	ret = psy->get_property(psy, POWER_SUPPLY_PROP_VOLTAGE_NOW, &val);
+	if (!ret)
+		*volt = (val.intval);
+
+	return ret;
+}
+
+static inline int get_battery_property
+	(enum power_supply_property prop, int *prop_val)
+{
+	struct power_supply *psy;
+
+	psy = get_psy_battery();
+	if (!psy)
+		return -EINVAL;
+
+	*prop_val = psy_get_ps_int_property(psy, prop);
+
+	return 0;
+}
+
+static inline int get_battery_volt_max_design(int *volt)
+{
+	return get_battery_property(POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, volt);
+
+}
+
+static void notify_worker(struct work_struct *work)
+{
+
+	struct bq24261_charger *chip = container_of(work,
+						    struct bq24261_charger,
+						    notify_work.work);
+
+	atomic_notifier_call_chain(&power_supply_notifier,
+				PSY_EVENT_PROP_CHANGED, &chip->psy_usb);
+	schedule_delayed_work(&chip->notify_work, WDT_RESET_DELAY);
+}
+
+int bq24261_get_bat_health(void)
+{
+
+	struct bq24261_charger *chip;
+
+	if (!bq24261_client)
+		return -ENODEV;
+
+	chip = i2c_get_clientdata(bq24261_client);
+
+	return chip->bat_health;
+}
+
+
+static void bq24261_low_supply_fault_work(struct work_struct *work)
+{
+	struct bq24261_charger *chip = container_of(work,
+						    struct bq24261_charger,
+						    low_supply_fault_work.work);
+
+	if (chip->chrgr_stat == BQ24261_CHRGR_STAT_FAULT) {
+		dev_err(&chip->client->dev, "Low Supply Fault detected!!\n");
+		chip->chrgr_health = POWER_SUPPLY_HEALTH_DEAD;
+		power_supply_changed(&chip->psy_usb);
+	}
+	return;
+}
+
+
+/* is_bat_over_voltage: check battery is over voltage or not
+*  @chip: bq24261_charger context
+*
+*  This function is used to verify the over voltage condition.
+*  In some scenarios, HW generates Over Voltage exceptions when
+*  battery voltage is normal. This function uses the over voltage
+*  condition (voltage_max_design * 1.01) to verify battery is really
+*  over charged or not.
+*/
+
+static bool is_bat_over_voltage(struct bq24261_charger *chip)
+{
+	int bat_volt, bat_volt_max_des, ret;
+
+	ret = get_battery_voltage(&bat_volt);
+	if (ret)
+		return true;
+
+	ret = get_battery_volt_max_design(&bat_volt_max_des);
+
+	if (ret)
+		bat_volt_max_des = BQ24261_DEF_BAT_VOLT_MAX_DESIGN;
+
+	dev_info(&chip->client->dev, "bat_volt=%d Voltage Max Design=%d OVP_VOLT=%d \n",
+			bat_volt, bat_volt_max_des,
+			(bat_volt_max_des/1000 * BQ24261_OVP_MULTIPLIER));
+
+	if ((bat_volt) >= (bat_volt_max_des / 1000 *
+					BQ24261_OVP_MULTIPLIER))
+			return true;
+
+	return false;
+}
+
+#define IS_BATTERY_OVER_VOLTAGE_RECOVERED(chip) \
+	(!is_bat_over_voltage(chip))
+
+static void handle_battery_over_voltage(struct bq24261_charger *chip)
+{
+	/*
+	* Set Health to Over Voltage. Disable charger to discharge
+	* battery to reduce the battery voltage.
+	*/
+	chip->bat_health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+	bq24261_enable_charger(chip, false);
+	chip->is_charger_enabled = false;
+	cancel_delayed_work_sync(&chip->exception_mon_work);
+	schedule_delayed_work(&chip->exception_mon_work,
+			EXCEPTION_MONITOR_DELAY);
+}
+
+static void bq24261_exception_mon_work(struct work_struct *work)
+{
+	struct bq24261_charger *chip = container_of(work,
+						    struct bq24261_charger,
+						    exception_mon_work.work);
+	/* Only over voltage exception need to monitor.*/
+	if (IS_BATTERY_OVER_VOLTAGE_RECOVERED(chip)) {
+		dev_info(&chip->client->dev, "Over Voltage Exception Recovered\n");
+		chip->bat_health = POWER_SUPPLY_HEALTH_GOOD;
+		bq24261_enable_charger(chip, true);
+		chip->is_charger_enabled = true;
+		resume_charging(chip);
+	} else {
+		schedule_delayed_work(&chip->exception_mon_work,
+			      EXCEPTION_MONITOR_DELAY);
+	}
+}
+
+static int bq24261_handle_irq(struct bq24261_charger *chip, u8 stat_reg)
+{
+	struct i2c_client *client = chip->client;
+	bool notify = true;
+
+	dev_info(&client->dev, "%s:%d stat=0x%x\n",
+			__func__, __LINE__, stat_reg);
+
+	switch (stat_reg & BQ24261_STAT_MASK) {
+	case BQ24261_STAT_READY:
+		chip->chrgr_stat = BQ24261_CHRGR_STAT_READY;
+		chip->chrgr_health = POWER_SUPPLY_HEALTH_GOOD;
+		chip->bat_health = POWER_SUPPLY_HEALTH_GOOD;
+		dev_info(&client->dev, "Charger Status: Ready\n");
+		notify = false;
+		break;
+	case BQ24261_STAT_CHRG_PRGRSS:
+		chip->chrgr_stat = BQ24261_CHRGR_STAT_CHARGING;
+		chip->chrgr_health = POWER_SUPPLY_HEALTH_GOOD;
+		chip->bat_health = POWER_SUPPLY_HEALTH_GOOD;
+		dev_info(&client->dev, "Charger Status: Charge Progress\n");
+		break;
+	case BQ24261_STAT_CHRG_DONE:
+		chip->chrgr_health = POWER_SUPPLY_HEALTH_GOOD;
+		chip->bat_health = POWER_SUPPLY_HEALTH_GOOD;
+		dev_info(&client->dev, "Charger Status: Charge Done\n");
+
+		/*
+		*  HW reports charge termination. Stop h/w charge termination
+		* and resume charging. Let power supply charging driver decide
+		* on charge termination
+		*/
+
+		bq24261_enable_hw_charge_term(chip, false);
+		resume_charging(chip);
+		break;
+
+	case BQ24261_STAT_FAULT:
+		break;
+	}
+
+	if (stat_reg & BQ24261_BOOST_MASK)
+		dev_info(&client->dev, "Boost Mode\n");
+
+	if ((stat_reg & BQ24261_STAT_MASK) == BQ24261_STAT_FAULT) {
+		chip->chrgr_stat = BQ24261_CHRGR_STAT_FAULT;
+
+		switch (stat_reg & BQ24261_FAULT_MASK) {
+		case BQ24261_VOVP:
+			chip->chrgr_health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+			dev_err(&client->dev, "Charger OVP Fault\n");
+			break;
+
+		case BQ24261_LOW_SUPPLY:
+			notify = false;
+
+			if (chip->cable_type !=
+					PSY_CHARGER_CABLE_TYPE_NONE) {
+				schedule_delayed_work
+					(&chip->low_supply_fault_work,
+					5*HZ);
+				dev_dbg(&client->dev,
+					"Schedule Low Supply Fault work!!\n");
+			}
+			break;
+
+		case BQ24261_THERMAL_SHUTDOWN:
+			chip->chrgr_health = POWER_SUPPLY_HEALTH_OVERHEAT;
+			dev_err(&client->dev, "Charger Thermal Fault\n");
+			break;
+
+		case BQ24261_BATT_TEMP_FAULT:
+			chip->bat_health = POWER_SUPPLY_HEALTH_OVERHEAT;
+			dev_err(&client->dev, "Battery Temperature Fault\n");
+			break;
+
+		case BQ24261_TIMER_FAULT:
+			chip->bat_health = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+			chip->chrgr_health = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+			dev_err(&client->dev, "Charger Timer Fault\n");
+			break;
+
+		case BQ24261_BATT_OVP:
+			notify = false;
+			if (chip->bat_health !=
+					POWER_SUPPLY_HEALTH_OVERVOLTAGE) {
+				if (!is_bat_over_voltage(chip)) {
+					chip->chrgr_stat =
+						BQ24261_CHRGR_STAT_UNKNOWN;
+					resume_charging(chip);
+				} else {
+					dev_err(&client->dev, "Battery Over Voltage Fault\n");
+					handle_battery_over_voltage(chip);
+					notify = true;
+				}
+			}
+			break;
+		case BQ24261_NO_BATTERY:
+			dev_err(&client->dev, "No Battery Connected\n");
+			break;
+
+		}
+
+	}
+
+	wake_up(&chip->wait_ready);
+
+	chip->is_vsys_on = bq24261_is_vsys_on(chip);
+	if (notify)
+		power_supply_changed(&chip->psy_usb);
+
+	return 0;
+}
+
+static irqreturn_t bq24261_thread_handler(int id, void *data)
+{
+	struct bq24261_charger *chip = (struct bq24261_charger *)data;
+	int ret;
+
+	mutex_lock(&chip->lock);
+
+	ret = bq24261_read_reg(chip->client, BQ24261_STAT_CTRL0_ADDR);
+	if (ret < 0)
+		dev_err(&chip->client->dev,
+			"Error (%d) in reading BQ24261_STAT_CTRL0_ADDR\n", ret);
+	else
+		bq24261_handle_irq(chip, ret);
+
+	mutex_unlock(&chip->lock);
+
+	return IRQ_HANDLED;
+}
+
+static enum bq2426x_model_num bq24261_get_model(int bq24261_rev_reg)
+{
+	switch (bq24261_rev_reg & BQ24261_REV_MASK) {
+	case BQ24260_REV:
+		return BQ24260;
+	case BQ24261_REV:
+		return BQ24261;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int bq24261_probe(struct i2c_client *client,
+			 const struct i2c_device_id *id)
+{
+	struct i2c_adapter *adapter;
+	struct bq24261_charger *chip;
+	int ret;
+	enum bq2426x_model_num bq24261_rev;
+
+	adapter = to_i2c_adapter(client->dev.parent);
+
+	if (!client->dev.platform_data) {
+		dev_err(&client->dev, "platform data is null");
+		return -EFAULT;
+	}
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
+		dev_err(&client->dev,
+			"I2C adapter %s doesn't support BYTE DATA transfer\n",
+			adapter->name);
+		return -EIO;
+	}
+
+	ret = bq24261_read_reg(client, BQ24261_VENDOR_REV_ADDR);
+	if (ret < 0) {
+		dev_err(&client->dev,
+			"Error (%d) in reading BQ24261_VENDOR_REV_ADDR\n", ret);
+		return ret;
+	}
+
+	bq24261_rev = bq24261_get_model(ret);
+	if (((ret & BQ24261_VENDOR_MASK) != BQ24261_VENDOR) ||
+		(bq24261_rev < 0)) {
+		dev_err(&client->dev,
+			"Invalid Vendor/Revision number in BQ24261_VENDOR_REV_ADDR: %d",
+			ret);
+		return -ENODEV;
+	}
+
+	chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
+	if (!chip) {
+		dev_err(&client->dev, "mem alloc failed\n");
+		return -ENOMEM;
+	}
+
+	init_waitqueue_head(&chip->wait_ready);
+	i2c_set_clientdata(client, chip);
+	chip->pdata = client->dev.platform_data;
+
+
+	chip->client = client;
+	chip->pdata = client->dev.platform_data;
+
+	chip->psy_usb.name = DEV_NAME;
+	chip->psy_usb.type = POWER_SUPPLY_TYPE_USB;
+	chip->psy_usb.properties = bq24261_usb_props;
+	chip->psy_usb.num_properties = ARRAY_SIZE(bq24261_usb_props);
+	chip->psy_usb.get_property = bq24261_usb_get_property;
+	chip->psy_usb.set_property = bq24261_usb_set_property;
+	chip->psy_usb.supplied_to = chip->pdata->supplied_to;
+	chip->psy_usb.num_supplicants = chip->pdata->num_supplicants;
+	/* Limit charge current */
+	chip->max_cc = 1500;
+	chip->chrgr_stat = BQ24261_CHRGR_STAT_UNKNOWN;
+	chip->chrgr_health = POWER_SUPPLY_HEALTH_UNKNOWN;
+
+	chip->psyc_usb.get_property = bq24261_usb_psyc_get_property;
+	chip->psyc_usb.set_property = bq24261_usb_psyc_set_property;
+	chip->psyc_usb.throttle_states = chip->pdata->throttle_states;
+	chip->psyc_usb.num_throttle_states = chip->pdata->num_throttle_states;
+	chip->psyc_usb.supported_cables = PSY_CHARGER_CABLE_TYPE_USB;
+
+	strncpy(chip->model_name,
+		bq24261_model_name[bq24261_rev].model_name,
+		MODEL_NAME_SIZE);
+	strncpy(chip->manufacturer, DEV_MANUFACTURER,
+		DEV_MANUFACTURER_NAME_SIZE);
+
+	mutex_init(&chip->lock);
+	ret = power_supply_register(&client->dev, &chip->psy_usb);
+	if (ret) {
+		dev_err(&client->dev, "Failed: power supply register (%d)\n",
+			ret);
+		return ret;
+	}
+
+	chip->psyc_usb.psy = &chip->psy_usb;
+
+	ret = power_supply_register_charger(&chip->psyc_usb);
+	if (ret) {
+		dev_err(&client->dev, "Failed: power supply register (%d)\n",
+			ret);
+		power_supply_unregister(&chip->psy_usb);
+		return ret;
+	}
+
+	INIT_DELAYED_WORK(&chip->notify_work, notify_worker);
+	INIT_DELAYED_WORK(&chip->low_supply_fault_work,
+				bq24261_low_supply_fault_work);
+	INIT_DELAYED_WORK(&chip->exception_mon_work,
+				bq24261_exception_mon_work);
+
+	if (chip->client->irq) {
+		ret = request_threaded_irq(chip->client->irq,
+					   NULL,
+					   bq24261_thread_handler,
+					   IRQF_SHARED|IRQF_NO_SUSPEND,
+					   DEV_NAME, chip);
+		if (ret) {
+			dev_err(&client->dev, "Failed: request_irq (%d)\n",
+				ret);
+			power_supply_unregister(&chip->psy_usb);
+			power_supply_unregister_charger(&chip->psyc_usb);
+			return ret;
+		}
+	}
+
+	if (is_bat_over_voltage(chip))
+		handle_battery_over_voltage(chip);
+	else
+		chip->bat_health = POWER_SUPPLY_HEALTH_GOOD;
+
+	bq24261_client = client;
+
+	return 0;
+}
+
+static int bq24261_remove(struct i2c_client *client)
+{
+	struct bq24261_charger *chip = i2c_get_clientdata(client);
+
+	if (client->irq)
+		free_irq(client->irq, chip);
+
+	flush_scheduled_work();
+
+	power_supply_unregister(&chip->psy_usb);
+	return 0;
+}
+
+static const struct i2c_device_id bq24261_id[] = {
+	{DEV_NAME, 0},
+	{},
+};
+
+MODULE_DEVICE_TABLE(i2c, bq24261_id);
+
+static struct i2c_driver bq24261_driver = {
+	.driver = {
+		   .name = DEV_NAME,
+	},
+	.probe = bq24261_probe,
+	.remove = bq24261_remove,
+	.id_table = bq24261_id,
+};
+
+module_i2c_driver(bq24261_driver);
+
+MODULE_AUTHOR("Jenny TC <jenny.tc@intel.com>");
+MODULE_DESCRIPTION("BQ24261 Charger Driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/power/bq24261-charger.h b/include/linux/power/bq24261-charger.h
new file mode 100644
index 0000000..4ff02af
--- /dev/null
+++ b/include/linux/power/bq24261-charger.h
@@ -0,0 +1,25 @@ 
+/*
+ * bq24261_charger.h: platform data structure for bq24261 driver
+ *
+ * (C) Copyright 2012 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; version 2
+ * of the License.
+ */
+
+#ifndef __BQ24261_CHARGER_H__
+#define __BQ24261_CHARGER_H__
+
+struct bq24261_plat_data {
+	char **supplied_to;
+	size_t num_supplicants;
+	struct psy_throttle_state *throttle_states;
+	size_t num_throttle_states;
+	int safety_timer;
+	bool is_ts_enabled;
+
+};
+
+#endif