All of lore.kernel.org
 help / color / mirror / Atom feed
From: Sebastian Reichel <sre@kernel.org>
To: Jenny TC <jenny.tc@intel.com>
Cc: linux-kernel@vger.kernel.org,
	Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>,
	Pavel Machek <pavel@ucw.cz>,
	Stephen Rothwell <sfr@canb.auug.org.au>,
	Anton Vorontsov <anton.vorontsov@linaro.org>,
	David Woodhouse <dwmw2@infradead.org>,
	David Cohen <david.a.cohen@linux.intel.com>,
	Pallala Ramakrishna <ramakrishna.pallala@intel.com>
Subject: Re: [PATCH 4/4] power_supply: bq24261 charger driver
Date: Thu, 3 Jul 2014 17:25:29 +0200	[thread overview]
Message-ID: <20140703152528.GB23309@earth.universe> (raw)
In-Reply-To: <1404122155-12369-5-git-send-email-jenny.tc@intel.com>

[-- Attachment #1: Type: text/plain, Size: 43091 bytes --]

Hi,

On Mon, Jun 30, 2014 at 03:25:55PM +0530, Jenny TC wrote:
> 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

I just found one thing. See below:

> Signed-off-by: Jenny TC <jenny.tc@intel.com>
> ---
>  drivers/power/Kconfig                 |   10 +
>  drivers/power/Makefile                |    1 +
>  drivers/power/bq24261_charger.c       | 1351 +++++++++++++++++++++++++++++++++
>  include/linux/power/bq24261-charger.h |   25 +
>  4 files changed, 1387 insertions(+)
>  create mode 100644 drivers/power/bq24261_charger.c
>  create mode 100644 include/linux/power/bq24261-charger.h
> 
> diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
> index 54a0321..4ff080c 100644
> --- a/drivers/power/Kconfig
> +++ b/drivers/power/Kconfig
> @@ -411,6 +411,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..a6e9897 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..c21ea04
> --- /dev/null
> +++ b/drivers/power/bq24261_charger.c
> @@ -0,0 +1,1351 @@
> +/*
> + * 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 MODEL_NAME_SIZE 8
> +
> +#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 BQ24261_PG2_3_REV		(0x06)
> +#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_mV 3500
> +#define BQ24261_MAX_CV_mV 4440
> +
> +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];
> +};
> +
> +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(
> +		unsigned long 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 u8 lookup_regval(u16 tbl[][2], size_t size, u16 in_val)
> +{
> +	int i;
> +
> +	for (i = 1; i < size; ++i)
> +		if (in_val < tbl[i][0])
> +			break;
> +
> +	return tbl[i - 1][1];
> +}
> +
> +static u8 bq24261_cc_to_reg(int cc)
> +{
> +	cc = clamp_t(int, cc, BQ24261_MIN_CC, BQ24261_MAX_CC);
> +	cc = cc - BQ24261_MIN_CC;
> +	return  (cc / 100) << 3;
> +}
> +
> +static u8 bq24261_cv_to_reg(int cv)
> +{
> +	cv = clamp_t(int, cv, BQ24261_MIN_CV_mV, BQ24261_MAX_CV_mV);
> +	cv = cv - BQ24261_MIN_CV_mV;
> +	return	(cv / 20) << 2;
> +}
> +
> +static u8 bq24261_inlmt_to_reg(int inlmt)
> +{
> +	return lookup_regval(bq24261_inlmt, ARRAY_SIZE(bq24261_inlmt), inlmt);
> +}
> +
> +static u8 bq24261_iterm_to_reg(int iterm)
> +{
> +	iterm = clamp_t(int, iterm, BQ24261_MIN_ITERM,  BQ24261_MAX_ITERM);
> +	iterm = iterm - BQ24261_MIN_ITERM;
> +
> +	return   iterm/50;
> +}
> +
> +static u8 bq24261_sfty_tmr_to_reg(int tmr)
> +{
> +	return lookup_regval(bq24261_sfty_tmr,
> +			ARRAY_SIZE(bq24261_sfty_tmr), tmr);
> +}
> +
> +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;
> +
> +	reg_val = bq24261_sfty_tmr_to_reg(chip->pdata->safety_timer);
> +
> +	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 && val) {
> +		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);
> +	}
> +
> +	reg_val = bq24261_cc_to_reg(cc);
> +
> +	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;
> +	}
> +
> +
> +	reg_val = bq24261_cv_to_reg(cv);
> +
> +	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;
> +
> +	reg_val = bq24261_inlmt_to_reg(inlmt);
> +
> +	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)
> +{
> +	int ret = 0;
> +
> +	if (chip->is_charger_enabled)
> +		ret = bq24261_enable_charger(chip, true);
> +	if (!ret && chip->inlmt)
> +		ret = bq24261_set_inlmt(chip, chip->inlmt);
> +	if (!ret && chip->cc)
> +		ret = bq24261_set_cc(chip, chip->cc);
> +	if (!ret && chip->cv)
> +		ret = bq24261_set_cv(chip, chip->cv);
> +	if (!ret && chip->is_charging_enabled)
> +		ret = bq24261_enable_charging(chip, true);
> +
> +	if (ret)
> +		dev_err(&bq24261_client->dev,
> +			"Error(%d) in resume charging\n", ret);
> +}
> +
> +static inline int bq24261_set_iterm(struct bq24261_charger *chip, int iterm)
> +{
> +	u8 reg_val;
> +
> +	reg_val = bq24261_iterm_to_reg(iterm);
> +
> +	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.
> +	*/
> +	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 = "TI";
> +		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)
> +{
> +	static struct power_supply *psy;
> +	struct bq24261_charger *chip;
> +	int i;
> +
> +	if (!bq24261_client)
> +		return NULL;
> +
> +	chip = i2c_get_clientdata(bq24261_client);
> +
> +	for (i = 0; i < chip->psy_usb.num_supplicants; ++i) {
> +		psy = power_supply_get_by_name(chip->psy_usb.supplied_to[i]);
> +		if (psy)
> +			return psy;
> +	}
> +
> +	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:
> +	case BQ24261_PG2_3_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;

why do you set platform_data two times?

> +	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);
> +
> +	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;
> +	}
> +
> +	ret = power_supply_register_charger(&chip->psy_usb, &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);
> +	bq24261_client = client;
> +
> +	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);
> +			bq24261_client = NULL;
> +			return ret;
> +		}
> +	}
> +
> +	if (is_bat_over_voltage(chip))
> +		handle_battery_over_voltage(chip);
> +	else
> +		chip->bat_health = POWER_SUPPLY_HEALTH_GOOD;
> +
> +
> +	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
> -- 
> 1.7.9.5
> 

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

  reply	other threads:[~2014-07-03 15:25 UTC|newest]

Thread overview: 30+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-06-30  9:55 [PATCHv10 0/4] power_supply: Introduce power supply charging driver Jenny TC
2014-06-30  9:55 ` [PATCH 1/4] power_supply: Add inlmt,iterm, min/max temp props Jenny TC
2014-07-03 13:05   ` Sebastian Reichel
2014-06-30  9:55 ` [PATCH 2/4] power_supply: Introduce generic psy charging driver Jenny TC
2014-06-30  9:55 ` [PATCH 3/4] power_supply: Introduce PSE compliant algorithm Jenny TC
2014-07-03 14:56   ` Sebastian Reichel
2014-07-08  6:07     ` Tc, Jenny
2014-07-08 15:56       ` Sebastian Reichel
2014-07-11  2:46         ` Tc, Jenny
2014-07-17 21:13           ` Sebastian Reichel
2014-06-30  9:55 ` [PATCH 4/4] power_supply: bq24261 charger driver Jenny TC
2014-07-03 15:25   ` Sebastian Reichel [this message]
  -- strict thread matches above, loose matches on Subject: below --
2014-07-08  6:04 [PATCHv11 0/4] power_supply: Introduce power supply charging driver Jenny TC
2014-07-08  6:04 ` [PATCH 4/4] power_supply: bq24261 charger driver Jenny TC
2014-06-19 14:02 [PATCHv9 0/4] power_supply: Introduce power supply charging driver Jenny TC
2014-06-19 14:02 ` [PATCH 4/4] power_supply: bq24261 charger driver Jenny TC
2014-02-20  5:53 [PATCH v6 0/4] power_supply: Introduce power supply charging driver Jenny TC
2014-02-20  5:54 ` [PATCH 4/4] power_supply: bq24261 charger driver Jenny TC
2014-02-04  5:12 [PATCH v5 0/4] power_supply: Introduce power supply charging driver Jenny TC
2014-02-04  5:13 ` [PATCH 4/4] power_supply: bq24261 charger driver Jenny TC
2014-02-04 11:36   ` Pavel Machek
2014-02-20  5:03     ` Jenny Tc
2014-02-21 14:44       ` Pavel Machek
2014-02-25 11:51         ` Jenny Tc
2014-01-30 17:30 [PATCH v4 0/4] power_supply: Introduce power supply charging driver Jenny TC
2014-01-30 17:30 ` [PATCH 4/4] power_supply: bq24261 charger driver Jenny TC
2014-01-30 17:01   ` Pavel Machek
2014-01-31  4:05     ` Jenny Tc
2014-01-22 17:19 [PATCH v3 0/4] power_supply: Introduce power supply charging driver Jenny TC
2014-01-22 17:19 ` [PATCH 4/4] power_supply: bq24261 charger driver Jenny TC
2014-01-23  8:58   ` Jingoo Han
2014-01-25 22:30   ` Pavel Machek
2014-01-28 14:14   ` Pavel Machek
2014-01-29 13:23     ` Jenny Tc
2014-01-29  7:26       ` Jingoo Han
2014-01-29  9:36       ` Pavel Machek

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20140703152528.GB23309@earth.universe \
    --to=sre@kernel.org \
    --cc=anton.vorontsov@linaro.org \
    --cc=david.a.cohen@linux.intel.com \
    --cc=dbaryshkov@gmail.com \
    --cc=dwmw2@infradead.org \
    --cc=jenny.tc@intel.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=pavel@ucw.cz \
    --cc=ramakrishna.pallala@intel.com \
    --cc=sfr@canb.auug.org.au \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.