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 <[email protected]>
> ---
>  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 <[email protected]>
> + */
> +
> +#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 <[email protected]>");
> +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: signature.asc
Description: Digital signature

Reply via email to