smb358_charger驱动

硬件连接图

下面是该项目所使用的充电芯片smb358的硬件连接图

GPIO引脚配置表

smb引脚 配置说明
SCL GPIO19
SDA GPIO18
EN 高电平使能
STAT MPP02(PMD9607)
THERM MPP06(PMD9607)
D+ USB D+
D- USB D-
USBIN USB_VBUS

cpu是通过i2c的通信方式与smb358进行通信。

正常工作时EN上拉成高电平使能。

PMIC通过读取STAT的状态来判断目前的充电状态。

通过读取THERM的ADC值读取温度。

设备树配置

相关的文件:

  1. kernel/arch/arm/boot/dts/qcom/mdm9607.dtsi
  2. kernel/arch/arm/boot/dts/qcom/mdm9607-pinctrl.dtsi
  3. kernel/arch/arm/boot/dts/qcom/mdm9607-mtp.dtsi

reg = <0x57>; 表示i2c读地址为0x57。

interrupt-parent = <&spmi_bus>; 表示中断控制器为spmi_bus。

interrupts = <0x0 0xa1 0x0>; /* PMIC MPP 2 */ 表示中断为PMIC MPP 2

qcom,float-voltage-mv = <4100>; 饱和充电电压4.2V

qcom,fastchg-current-max-ma = <1000>; 最大快充电流1A

qcom,irq-gpio = <&pm8019_mpps 2 0>;STAT中断引脚用的是MPP02

qcom,chg-vadc = <&pm8019_vadc>; 测电池电压温度所使用的adc

qcom,iterm-ma = <100>;充电饱和的截止电流(充电结束的标志是充电电流小于100mA)

qcom,recharge-mv = <100>;复冲电压,电压低于饱和电流100mA时,再次充电

smb 驱动流程

相关文件/kernel/driver/power/smb-charger.c

if (of_find_property(chip->dev->of_node, "qcom,chg-vadc", NULL)) {
     /* early for VADC get, defer probe if needed */ 
     chip->vadc_dev = qpnp_get_vadc(chip->dev, "chg");
     if (IS_ERR(chip->vadc_dev)) {      
     rc = PTR_ERR(chip->vadc_dev);  
     if (rc != -EPROBE_DEFER)           
         pr_err("vadc property configured incorrectly\n");       
         return rc;   
     }
} 

解析所使用的adc,通过获取vadc指针,根据不同的通道,可以获取对应的电池电压和电池温度。

static int smb_parse_dt(struct smb358_charger *chip)
{               
    int rc; 
    enum of_gpio_flags gpio_flags;
    struct device_node *node = chip->dev->of_node;
    int batt_present_degree_negative;

    if (!node) {
        dev_err(chip->dev, "device tree info. missing\n");
        return -EINVAL;
    }   

    chip->charging_disabled = of_property_read_bool(node,"qcom,charger-disabled");          
    chip->inhibit_disabled = of_property_read_bool(node, "qcom,chg-inhibit-disabled");
    chip->chg_autonomous_mode = of_property_read_bool(node,"qcom,chg-autonomous-mode");
    chip->disable_apsd = of_property_read_bool(node, "qcom,disable-apsd");
    chip->using_pmic_therm = of_property_read_bool(node,"qcom,using-pmic-therm");
    chip->pmic_vbat_sns = of_property_read_bool(node,"qcom,using-vbat-sns");
    chip->bms_controlled_charging = of_property_read_bool(node,"qcom,bms-controlled-charging");
    rc = of_property_read_string(node, "qcom,bms-psy-name",&chip->bms_psy_name);
    chip->chg_valid_gpio = of_get_named_gpio_flags(node,"qcom,chg-valid-gpio", 0, &gpio_flags);
    if (!gpio_is_valid(chip->chg_valid_gpio))
        dev_dbg(chip->dev, "Invalid chg-valid-gpio");
    else
        chip->chg_valid_act_low = gpio_flags & OF_GPIO_ACTIVE_LOW;
    rc = of_property_read_u32(node, "qcom,fastchg-current-max-ma",
                        &chip->fastchg_current_max_ma);
    if (rc)
        chip->fastchg_current_max_ma = SMB358_FAST_CHG_MAX_MA;

    chip->iterm_disabled = of_property_read_bool(node,
                    "qcom,iterm-disabled");

    rc = of_property_read_u32(node, "qcom,iterm-ma", &chip->iterm_ma);
    if (rc < 0)
        chip->iterm_ma = -EINVAL;

    rc = of_property_read_u32(node, "qcom,float-voltage-mv",
                        &chip->vfloat_mv);
    if (rc < 0) {
        chip->vfloat_mv = -EINVAL;
        pr_err("float-voltage-mv property missing, exit\n");
        return -EINVAL;
    }

    rc = of_property_read_u32(node, "qcom,recharge-mv",
                        &chip->recharge_mv);
    if (rc < 0)
        chip->recharge_mv = -EINVAL;

    chip->recharge_disabled = of_property_read_bool(node,
                    "qcom,recharge-disabled");

    rc = of_property_read_u32(node, "qcom,cold-bat-decidegc",
                        &chip->cold_bat_decidegc);
    if (rc < 0)
        chip->cold_bat_decidegc = -EINVAL;

    rc = of_property_read_u32(node, "qcom,hot-bat-decidegc",
                        &chip->hot_bat_decidegc);
    if (rc < 0)
        chip->hot_bat_decidegc = -EINVAL;

    rc = of_property_read_u32(node, "qcom,warm-bat-decidegc",
                        &chip->warm_bat_decidegc);

    rc |= of_property_read_u32(node, "qcom,cool-bat-decidegc",
                        &chip->cool_bat_decidegc);
    if (!rc) {
        rc = of_property_read_u32(node, "qcom,cool-bat-mv",
                        &chip->cool_bat_mv);

        rc |= of_property_read_u32(node, "qcom,warm-bat-mv",
                        &chip->warm_bat_mv);

        rc |= of_property_read_u32(node, "qcom,cool-bat-ma",
                        &chip->cool_bat_ma);

        rc |= of_property_read_u32(node, "qcom,warm-bat-ma",
                        &chip->warm_bat_ma);
        if (rc)
            chip->jeita_supported = false;
        else
            chip->jeita_supported = true;
    }

    pr_debug("jeita_supported = %d", chip->jeita_supported);

    rc = of_property_read_u32(node, "qcom,bat-present-decidegc",
                        &batt_present_degree_negative);
    if (rc < 0)
        chip->bat_present_decidegc = -EINVAL;
    else
        chip->bat_present_decidegc = -batt_present_degree_negative;

    if (of_get_property(node, "qcom,vcc-i2c-supply", NULL)) {
        chip->vcc_i2c = devm_regulator_get(chip->dev, "vcc-i2c");
        if (IS_ERR(chip->vcc_i2c)) {
            dev_err(chip->dev,
                "%s: Failed to get vcc_i2c regulator\n",
                                __func__);
            return PTR_ERR(chip->vcc_i2c);
        }
    }

    chip->skip_usb_suspend_for_fake_battery = of_property_read_bool(node,
                "qcom,skip-usb-suspend-for-fake-battery");
    if (!chip->skip_usb_suspend_for_fake_battery) {
        if (!chip->vadc_dev) {
            dev_err(chip->dev,
                "VADC device not present with usb suspend on fake battery\n");
            return -EINVAL;
        }

        rc = smb_parse_batt_id(chip);
        if (rc) {
            dev_err(chip->dev,
                "failed to read batt-id rc=%d\n", rc);
            return rc;
        }
    }

    pr_debug("inhibit-disabled = %d, recharge-disabled = %d, recharge-mv = %d,",
        chip->inhibit_disabled, chip->recharge_disabled,
                        chip->recharge_mv);
    pr_debug("vfloat-mv = %d, iterm-disabled = %d,",
            chip->vfloat_mv, chip->iterm_disabled);
    pr_debug("fastchg-current = %d, charging-disabled = %d,",
            chip->fastchg_current_max_ma,
                    chip->charging_disabled);
    pr_debug("disable-apsd = %d bms = %s cold-bat-degree = %d,",
        chip->disable_apsd, chip->bms_psy_name,
                    chip->cold_bat_decidegc);
    pr_debug("hot-bat-degree = %d, bat-present-decidegc = %d\n",
        chip->hot_bat_decidegc, chip->bat_present_decidegc);
    return 0;
}

smb_parse_dt解析充电芯片功能配置,根据解析出来的结果,选择某些功能是否支持。比如电池温度检测,各种状态下的温度阈值,对应电池状态的限制电流等
由于设备树只配置了充电最大电压、电流以及截至电流和复充电压。这里可以打印这些参数解析首付正常。

    /* probe the device to check if its actually connected */
    rc = smb358_read_reg(chip, CHG_OTH_CURRENT_CTRL_REG, ®);
    if (rc) {
        pr_err("Failed to detect SMB358, device absent, rc = %d\n", rc);
        goto err_set_vtg_i2c;
    }   

上面这步操作就是判断充电芯片是否正常,由于芯片没有 reset 功能,所以系统启动的
过程芯片自动工作。通过这一步测试 i2c 读写,可以判断芯片的类型是否是 smb853 以
及芯片工作状态。

    chip->batt_psy.name     = "battery";
    chip->batt_psy.type     = POWER_SUPPLY_TYPE_BATTERY;
    chip->batt_psy.get_property = smb358_battery_get_property;
    chip->batt_psy.set_property = smb358_battery_set_property;
    chip->batt_psy.property_is_writeable =
                    smb358_batt_property_is_writeable;
    chip->batt_psy.properties   = smb358_battery_properties;
    chip->batt_psy.num_properties   = ARRAY_SIZE(smb358_battery_properties);
    chip->batt_psy.external_power_changed = smb358_external_power_changed;
    chip->batt_psy.supplied_to = pm_batt_supplied_to;
    chip->batt_psy.num_supplicants = ARRAY_SIZE(pm_batt_supplied_to);

    chip->resume_completed = true;

    rc = power_supply_register(chip->dev, &chip->batt_psy);

power_supply_register()向 power_supply 里添加一个 battery 设备,以及添加设备的信息。smb358_battery_properties 数组内根据我们的需要添加各种电池信息供上层使用,然后
在 smb358_battery_get_property,smb358_battery_set_property 中对这些消息进行处理

    if (chip->using_pmic_therm) {
        if (!chip->jeita_supported) {
            /* add hot/cold temperature monitor */
            chip->adc_param.low_temp = chip->cold_bat_decidegc;
            chip->adc_param.high_temp = chip->hot_bat_decidegc;
        } else {
            chip->adc_param.low_temp = chip->cool_bat_decidegc;
            chip->adc_param.high_temp = chip->warm_bat_decidegc;
        }
        chip->adc_param.timer_interval = ADC_MEAS2_INTERVAL_1S;
        chip->adc_param.state_request = ADC_TM_HIGH_LOW_THR_ENABLE;
        chip->adc_param.btm_ctx = chip;
        chip->adc_param.threshold_notification =
                smb_chg_adc_notification;
#ifdef CONFIG_FOR_BROBDMOBI_R700
        chip->adc_param.channel = P_MUX6_1_1;
#else
        chip->adc_param.channel = LR_MUX1_BATT_THERM;
#endif

        /* update battery missing info in tm_channel_measure*/
        rc = qpnp_adc_tm_channel_measure(chip->adc_tm_dev,
                            &chip->adc_param);
        if (rc)
            pr_err("requesting ADC error %d\n", rc);
    }

probe 中 qpnp_adc_tm_channel_measure 相当于开启一个定时器,循环监控电池温度,
根据电池状况,调整最大电流值,各阶段最大电流值需要根据实际情况添加,根据代码
中的解析设备树部分添加个参数,另外充放电曲线根据实验室实际测试结果填写从放电
数组中

static int smb358_battery_get_property(struct power_supply *psy,
                       enum power_supply_property prop,
                       union power_supply_propval *val)
{
    struct smb358_charger *chip = container_of(psy,
                struct smb358_charger, batt_psy);

    switch (prop) {
    case POWER_SUPPLY_PROP_STATUS:
        val->intval = smb358_get_prop_batt_status(chip);
        break;
    case POWER_SUPPLY_PROP_PRESENT:
        val->intval = smb358_get_prop_batt_present(chip);
        break;
    case POWER_SUPPLY_PROP_CAPACITY:
        val->intval = smb358_get_prop_batt_capacity(chip);
        break;
    case POWER_SUPPLY_PROP_CHARGING_ENABLED:
        val->intval = !(chip->charging_disabled_status & USER);
        break;
    case POWER_SUPPLY_PROP_CHARGE_TYPE:
        val->intval = smb358_get_prop_charge_type(chip);
        break;
    case POWER_SUPPLY_PROP_HEALTH:
        val->intval = smb358_get_prop_batt_health(chip);
        break;
    case POWER_SUPPLY_PROP_TECHNOLOGY:
        val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
        break;
    case POWER_SUPPLY_PROP_MODEL_NAME:
        val->strval = "SMB358";
        break;
    case POWER_SUPPLY_PROP_TEMP:
        val->intval = smb358_get_prop_batt_temp(chip);
        break;
    case POWER_SUPPLY_PROP_VOLTAGE_NOW:
        val->intval = smb358_get_prop_battery_voltage_now(chip);
        break;
    default:
        return -EINVAL;
    }
    return 0;
}

smb358_battery_get_property()函数就是获取各种电池状态(status,present,capacity,health等等),然后上报给上层。

完整代码

/* Copyright (c) 2014-2015 The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * only version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#define pr_fmt(fmt) "SMB358 %s: " fmt, __func__
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 

#define CONFIG_FOR_BROBDMOBI_R700

#define _SMB358_MASK(BITS, POS) \
    ((unsigned char)(((1 << (BITS)) - 1) << (POS)))
#define SMB358_MASK(LEFT_BIT_POS, RIGHT_BIT_POS) \
        _SMB358_MASK((LEFT_BIT_POS) - (RIGHT_BIT_POS) + 1, \
            (RIGHT_BIT_POS))

/* Config/Control registers */
#define CHG_CURRENT_CTRL_REG       0x0
#define CHG_OTH_CURRENT_CTRL_REG   0x1
#define VARIOUS_FUNC_REG       0x2
#define VFLOAT_REG         0x3
#define CHG_CTRL_REG           0x4
#define STAT_AND_TIMER_CTRL_REG        0x5
#define CHG_PIN_EN_CTRL_REG        0x6
#define THERM_A_CTRL_REG       0x7
#define SYSOK_AND_USB3_REG     0x8
#define OTHER_CTRL_REG         0x9
#define OTG_TLMN_THERM_REG     0xA
#define FAULT_INT_REG          0xC
#define STATUS_INT_REG         0xD
#define OTG_TLMN_THERM_REG     0xA

/* Command registers */
#define CMD_A_REG          0x30
#define CMD_B_REG          0x31

/* IRQ status registers */
#define IRQ_A_REG          0x35
#define IRQ_B_REG          0x36
#define IRQ_C_REG          0x37
#define IRQ_D_REG          0x38
#define IRQ_E_REG          0x39
#define IRQ_F_REG          0x3A

/* Status registers */
#define STATUS_C_REG           0x3D
#define STATUS_D_REG           0x3E
#define STATUS_E_REG           0x3F

/* Config bits */
#define CHG_INHI_EN_MASK           BIT(1)
#define CHG_INHI_EN_BIT                BIT(1)
#define CMD_A_CHG_ENABLE_BIT           BIT(1)
#define CMD_A_VOLATILE_W_PERM_BIT      BIT(7)
#define CMD_A_CHG_SUSP_EN_BIT          BIT(2)
#define CMD_A_CHG_SUSP_EN_MASK         BIT(2)
#define CMD_A_OTG_ENABLE_BIT           BIT(4)
#define CMD_A_OTG_ENABLE_MASK          BIT(4)
#define CMD_B_CHG_HC_ENABLE_BIT            BIT(0)
#define USB3_ENABLE_BIT                BIT(5)
#define USB3_ENABLE_MASK           BIT(5)
#define CMD_B_CHG_USB_500_900_ENABLE_BIT   BIT(1)
#define CHG_CTRL_AUTO_RECHARGE_ENABLE_BIT  0x0
#define CHG_CTRL_CURR_TERM_END_CHG_BIT     0x0
//#define CHG_CTRL_BATT_MISSING_DET_THERM_IO    SMB358_MASK(5, 4)
#define CHG_CTRL_BATT_MISSING_DET_THERM_IO 0
#define CHG_CTRL_AUTO_RECHARGE_MASK        BIT(7)
#define CHG_AUTO_RECHARGE_DIS_BIT      BIT(7)
#define CHG_CTRL_CURR_TERM_END_MASK        BIT(6)
#define CHG_CTRL_BATT_MISSING_DET_MASK SMB358_MASK(1, 0)
#define CHG_CTRL_BATT_MISSING_DET_MASK1    SMB358_MASK(5, 4)
#define CHG_CTRL_APSD_EN_BIT           BIT(2)
#define CHG_CTRL_APSD_EN_MASK          BIT(2)
#define CHG_ITERM_MASK             0x07
#define CHG_PIN_CTRL_USBCS_REG_BIT     0x0
#define CHG_PIN_CTRL_STATE_REG_BIT     0x0
/* This is to select if use external pin EN to control CHG */
#define CHG_PIN_CTRL_CHG_EN_LOW_PIN_BIT        SMB358_MASK(6, 5)
#define CHG_PIN_CTRL_CHG_EN_LOW_REG_BIT        0x0
#define CHG_PIN_CTRL_CHG_EN_MASK       SMB358_MASK(6, 5)

#define CHG_LOW_BATT_THRESHOLD \
                SMB358_MASK(3, 0)
#define CHG_PIN_CTRL_USBCS_REG_MASK        BIT(4)
#define CHG_PIN_CTRL_STATE_REG_MASK        BIT(3)
#define CHG_PIN_CTRL_APSD_IRQ_BIT      BIT(1)
#define CHG_PIN_CTRL_APSD_IRQ_MASK     BIT(1)
#define CHG_PIN_CTRL_CHG_ERR_IRQ_BIT       BIT(2)
#define CHG_PIN_CTRL_CHG_ERR_IRQ_MASK      BIT(2)
#define VARIOUS_FUNC_USB_SUSP_EN_REG_BIT   BIT(6)
#define VARIOUS_FUNC_USB_SUSP_MASK     BIT(6)
#define FAULT_INT_HOT_COLD_HARD_BIT        BIT(7)
#define FAULT_INT_HOT_COLD_SOFT_BIT        BIT(6)
#define FAULT_INT_INPUT_OV_BIT         BIT(3)
#define FAULT_INT_INPUT_UV_BIT         BIT(2)
#define FAULT_INT_AICL_COMPLETE_BIT        BIT(1)
#define STATUS_INT_CHG_TIMEOUT_BIT     BIT(7)
#define STATUS_INT_OTG_DETECT_BIT      BIT(6)
#define STATUS_INT_BATT_OV_BIT         BIT(5)
#define STATUS_INT_CHGING_BIT          BIT(4)
#define STATUS_INT_CHG_INHI_BIT            BIT(3)
#define STATUS_INT_INOK_BIT            BIT(2)
#define STATUS_INT_MISSING_BATT_BIT        BIT(1)
#define STATUS_INT_LOW_BATT_BIT            BIT(0)
#define THERM_A_THERM_MONITOR_EN_BIT   BIT(4)  
#define THERM_A_THERM_MONITOR_EN_MASK      BIT(4)
#define VFLOAT_MASK                0x3F

#define CHG_OTG_CURRENT_LIMIT_REG_BIT  BIT(2) // 500mA 
#define CHG_OTG_CURRENT_LIMIT_REG_MASK     BIT(2) 

/* IRQ status bits */
#define IRQ_A_HOT_HARD_BIT         BIT(6)
#define IRQ_A_COLD_HARD_BIT            BIT(4)
#define IRQ_A_HOT_SOFT_BIT         BIT(2)
#define IRQ_A_COLD_SOFT_BIT            BIT(0)
#define IRQ_B_BATT_MISSING_BIT         BIT(4)
#define IRQ_B_BATT_LOW_BIT         BIT(2)
#define IRQ_B_BATT_OV_BIT          BIT(6)
#define IRQ_B_PRE_FAST_CHG_BIT         BIT(0)
#define IRQ_C_TAPER_CHG_BIT            BIT(2)
#define IRQ_C_TERM_BIT             BIT(0)
#define IRQ_C_INT_OVER_TEMP_BIT            BIT(6)
#define IRQ_D_CHG_TIMEOUT_BIT          (BIT(0) | BIT(2))
#define IRQ_D_AICL_DONE_BIT            BIT(4)
#define IRQ_D_APSD_COMPLETE            BIT(6)
#define IRQ_E_INPUT_UV_BIT         BIT(0)
#define IRQ_E_INPUT_OV_BIT         BIT(2)
#define IRQ_E_AFVC_ACTIVE                       BIT(4)
#define IRQ_F_OTG_VALID_BIT            BIT(2)
#define IRQ_F_OTG_BATT_FAIL_BIT            BIT(4)
#define IRQ_F_OTG_OC_BIT           BIT(6)
#define IRQ_F_POWER_OK             BIT(0)

/* Status  bits */
#define STATUS_C_CHARGING_MASK         SMB358_MASK(2, 1)
#define STATUS_C_FAST_CHARGING         BIT(2)
#define STATUS_C_PRE_CHARGING          BIT(1)
#define STATUS_C_TAPER_CHARGING            SMB358_MASK(2, 1)
#define STATUS_C_CHG_ERR_STATUS_BIT        BIT(6)
#define STATUS_C_CHG_ENABLE_STATUS_BIT     BIT(0)
#define STATUS_C_CHG_HOLD_OFF_BIT      BIT(3)
#define STATUS_D_CHARGING_PORT_MASK \
                SMB358_MASK(3, 0)
#define STATUS_D_PORT_ACA_DOCK         BIT(3)
#define STATUS_D_PORT_SDP          BIT(2)
#define STATUS_D_PORT_DCP          BIT(1)
#define STATUS_D_PORT_CDP          BIT(0)
#define STATUS_D_PORT_OTHER            SMB358_MASK(1, 0)
#define STATUS_D_PORT_ACA_A            (BIT(2) | BIT(0))
#define STATUS_D_PORT_ACA_B            SMB358_MASK(2, 1)
#define STATUS_D_PORT_ACA_C            SMB358_MASK(2, 0)

#define CHG_SYSOK_AND_USB3_HARD_TEMP_LIMITS BIT(2) 
#define CHG_SYSOK_AND_USB3_HARD_TEMP_LIMITS_MASK BIT(2)

/* constants */
#define USB2_MIN_CURRENT_MA        100
#define USB2_MAX_CURRENT_MA        500
#define USB3_MIN_CURRENT_MA        150
#define USB3_MAX_CURRENT_MA        900
#define AC_CHG_CURRENT_MASK        0x70
#define AC_CHG_CURRENT_SHIFT       4
#define SMB358_IRQ_REG_COUNT       6
#define SMB358_FAST_CHG_MIN_MA     200
#define SMB358_FAST_CHG_MAX_MA     2000
#define SMB358_FAST_CHG_SHIFT      5
#define SMB_FAST_CHG_CURRENT_MASK  0xE0
#define SMB358_DEFAULT_BATT_CAPACITY   50
#define SMB358_BATT_GOOD_THRE_2P5  0x1
#define ADC_BUFF_LEN               100
#define PERCENT_BUFF_LEN               100
static int adc_buff[ADC_BUFF_LEN] = {0};
static int percent_buff[PERCENT_BUFF_LEN] = {0};
enum {
    USER        = BIT(0),
    THERMAL     = BIT(1),
    CURRENT     = BIT(2),
    SOC     = BIT(3),
    FAKE_BATTERY    = BIT(4),
};

struct smb358_regulator {
    struct regulator_desc   rdesc;
    struct regulator_dev    *rdev;
};

struct smb358_charger {
    struct i2c_client   *client;
    struct device       *dev;

    bool            inhibit_disabled;
    bool            recharge_disabled;
    int         recharge_mv;
    bool            iterm_disabled;
    int         iterm_ma;
    int         vfloat_mv;
    int         chg_valid_gpio;
    int         chg_valid_act_low;
    int         chg_present;
    int         fake_battery_soc;
    bool            chg_autonomous_mode;
    bool            disable_apsd;
    bool            using_pmic_therm;
    bool            pmic_vbat_sns;
    bool            battery_missing;
    const char      *bms_psy_name;
    bool            resume_completed;
    bool            irq_waiting;
    bool            bms_controlled_charging;
    bool            skip_usb_suspend_for_fake_battery;
    struct mutex        read_write_lock;
    struct mutex        path_suspend_lock;
    struct mutex        irq_complete;
    u8          irq_cfg_mask[2];
    int         irq_gpio;
    int         charging_disabled;
    int         fastchg_current_max_ma;
    unsigned int        cool_bat_ma;
    unsigned int        warm_bat_ma;
    unsigned int        cool_bat_mv;
    unsigned int        warm_bat_mv;
    unsigned int        connected_rid;

    /* debugfs related */
#if defined(CONFIG_DEBUG_FS)
    struct dentry       *debug_root;
    u32         peek_poke_address;
#endif
    /* status tracking */
    bool            batt_full;
    bool            batt_hot;
    bool            batt_cold;
    bool            batt_warm;
    bool            batt_cool;
    bool            jeita_supported;
    int         charging_disabled_status;
    int         usb_suspended;

    /* power supply */
    struct power_supply *usb_psy;
    struct power_supply *bms_psy;
    struct power_supply batt_psy;

    /* otg 5V regulator */
    struct smb358_regulator otg_vreg;

    /* adc_tm paramters */
    struct qpnp_vadc_chip   *vadc_dev;
    struct qpnp_adc_tm_chip *adc_tm_dev;
    struct qpnp_adc_tm_btm_param    adc_param;
    int         cold_bat_decidegc;
    int         hot_bat_decidegc;
    int         cool_bat_decidegc;
    int         warm_bat_decidegc;
    int         bat_present_decidegc;
    /* i2c pull up regulator */
    struct regulator    *vcc_i2c;
};

struct smb_irq_info {
    const char      *name;
    int         (*smb_irq)(struct smb358_charger *chip,
                            u8 rt_stat);
    int         high;
    int         low;
};

struct irq_handler_info {
    u8          stat_reg;
    u8          val;
    u8          prev_val;
    struct smb_irq_info irq_info[4];
};

static int chg_current[] = {
    300, 500, 700, 1000, 1200, 1500, 1800, 2000,
};

static int fast_chg_current[] = {
    200, 450, 600, 900, 1300, 1500, 1800, 2000,
};

/* add supplied to "bms" function */
static char *pm_batt_supplied_to[] = {
    "bms",
};
//copy from MDM9X25
//index is the percent% of battery capacity ,so index 0 is 0, index 101 is +inf
//value is in mV
//this battery's nominal capacity is:2000mAh,step is 20mAh
#ifdef LCD_SECOND_SECHEME
static int r700_chg_bat_voltage[102]=
{
    0,
    3452,3466,3480,3494,3508,3522,3536,3550,3564,3578,
    3592,3606,3620,3634,3650,3654,3658,3662,3666,3670,
    3674,3678,3682,3686,3690,3694,3698,3702,3706,3710,
    3714,3718,3722,3726,3730,3734,3738,3742,3746,3750,
    3756,3762,3768,3774,3780,3786,3792,3798,3804,3810,
    3816,3822,3828,3834,3840,3846,3852,3858,3864,3870,
    3829,3836,3844,3850,3900,3912,3924,3936,3948,3960,
    3972,3984,3996,4008,4020,4032,4044,4056,4068,4080,
    4092,4104,4116,4128,4140,4152,4164,4176,4188,4200,
    4206,4212,4218,4224,4230,4236,4242,4248,4254,4260,
    LONG_MAX
};
static int r700_dischg_bat_voltage[102]=
{
    0,
    3314,3328,3342,3356,3370,3384,3398,3412,3424,3438,
    3452,3466,3480,3494,3508,3522,3536,3550,3564,3578,
    3592,3606,3620,3634,3650,3654,3658,3662,3666,3670,
    3674,3678,3682,3686,3690,3694,3698,3702,3706,3710,
    3714,3718,3722,3726,3730,3734,3738,3742,3746,3750,
    3756,3762,3768,3774,3780,3786,3792,3798,3804,3810,
    3816,3822,3828,3834,3840,3846,3852,3858,3864,3870,
    3829,3836,3844,3850,3900,3912,3924,3936,3948,3960,
    3972,3984,3996,4008,4020,4032,4044,4056,4068,4080,
    4092,4104,4116,4128,4140,4152,4164,4176,4188,4200,
    LONG_MAX
};
#else 
static int r700_chg_bat_voltage[102]=
{
    0,
    3406,3489,3552,3606,3639,3655,3670,3687,3708,3727,
    3736,3744,3758,3767,3777,3790,3798,3806,3816,3825,
    3831,3839,3845,3848,3850,3855,3856,3857,3860,3862,
    3863,3865,3866,3867,3869,3870,3873,3875,3876,3878,
    3880,3882,3885,3888,3889,3890,3894,3897,3899,3903,
    3905,3909,3914,3919,3923,3927,3929,3934,3938,3943,
    3948,3953,3957,3962,3968,3972,3978,3982,3987,3992,
    3997,4006,4012,4017,4024,4031,4036,4046,4051,4061,
    4065,4075,4084,4090,4100,4109,4118,4129,4138,4149,
    4158,4168,4170,4180,4182,4184,4186,4188,4190,4200,
    LONG_MAX
};
static int r700_dischg_bat_voltage[102]=
{
    0,
    3300,3406,3450,3469,3484,3503,3522,3538,3554,3570,
    3582,3591,3601,3611,3621,3627,3635,3640,3645,3650,
    3655,3660,3662,3665,3670,3670,3675,3675,3678,3679,
    3683,3684,3684,3688,3689,3689,3693,3694,3694,3697,
    3699,3699,3701,3704,3704,3709,3709,3714,3714,3719,
    3723,3724,3728,3733,3738,3743,3748,3750,3754,3761,
    3766,3772,3777,3782,3787,3797,3802,3806,3816,3821,
    3829,3836,3844,3850,3860,3865,3875,3880,3890,3898,
    3904,3914,3923,3929,3938,3948,3958,3968,3978,3987,
    3997,4007,4017,4026,4036,4050,4061,4075,4090,4129,
    LONG_MAX
};
#endif
static  int bat_get_chg_cap_percent(int bat_vol);
static  int bat_get_dischg_cap_percent(int bat_vol);
static int batt_avg_percent(int percent);
static int batt_avg_adc(int bat_voltage);

static  int bat_get_chg_cap_percent(int bat_vol)
{
     static int last_percent =0 ;
    if( bat_vol < 0)
        bat_vol = 0;
    if (bat_vol > r700_chg_bat_voltage[100])
        bat_vol = r700_chg_bat_voltage[100];

    //calculate percent

    if( bat_vol < r700_chg_bat_voltage[last_percent])
    {
        while( bat_vol < r700_chg_bat_voltage[last_percent])
            last_percent--;
    }
    else if( bat_vol >= r700_chg_bat_voltage[last_percent+1])
    {
        last_percent++;
         while( r700_chg_bat_voltage[last_percent+1] <= bat_vol)
         {
            last_percent++;
         }
         if(last_percent > 99)
            last_percent = 99;
    }
//  printk("bat_vol = %d , r700_chg_bat_voltage = %d\n",bat_vol,last_percent);
    return last_percent;
}
static  int bat_get_dischg_cap_percent(int bat_vol)
{
    static int last_percent = 0;
    if( bat_vol < 0)
        bat_vol = 0;
    if (bat_vol > r700_dischg_bat_voltage[100])
        bat_vol = r700_dischg_bat_voltage[100];

    //calculate percent

    if( bat_vol < r700_dischg_bat_voltage[last_percent])
    {
        while( bat_vol < r700_dischg_bat_voltage[last_percent])
            last_percent--;
    }
    else if( bat_vol >= r700_dischg_bat_voltage[last_percent+1])
    {
        last_percent++;
         while( r700_dischg_bat_voltage[last_percent+1] <= bat_vol)
        {
            last_percent++;
        }
        if(last_percent > 100)
            last_percent = 100;
        }
//  printk("bat_vol = %d , r700_dischg_bat_voltage = %d\n",bat_vol,last_percent);
    return last_percent;
}
static int batt_avg_adc(int bat_voltage)
{
    int i;
    int stop = 0;
    int sum = 0;

    for (i=0; i= ADC_BUFF_LEN-1) {
        memmove(adc_buff, adc_buff+1, (ADC_BUFF_LEN-1)*sizeof(adc_buff[0]));
        adc_buff[ADC_BUFF_LEN-1] = 0;
    }
    return (sum/(i+1));
}
static int batt_avg_percent(int percent)
{
    int i;
    int stop = 0;
    int sum = 0;

    for (i=0; i= PERCENT_BUFF_LEN-1) {
        memmove(percent_buff, percent_buff+1, (PERCENT_BUFF_LEN-1)*sizeof(percent_buff[0]));
        percent_buff[PERCENT_BUFF_LEN-1] = 0;
    }

    return (sum/(i+1));
}

static int __smb358_read_reg(struct smb358_charger *chip, u8 reg, u8 *val)
{
    s32 ret;

    ret = i2c_smbus_read_byte_data(chip->client, reg);
    if (ret < 0) {
        dev_err(chip->dev,
            "i2c read fail: can't read from %02x: %d\n", reg, ret);
        return ret;
    } else {
        *val = ret;
    }

    return 0;
}

static int __smb358_write_reg(struct smb358_charger *chip, int reg, u8 val)
{
    s32 ret;

    ret = i2c_smbus_write_byte_data(chip->client, reg, val);
    if (ret < 0) {
        dev_err(chip->dev,
            "i2c write fail: can't write %02x to %02x: %d\n",
            val, reg, ret);
        return ret;
    }
    return 0;
}

static int smb358_read_reg(struct smb358_charger *chip, int reg,
                        u8 *val)
{
    int rc;

    mutex_lock(&chip->read_write_lock);
    rc = __smb358_read_reg(chip, reg, val);
    mutex_unlock(&chip->read_write_lock);

    return rc;
}

static int smb358_write_reg(struct smb358_charger *chip, int reg,
                        u8 val)
{
    int rc;

    mutex_lock(&chip->read_write_lock);
    rc = __smb358_write_reg(chip, reg, val);
    mutex_unlock(&chip->read_write_lock);

    return rc;
}

static int smb358_masked_write(struct smb358_charger *chip, int reg,
                            u8 mask, u8 val)
{
    s32 rc;
    u8 temp;

    mutex_lock(&chip->read_write_lock);
    rc = __smb358_read_reg(chip, reg, &temp);
    if (rc) {
        dev_err(chip->dev,
            "smb358_read_reg Failed: reg=%03X, rc=%d\n", reg, rc);
        goto out;
    }
    temp &= ~mask;
    temp |= val & mask;
    rc = __smb358_write_reg(chip, reg, temp);
    if (rc) {
        dev_err(chip->dev,
            "smb358_write Failed: reg=%03X, rc=%d\n", reg, rc);
    }
out:
    mutex_unlock(&chip->read_write_lock);
    return rc;
}

static int smb358_enable_volatile_writes(struct smb358_charger *chip)
{
    int rc;

    rc = smb358_masked_write(chip, CMD_A_REG, CMD_A_VOLATILE_W_PERM_BIT,
                        CMD_A_VOLATILE_W_PERM_BIT);
    if (rc)
        dev_err(chip->dev, "Couldn't write VOLATILE_W_PERM_BIT rc=%d\n",
                rc);

    return rc;
}

static int smb358_fastchg_current_set(struct smb358_charger *chip,
                    unsigned int fastchg_current)
{
    int i;

    if ((fastchg_current < SMB358_FAST_CHG_MIN_MA) ||
        (fastchg_current >  SMB358_FAST_CHG_MAX_MA)) {
        dev_dbg(chip->dev, "bad fastchg current mA=%d asked to set\n",
                        fastchg_current);
        return -EINVAL;
    }

    for (i = ARRAY_SIZE(fast_chg_current) - 1; i >= 0; i--) {
        if (fast_chg_current[i] <= fastchg_current)
            break;
    }

    if (i < 0) {
        dev_err(chip->dev, "Invalid current setting %dmA\n",
                        fastchg_current);
        i = 0;
    }

    i = i << SMB358_FAST_CHG_SHIFT;
    dev_dbg(chip->dev, "fastchg limit=%d setting %02x\n",
                    fastchg_current, i);

    return smb358_masked_write(chip, CHG_CURRENT_CTRL_REG,
                SMB_FAST_CHG_CURRENT_MASK, i);
}

#define MIN_FLOAT_MV       3500
#define MAX_FLOAT_MV       4500
#define VFLOAT_STEP_MV     20
#define VFLOAT_4350MV      4350
static int smb358_float_voltage_set(struct smb358_charger *chip, int vfloat_mv)
{
    u8 temp;

    if ((vfloat_mv < MIN_FLOAT_MV) || (vfloat_mv > MAX_FLOAT_MV)) {
        dev_err(chip->dev, "bad float voltage mv =%d asked to set\n",
                    vfloat_mv);
        return -EINVAL;
    }

    if (VFLOAT_4350MV == vfloat_mv)
        temp = 0x2B;
    else if (vfloat_mv > VFLOAT_4350MV)
        temp = (vfloat_mv - MIN_FLOAT_MV) / VFLOAT_STEP_MV + 1;
    else
        temp = (vfloat_mv - MIN_FLOAT_MV) / VFLOAT_STEP_MV;

    return smb358_masked_write(chip, VFLOAT_REG, VFLOAT_MASK, temp);
}

#define CHG_ITERM_30MA         0x00
#define CHG_ITERM_40MA         0x01
#define CHG_ITERM_60MA         0x02
#define CHG_ITERM_80MA         0x03
#define CHG_ITERM_100MA            0x04
#define CHG_ITERM_125MA            0x05
#define CHG_ITERM_150MA            0x06
#define CHG_ITERM_200MA            0x07
static int smb358_term_current_set(struct smb358_charger *chip)
{
    u8 reg = 0;
    int rc;

    if (chip->iterm_ma != -EINVAL) {
        if (chip->iterm_disabled)
            dev_err(chip->dev, "Error: Both iterm_disabled and iterm_ma set\n");

        if (chip->iterm_ma <= 30)
            reg = CHG_ITERM_30MA;
        else if (chip->iterm_ma <= 40)
            reg = CHG_ITERM_40MA;
        else if (chip->iterm_ma <= 60)
            reg = CHG_ITERM_60MA;
        else if (chip->iterm_ma <= 80)
            reg = CHG_ITERM_80MA;
        else if (chip->iterm_ma <= 100)
            reg = CHG_ITERM_100MA;
        else if (chip->iterm_ma <= 125)
            reg = CHG_ITERM_125MA;
        else if (chip->iterm_ma <= 150)
            reg = CHG_ITERM_150MA;
        else
            reg = CHG_ITERM_200MA;

        rc = smb358_masked_write(chip, CHG_CURRENT_CTRL_REG,
                            CHG_ITERM_MASK, reg);
        if (rc) {
            dev_err(chip->dev,
                "Couldn't set iterm rc = %d\n", rc);
            return rc;
        }
    }

    if (chip->iterm_disabled) {
        rc = smb358_masked_write(chip, CHG_CTRL_REG,
                    CHG_CTRL_CURR_TERM_END_MASK,
                    CHG_CTRL_CURR_TERM_END_MASK);
        if (rc) {
            dev_err(chip->dev, "Couldn't set iterm rc = %d\n",
                                rc);
            return rc;
        }
    } else {
        rc = smb358_masked_write(chip, CHG_CTRL_REG,
                    CHG_CTRL_CURR_TERM_END_MASK, 0);
        if (rc) {
            dev_err(chip->dev,
                "Couldn't enable iterm rc = %d\n", rc);
            return rc;
        }
    }

    return 0;
}

#define VFLT_300MV         0x0C
#define VFLT_200MV         0x08
#define VFLT_100MV         0x04
#define VFLT_50MV          0x00
#define VFLT_MASK          0x0C
static int smb358_recharge_and_inhibit_set(struct smb358_charger *chip)
{
    u8 reg = 0;
    int rc;

    if (chip->recharge_disabled)
        rc = smb358_masked_write(chip, CHG_CTRL_REG,
        CHG_CTRL_AUTO_RECHARGE_MASK, CHG_AUTO_RECHARGE_DIS_BIT);
    else
        rc = smb358_masked_write(chip, CHG_CTRL_REG,
            CHG_CTRL_AUTO_RECHARGE_MASK, 0x0);
    if (rc) {
        dev_err(chip->dev,
            "Couldn't set auto recharge en reg rc = %d\n", rc);
    }

    if (chip->inhibit_disabled)
        rc = smb358_masked_write(chip, CHG_OTH_CURRENT_CTRL_REG,
                    CHG_INHI_EN_MASK, 0x0);
    else
        rc = smb358_masked_write(chip, CHG_OTH_CURRENT_CTRL_REG,
                    CHG_INHI_EN_MASK, CHG_INHI_EN_BIT);
    if (rc) {
        dev_err(chip->dev,
            "Couldn't set inhibit en reg rc = %d\n", rc);
    }

    if (chip->recharge_mv != -EINVAL) {
        if (chip->recharge_mv <= 50)
            reg = VFLT_50MV;
        else if (chip->recharge_mv <= 100)
            reg = VFLT_100MV;
        else if (chip->recharge_mv <= 200)
            reg = VFLT_200MV;
        else
            reg = VFLT_300MV;

        rc = smb358_masked_write(chip, CHG_OTH_CURRENT_CTRL_REG,
                        VFLT_MASK, reg);
        if (rc) {
            dev_err(chip->dev,
                "Couldn't set inhibit threshold rc = %d\n", rc);
            return rc;
        }
    }

    return 0;
}

static int smb358_chg_otg_regulator_enable(struct regulator_dev *rdev)
{
    int rc = 0;
    struct smb358_charger *chip = rdev_get_drvdata(rdev);

    rc = smb358_masked_write(chip, CMD_A_REG, CMD_A_OTG_ENABLE_BIT,
                            CMD_A_OTG_ENABLE_BIT);
    if (rc)
        dev_err(chip->dev, "Couldn't enable OTG mode rc=%d, reg=%2x\n",
                                rc, CMD_A_REG);
    return rc;
}

static int smb358_chg_otg_regulator_disable(struct regulator_dev *rdev)
{
    int rc = 0;
    struct smb358_charger *chip = rdev_get_drvdata(rdev);

    rc = smb358_masked_write(chip, CMD_A_REG, CMD_A_OTG_ENABLE_BIT, 0);
    if (rc)
        dev_err(chip->dev, "Couldn't disable OTG mode rc=%d, reg=%2x\n",
                                rc, CMD_A_REG);
    return rc;
}

static int smb358_chg_otg_regulator_is_enable(struct regulator_dev *rdev)
{
    int rc = 0;
    u8 reg = 0;
    struct smb358_charger *chip = rdev_get_drvdata(rdev);

    rc = smb358_read_reg(chip, CMD_A_REG, ®);
    if (rc) {
        dev_err(chip->dev,
            "Couldn't read OTG enable bit rc=%d, reg=%2x\n",
                            rc, CMD_A_REG);
        return rc;
    }

    return  (reg & CMD_A_OTG_ENABLE_BIT) ? 1 : 0;
}

struct regulator_ops smb358_chg_otg_reg_ops = {
    .enable     = smb358_chg_otg_regulator_enable,
    .disable    = smb358_chg_otg_regulator_disable,
    .is_enabled = smb358_chg_otg_regulator_is_enable,
};

static int smb358_regulator_init(struct smb358_charger *chip)
{
    int rc = 0;
    struct regulator_init_data *init_data;
    struct regulator_config cfg = {};

    init_data = of_get_regulator_init_data(chip->dev, chip->dev->of_node);
    if (!init_data) {
        dev_err(chip->dev, "Allocate memory failed\n");
        return -ENOMEM;
    }

    /* Give the name, then will register */
    if (init_data->constraints.name) {
        chip->otg_vreg.rdesc.owner = THIS_MODULE;
        chip->otg_vreg.rdesc.type = REGULATOR_VOLTAGE;
        chip->otg_vreg.rdesc.ops = &smb358_chg_otg_reg_ops;
        chip->otg_vreg.rdesc.name = init_data->constraints.name;

        cfg.dev = chip->dev;
        cfg.init_data = init_data;
        cfg.driver_data = chip;
        cfg.of_node = chip->dev->of_node;

        init_data->constraints.valid_ops_mask
            |= REGULATOR_CHANGE_STATUS;

        chip->otg_vreg.rdev = regulator_register(
                    &chip->otg_vreg.rdesc, &cfg);
        if (IS_ERR(chip->otg_vreg.rdev)) {
            rc = PTR_ERR(chip->otg_vreg.rdev);
            chip->otg_vreg.rdev = NULL;
            if (rc != -EPROBE_DEFER)
                dev_err(chip->dev,
                    "OTG reg failed, rc=%d\n", rc);
        }
    }
    return rc;
}

static int __smb358_path_suspend(struct smb358_charger *chip, bool suspend)
{
    int rc;

    rc = smb358_masked_write(chip, CMD_A_REG, CMD_A_CHG_SUSP_EN_MASK,
                    suspend ? CMD_A_CHG_SUSP_EN_BIT : 0);
    if (rc < 0)
        dev_err(chip->dev, "Couldn't set CMD_A reg, rc = %d\n", rc);

    return rc;
}

static int smb358_path_suspend(struct smb358_charger *chip, int reason,
                                bool suspend)
{
    int rc = 0;
    int suspended;

    mutex_lock(&chip->path_suspend_lock);
    suspended = chip->usb_suspended;

    if (suspend == false)
        suspended &= ~reason;
    else
        suspended |= reason;

    if (!chip->usb_suspended && suspended) {
        rc = __smb358_path_suspend(chip, true);
        chip->usb_suspended = suspended;
        power_supply_set_online(chip->usb_psy, !chip->usb_suspended);
        power_supply_changed(chip->usb_psy);
    } else if (chip->usb_suspended && !suspended) {
        rc = __smb358_path_suspend(chip, false);
        chip->usb_suspended = suspended;
        power_supply_set_online(chip->usb_psy, !chip->usb_suspended);
        power_supply_changed(chip->usb_psy);
    }

    if (rc)
        dev_err(chip->dev, "Couldn't set/unset suspend rc = %d\n", rc);

    mutex_unlock(&chip->path_suspend_lock);

    return rc;
}


static int __smb358_charging_disable(struct smb358_charger *chip, bool disable)
{
    int rc;

    rc = smb358_masked_write(chip, CMD_A_REG, CMD_A_CHG_ENABLE_BIT,
            disable ? 0 : CMD_A_CHG_ENABLE_BIT);
    if (rc < 0)
        pr_err("Couldn't set CHG_ENABLE_BIT diable = %d, rc = %d\n",
                disable, rc);
    return rc;
}

static int smb358_charging_disable(struct smb358_charger *chip,
                        int reason, int disable)
{
    int rc = 0;
    int disabled;

    disabled = chip->charging_disabled_status;

    pr_debug("reason = %d requested_disable = %d disabled_status = %d\n",
                        reason, disable, disabled);

    if (disable == true)
        disabled |= reason;
    else
        disabled &= ~reason;

    if (!!disabled == !!chip->charging_disabled_status)
        goto skip;

    rc = __smb358_charging_disable(chip, !!disabled);
    if (rc) {
        pr_err("Failed to disable charging rc = %d\n", rc);
        return rc;
    } else {
    /* will not modify online status in this condition */
        power_supply_changed(&chip->batt_psy);
    }

skip:
    chip->charging_disabled_status = disabled;
    return rc;
}

#define MAX_INV_BATT_ID        7700
#define MIN_INV_BATT_ID        7300
static int smb358_hw_init(struct smb358_charger *chip)
{
    int rc;
    u8 reg = 0, mask = 0;

    /*
     * If the charger is pre-configured for autonomous operation,
     * do not apply additonal settings
     */
    if (chip->chg_autonomous_mode) {
        dev_dbg(chip->dev, "Charger configured for autonomous mode\n");
        return 0;
    }

    rc = smb358_enable_volatile_writes(chip);
    if (rc) {
        dev_err(chip->dev, "Couldn't configure volatile writes rc=%d\n",
                rc);
        return rc;
    }

    /* setup defaults for CHG_CNTRL_REG */
    reg = CHG_CTRL_BATT_MISSING_DET_THERM_IO;
    mask = CHG_CTRL_BATT_MISSING_DET_MASK;
    rc = smb358_masked_write(chip, CHG_CTRL_REG, mask, reg);
    if (rc) {
        dev_err(chip->dev, "Couldn't set CHG_CTRL_REG rc=%d\n", rc);
        return rc;
    }

    //reg = BIT(4) | (~(BIT(5)));
    mask = CHG_CTRL_BATT_MISSING_DET_MASK1;
    rc = smb358_masked_write(chip, CHG_CTRL_REG, mask, reg);
    if (rc) {
        dev_err(chip->dev, "Couldn't set CHG_CTRL_REG rc=%d\n", rc);
        return rc;
    }

    /* setup charging will not suspend, if battery temperature outside hard limits. */
    reg = CHG_SYSOK_AND_USB3_HARD_TEMP_LIMITS;
    mask = CHG_SYSOK_AND_USB3_HARD_TEMP_LIMITS_MASK;
    rc = smb358_masked_write(chip, SYSOK_AND_USB3_REG, mask, reg);
    if (rc) {
        dev_err(chip->dev, "Couldn't set SYSOK_AND_USB3_REG rc=%d\n", rc);
        return rc;
    }

    /* setup defaults for PIN_CTRL_REG */
    if (!chip->battery_missing) {
        reg = CHG_PIN_CTRL_USBCS_REG_BIT | CHG_PIN_CTRL_CHG_EN_LOW_REG_BIT | CHG_PIN_CTRL_STATE_REG_BIT |
            CHG_PIN_CTRL_APSD_IRQ_BIT | CHG_PIN_CTRL_CHG_ERR_IRQ_BIT;
        mask = CHG_PIN_CTRL_CHG_EN_MASK | CHG_PIN_CTRL_USBCS_REG_MASK | CHG_PIN_CTRL_STATE_REG_MASK |
            CHG_PIN_CTRL_APSD_IRQ_MASK | CHG_PIN_CTRL_CHG_ERR_IRQ_MASK;
        rc = smb358_masked_write(chip, CHG_PIN_EN_CTRL_REG, mask, reg);
        if (rc) {
            dev_err(chip->dev, "Couldn't set CHG_PIN_EN_CTRL_REG rc=%d\n",
                    rc);
            return rc;
        }
    }

    /* set otg current limit*/
    reg = CHG_OTG_CURRENT_LIMIT_REG_BIT;
    mask = CHG_OTG_CURRENT_LIMIT_REG_MASK;
    rc = smb358_masked_write(chip, OTG_TLMN_THERM_REG, mask, reg);
    if (rc) {
        dev_err(chip->dev, "Couldn't set CHG_OTG_CURRENT_LIMIT_REG rc=%d\n",
                rc);
        return rc;
    }

    /* setup USB suspend and APSD  */
    rc = smb358_masked_write(chip, VARIOUS_FUNC_REG,
        VARIOUS_FUNC_USB_SUSP_MASK, VARIOUS_FUNC_USB_SUSP_EN_REG_BIT);
    if (rc) {
        dev_err(chip->dev, "Couldn't set VARIOUS_FUNC_REG rc=%d\n",
                rc);
        return rc;
    }

    if (!chip->disable_apsd)
        reg = CHG_CTRL_APSD_EN_BIT;
    else
        reg = 0;

    rc = smb358_masked_write(chip, CHG_CTRL_REG,
                CHG_CTRL_APSD_EN_MASK, reg);
    if (rc) {
        dev_err(chip->dev, "Couldn't set CHG_CTRL_REG rc=%d\n",
                rc);
        return rc;
    }
    /* Fault and Status IRQ configuration */
    //reg = FAULT_INT_HOT_COLD_HARD_BIT | FAULT_INT_HOT_COLD_SOFT_BIT
    reg = FAULT_INT_INPUT_UV_BIT | FAULT_INT_AICL_COMPLETE_BIT | FAULT_INT_INPUT_OV_BIT;
    rc = smb358_write_reg(chip, FAULT_INT_REG, reg);
    if (rc) {
        dev_err(chip->dev, "Couldn't set FAULT_INT_REG rc=%d\n", rc);
        return rc;
    }
    reg = STATUS_INT_CHG_TIMEOUT_BIT | STATUS_INT_OTG_DETECT_BIT |
        STATUS_INT_BATT_OV_BIT | STATUS_INT_CHGING_BIT |
        STATUS_INT_CHG_INHI_BIT | STATUS_INT_INOK_BIT |
        STATUS_INT_LOW_BATT_BIT | STATUS_INT_MISSING_BATT_BIT;
    rc = smb358_write_reg(chip, STATUS_INT_REG, reg);
    if (rc) {
        dev_err(chip->dev, "Couldn't set STATUS_INT_REG rc=%d\n", rc);
        return rc;
    }
    /* setup THERM Monitor */
    rc = smb358_masked_write(chip, THERM_A_CTRL_REG,
        THERM_A_THERM_MONITOR_EN_MASK, THERM_A_THERM_MONITOR_EN_BIT);
    if (rc) {
        dev_err(chip->dev, "Couldn't set THERM_A_CTRL_REG rc=%d\n",
                rc);
        return rc;
    }
    /* set the fast charge current limit */
    rc = smb358_fastchg_current_set(chip, chip->fastchg_current_max_ma);
    if (rc) {
        dev_err(chip->dev, "Couldn't set fastchg current rc=%d\n", rc);
        return rc;
    }

    /* set the float voltage */
    rc = smb358_float_voltage_set(chip, chip->vfloat_mv);
    if (rc < 0) {
        dev_err(chip->dev,
            "Couldn't set float voltage rc = %d\n", rc);
        return rc;
    }

    /* set iterm */
    rc = smb358_term_current_set(chip);
    if (rc)
        dev_err(chip->dev, "Couldn't set term current rc=%d\n", rc);

    /* set recharge */
    rc = smb358_recharge_and_inhibit_set(chip);
    if (rc)
        dev_err(chip->dev, "Couldn't set recharge para rc=%d\n", rc);

    /* suspend USB path for fake battery */
    if (!chip->skip_usb_suspend_for_fake_battery) {
        if ((chip->connected_rid >= MIN_INV_BATT_ID) &&
                (chip->connected_rid <= MAX_INV_BATT_ID)) {
            rc = smb358_path_suspend(chip, FAKE_BATTERY, true);
            if (!rc)
                dev_info(chip->dev,
                    "Suspended USB path reason FAKE_BATTERY\n");
        }
    }

    /* enable/disable charging */
    if (chip->charging_disabled) {
        rc = smb358_charging_disable(chip, USER, 1);
        if (rc)
            dev_err(chip->dev, "Couldn't '%s' charging rc = %d\n",
            chip->charging_disabled ? "disable" : "enable", rc);
    } else {
        /*
         * Enable charging explictly,
         * because not sure the default behavior.
         */
        rc = __smb358_charging_disable(chip, 0);
        if (rc)
            dev_err(chip->dev, "Couldn't enable charging\n");
    }

    /*
    * Workaround for recharge frequent issue: When battery is
    * greater than 4.2v, and charging is disabled, charger
    * stops switching. In such a case, system load is provided
    * by battery rather than input, even though input is still
    * there. Make reg09[0:3] to be a non-zero value which can
    * keep the switcher active
    */
    rc = smb358_masked_write(chip, OTHER_CTRL_REG, CHG_LOW_BATT_THRESHOLD,
                        SMB358_BATT_GOOD_THRE_2P5);
    if (rc)
        dev_err(chip->dev, "Couldn't write OTHER_CTRL_REG, rc = %d\n",
                                rc);

    return rc;
}

static enum power_supply_property smb358_battery_properties[] = {
    POWER_SUPPLY_PROP_STATUS,
    POWER_SUPPLY_PROP_PRESENT,
    POWER_SUPPLY_PROP_CHARGING_ENABLED,
    POWER_SUPPLY_PROP_CHARGE_TYPE,
    POWER_SUPPLY_PROP_CAPACITY,
    POWER_SUPPLY_PROP_HEALTH,
    POWER_SUPPLY_PROP_TECHNOLOGY,
    POWER_SUPPLY_PROP_MODEL_NAME,
    POWER_SUPPLY_PROP_TEMP,
    POWER_SUPPLY_PROP_VOLTAGE_NOW,
};

static int smb358_get_prop_batt_status(struct smb358_charger *chip)
{
    int rc;
    u8 reg = 0;

    rc = smb358_read_reg(chip, STATUS_C_REG, ®);
    if (rc) {
        dev_err(chip->dev, "Couldn't read STAT_C rc = %d\n", rc);
        return POWER_SUPPLY_STATUS_UNKNOWN;
    }
    dev_dbg(chip->dev, "%s: STATUS_C_REG=%x\n", __func__, reg);
    if(reg == 0){
        chip->batt_full = false;    
        return POWER_SUPPLY_STATUS_DISCHARGING;
    }else if((reg & STATUS_C_CHARGING_MASK) &&
            !(reg & STATUS_C_CHG_ERR_STATUS_BIT)){
        chip->batt_full = false;    
        return POWER_SUPPLY_STATUS_CHARGING;
    }else{
        chip->batt_full = true;
        return POWER_SUPPLY_STATUS_FULL;
    }
}

static int smb358_get_prop_batt_present(struct smb358_charger *chip)
{
    return !chip->battery_missing;
}

#define TEMP_ARR_SIZE 10
static int smb358_get_prop_battery_voltage_now(struct smb358_charger *chip);
static int smb358_battery_set_property(struct power_supply *psy, enum power_supply_property prop, const union power_supply_propval *val);

static int smb358_get_prop_batt_capacity(struct smb358_charger *chip)
{
    union power_supply_propval ret = {0, };
    int cur_v = 0,bat_voltage=0,ret_cap = 0;
    int cur_v_tmp = 0,p_ret_cap=0;

#ifdef CONFIG_FOR_BROBDMOBI_R700
    static int pre_cap = 0;
    static int cap_arr[TEMP_ARR_SIZE] = {0};
    static int cnt = 0;
    int interval = 0, i = 0, sum = 0;
    int rc;
    u8 reg = 0;
#if 0
    if (chip->battery_missing)
        return 0;
#endif
    cur_v_tmp = smb358_get_prop_battery_voltage_now(chip);

    cur_v = cur_v_tmp / 1000;

    cap_arr[cnt] = cur_v;
    cnt++;

    if (cnt != 0 && (cnt % TEMP_ARR_SIZE) == 0)
        cnt = 0;

    for (; i < TEMP_ARR_SIZE; i++) {
        if (0 == cap_arr[i])
            break;
        sum += cap_arr[i];
    }

    cur_v = sum / i;
    bat_voltage = batt_avg_adc(cur_v);
    //for (i = 0; i < TEMP_ARR_SIZE; i++)
    //  printk("the cap_v val is:%d\n", cap_arr[i]);
#if 0
    switch (cur_v) {
        case 0 ... CAP_RAT10_VOL :
            ret_cap = 0;
            break;
        case CAP_RAT10_VOL + 1 ... CAP_RAT20_VOL :
            ret_cap = 10;
            break;
        case CAP_RAT20_VOL + 1 ... CAP_RAT30_VOL :
            ret_cap = 20;
            break;
        case CAP_RAT30_VOL + 1 ... CAP_RAT40_VOL :
            ret_cap = 30;
            break;
        case CAP_RAT40_VOL + 1 ... CAP_RAT50_VOL :
            ret_cap = 40;
            break;
        case CAP_RAT50_VOL + 1 ... CAP_RAT60_VOL :
            ret_cap = 50;
            break;
        case CAP_RAT60_VOL + 1 ... CAP_RAT70_VOL :
            ret_cap = 60;
            break;
        case CAP_RAT70_VOL + 1 ... CAP_RAT80_VOL :
            ret_cap = 70;
            break;
        case CAP_RAT80_VOL + 1 ... CAP_RAT90_VOL :
            ret_cap = 80;
            break;
        case CAP_RAT90_VOL + 1 ... CAP_RAT100_VOL :
            ret_cap = 90;
            break;
        case CAP_RAT100_VOL + 1 ... CAP_RATEND_VOL:
            ret_cap = 100;
            break;
    }
#endif
    rc = smb358_read_reg(chip, STATUS_C_REG, ®);
    if (rc) {
        dev_err(chip->dev, "Couldn't read STAT_C rc = %d\n", rc);
    }
    if ((reg & STATUS_C_CHARGING_MASK) &&
            !(reg & STATUS_C_CHG_ERR_STATUS_BIT)){
        p_ret_cap = bat_get_chg_cap_percent(bat_voltage);
        ret_cap = batt_avg_percent(p_ret_cap);
        if(pre_cap >= ret_cap && pre_cap < 100)
            ret_cap = pre_cap;   
    //  printk("batt_avg_percent = %d\n",ret_cap);      
    }else{
        p_ret_cap = bat_get_dischg_cap_percent(bat_voltage);
        ret_cap = batt_avg_percent(p_ret_cap);
                if(ret_cap == 100)
                    pre_cap = 100;
        if(pre_cap <= ret_cap && pre_cap > 0)
              ret_cap = pre_cap;
    //  printk("dis_batt_avg_percent = %d\n",ret_cap);    
        if(ret_cap < 2)
            kernel_power_off();
    }
    if (chip->batt_full){
        ret_cap = 100;
    //  printk("chip->batt_full\n");
    }
pre_cap > ret_cap ? (interval = pre_cap - ret_cap) : (interval = ret_cap - pre_cap);

    if (pre_cap != ret_cap && interval <= 10) { // if the interval is more than 10, ignor it.
        ret.intval = ret_cap;
        smb358_battery_set_property(&chip->batt_psy, POWER_SUPPLY_PROP_CAPACITY, &ret);
    }
    pre_cap = ret_cap;       
    return ret_cap;
#else
    if (chip->fake_battery_soc >= 0)
        return chip->fake_battery_soc;

    if (chip->bms_psy) {
        chip->bms_psy->get_property(chip->bms_psy,
                POWER_SUPPLY_PROP_CAPACITY, &ret);
        return ret.intval;
    }

    dev_dbg(chip->dev,
        "Couldn't get bms_psy, return default capacity\n");
    return SMB358_DEFAULT_BATT_CAPACITY;
#endif

}

static int smb358_get_prop_charge_type(struct smb358_charger *chip)
{
    int rc;
    u8 reg = 0;

    rc = smb358_read_reg(chip, STATUS_C_REG, ®);
    if (rc) {
        dev_err(chip->dev, "Couldn't read STAT_C rc = %d\n", rc);
        return POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
    }

    dev_dbg(chip->dev, "%s: STATUS_C_REG=%x\n", __func__, reg);

    reg &= STATUS_C_CHARGING_MASK;

    if (reg == STATUS_C_FAST_CHARGING)
        return POWER_SUPPLY_CHARGE_TYPE_FAST;
    else if (reg == STATUS_C_TAPER_CHARGING)
        return POWER_SUPPLY_CHARGE_TYPE_TAPER;
    else if (reg == STATUS_C_PRE_CHARGING)
        return POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
    else
        return POWER_SUPPLY_CHARGE_TYPE_NONE;
}

static int smb358_get_prop_batt_temp(struct smb358_charger *chip);
static int smb358_get_prop_batt_health(struct smb358_charger *chip)
{
    union power_supply_propval ret = {0, };

#ifdef CONFIG_FOR_BROBDMOBI_R700
    int temp_adc = 0, rc;

    temp_adc = smb358_get_prop_batt_temp(chip);

    switch (temp_adc) {
        case 0 ... TEMP_HOT_VOLTAGE:
            ret.intval = POWER_SUPPLY_HEALTH_OVERHEAT;
            rc = smb358_charging_disable(chip, SOC, true);
            if (rc < 0) {
                dev_err(chip->dev, "Couldn't set charging disable rc = %d\n", rc);
            }
            if (chip->battery_missing)
                chip->battery_missing = false;

            //kernel_power_off();
            break;
        case TEMP_HOT_VOLTAGE + 1 ... TEMP_COLD_VOLTAGE:
            ret.intval = POWER_SUPPLY_HEALTH_GOOD;
            if (chip->battery_missing)
                chip->battery_missing = false;
            //temperature is ok,go to charging state
            rc = smb358_charging_disable(chip, SOC, false);
            if (rc < 0) {
                dev_err(chip->dev, "Couldn't set charging disable rc = %d\n", rc);
            }
            if (chip->battery_missing)
                chip->battery_missing = false;

            break;
        case TEMP_COLD_VOLTAGE + 1 ... TEMP_OVER_COLD_VOLTAGE:
            ret.intval = POWER_SUPPLY_HEALTH_COLD;
            rc = smb358_charging_disable(chip, SOC, true);
            if (rc < 0) {
                dev_err(chip->dev, "Couldn't set charging disable rc = %d\n", rc);
            }
            //kernel_power_off();
            break;
        default:
            ret.intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
            //dev_err(chip->dev, "it is unknow temp, disable the charging = %d\n", temp_adc);
            dev_err(chip->dev, "it is unknow temp %d\n", temp_adc);
            //rc = smb358_charging_disable(chip, SOC, true);
            //if (rc < 0) {
            //  dev_err(chip->dev, "Couldn't set charging disable rc = %d\n", rc);
            //}
            if (!chip->battery_missing)
                chip->battery_missing = true;
            break;
    }
#else

    if (chip->batt_hot)
        ret.intval = POWER_SUPPLY_HEALTH_OVERHEAT;
    else if (chip->batt_cold)
        ret.intval = POWER_SUPPLY_HEALTH_COLD;
    else if (chip->batt_warm)
        ret.intval = POWER_SUPPLY_HEALTH_WARM;
    else if (chip->batt_cool)
        ret.intval = POWER_SUPPLY_HEALTH_COOL;
    else
        ret.intval = POWER_SUPPLY_HEALTH_GOOD;
#endif

    return ret.intval;
}

#define DEFAULT_TEMP 250
static int smb358_get_prop_batt_temp(struct smb358_charger *chip)
{
    int rc = 0;
    struct qpnp_vadc_result results;

#ifdef CONFIG_FOR_BROBDMOBI_R700
    rc = qpnp_vadc_read(chip->vadc_dev, P_MUX6_1_1, &results);
#else
    if (!smb358_get_prop_batt_present(chip)
            || !chip->vadc_dev
            || !chip->using_pmic_therm)
        return DEFAULT_TEMP;

    rc = qpnp_vadc_read(chip->vadc_dev, LR_MUX1_BATT_THERM, &results);
#endif

    if (rc) {
        pr_debug("Unable to read batt temperature rc=%d\n", rc);
        return DEFAULT_TEMP;
    }
    pr_debug("get_bat_temp %d, %lld\n",
        results.adc_code, results.physical);

    return (int)results.physical;
}

static int
smb358_get_prop_battery_voltage_now(struct smb358_charger *chip)
{
    int rc = 0;
    struct qpnp_vadc_result results;

    if (!chip->vadc_dev || !chip->pmic_vbat_sns)
        return 0;

    rc = qpnp_vadc_read(chip->vadc_dev, VBAT_SNS, &results);
    if (rc) {
        pr_err("Unable to read vbat rc=%d\n", rc);
        return 0;
    }
    return results.physical;
}

static int smb358_set_usb_chg_current(struct smb358_charger *chip,
        int current_ma)
{
    int i, rc = 0;
    u8 reg1 = 0, reg2 = 0, mask = 0;

    dev_dbg(chip->dev, "%s: USB current_ma = %d\n", __func__, current_ma);

    if (chip->chg_autonomous_mode) {
        dev_dbg(chip->dev, "%s: Charger in autonmous mode\n", __func__);
        return 0;
    }

    if (current_ma < USB3_MIN_CURRENT_MA && current_ma != 2)
        current_ma = USB2_MIN_CURRENT_MA;

    if (current_ma == USB2_MIN_CURRENT_MA) {
        /* USB 2.0 - 100mA */
        reg1 &= ~USB3_ENABLE_BIT;
        reg2 &= ~CMD_B_CHG_USB_500_900_ENABLE_BIT;
    } else if (current_ma == USB2_MAX_CURRENT_MA) {
        /* USB 2.0 - 500mA */
        reg1 &= ~USB3_ENABLE_BIT;
        reg2 |= CMD_B_CHG_USB_500_900_ENABLE_BIT;
    } else if (current_ma == USB3_MAX_CURRENT_MA) {
        /* USB 3.0 - 900mA */
        reg1 |= USB3_ENABLE_BIT;
        reg2 |= CMD_B_CHG_USB_500_900_ENABLE_BIT;
    } else if (current_ma > USB2_MAX_CURRENT_MA) {
        /* HC mode  - if none of the above */
        reg2 |= CMD_B_CHG_HC_ENABLE_BIT;

        for (i = ARRAY_SIZE(chg_current) - 1; i >= 0; i--) {
            if (chg_current[i] <= current_ma)
                break;
        }
        if (i < 0) {
            dev_err(chip->dev, "Cannot find %dmA\n", current_ma);
            i = 0;
        }

        i = i << AC_CHG_CURRENT_SHIFT;
        rc = smb358_masked_write(chip, CHG_OTH_CURRENT_CTRL_REG,
                        AC_CHG_CURRENT_MASK, i);
        if (rc)
            dev_err(chip->dev, "Couldn't set input mA rc=%d\n", rc);
    }

    mask = CMD_B_CHG_HC_ENABLE_BIT | CMD_B_CHG_USB_500_900_ENABLE_BIT;
    rc = smb358_masked_write(chip, CMD_B_REG, mask, reg2);
    if (rc < 0)
        dev_err(chip->dev, "Couldn't set charging mode rc = %d\n", rc);

    mask = USB3_ENABLE_MASK;
    rc = smb358_masked_write(chip, SYSOK_AND_USB3_REG, mask, reg1);
    if (rc < 0)
        dev_err(chip->dev, "Couldn't set USB3 mode rc = %d\n", rc);

    /* Only set suspend bit when chg present and current_ma = 2 */
    //if (current_ma == 2 && chip->chg_present) {
    if (current_ma == 2 && chip->chg_present && !chip->battery_missing) {
        rc = smb358_path_suspend(chip, CURRENT, true);
        if (rc < 0)
            dev_err(chip->dev, "Couldn't suspend rc = %d\n", rc);
    } else {
        rc = smb358_path_suspend(chip, CURRENT, false);
        if (rc < 0)
            dev_err(chip->dev, "Couldn't set susp rc = %d\n", rc);
    }

    return rc;
}

static int
smb358_batt_property_is_writeable(struct power_supply *psy,
                    enum power_supply_property psp)
{
    switch (psp) {
    case POWER_SUPPLY_PROP_CHARGING_ENABLED:
    case POWER_SUPPLY_PROP_CAPACITY:
        return 1;
    default:
        break;
    }

    return 0;
}

static int bound_soc(int soc)
{
    soc = max(0, soc);
    soc = min(soc, 100);
    return soc;
}

static int smb358_battery_set_property(struct power_supply *psy,
                    enum power_supply_property prop,
                    const union power_supply_propval *val)
{
    int rc;
    struct smb358_charger *chip = container_of(psy,
                struct smb358_charger, batt_psy);

    switch (prop) {
    case POWER_SUPPLY_PROP_STATUS:
        if (!chip->bms_controlled_charging)
            return -EINVAL;
        switch (val->intval) {
        case POWER_SUPPLY_STATUS_FULL:
            rc = smb358_charging_disable(chip, SOC, true);
            if (rc < 0) {
                dev_err(chip->dev,
                    "Couldn't set charging disable rc = %d\n",
                    rc);
            } else {
                chip->batt_full = true;
                dev_dbg(chip->dev, "status = FULL, batt_full = %d\n",
                            chip->batt_full);
            }
            break;
        case POWER_SUPPLY_STATUS_DISCHARGING:
            chip->batt_full = false;
            power_supply_changed(&chip->batt_psy);
            dev_dbg(chip->dev, "status = DISCHARGING, batt_full = %d\n",
                            chip->batt_full);
            break;
        case POWER_SUPPLY_STATUS_CHARGING:
            rc = smb358_charging_disable(chip, SOC, false);
            if (rc < 0) {
                dev_err(chip->dev,
                "Couldn't set charging disable rc = %d\n",
                                rc);
            } else {
                chip->batt_full = false;
                dev_dbg(chip->dev, "status = CHARGING, batt_full = %d\n",
                            chip->batt_full);
            }
            break;
        default:
            return -EINVAL;
        }
        break;
    case POWER_SUPPLY_PROP_CHARGING_ENABLED:
        smb358_charging_disable(chip, USER, !val->intval);
        smb358_path_suspend(chip, USER, !val->intval);
        break;
    case POWER_SUPPLY_PROP_CAPACITY:
        chip->fake_battery_soc = bound_soc(val->intval);
        power_supply_changed(&chip->batt_psy);
        break;
    default:
        return -EINVAL;
    }

    return 0;
}

static int smb358_battery_get_property(struct power_supply *psy,
                       enum power_supply_property prop,
                       union power_supply_propval *val)
{
    struct smb358_charger *chip = container_of(psy,
                struct smb358_charger, batt_psy);

    switch (prop) {
    case POWER_SUPPLY_PROP_STATUS:
        val->intval = smb358_get_prop_batt_status(chip);
        break;
    case POWER_SUPPLY_PROP_PRESENT:
        val->intval = smb358_get_prop_batt_present(chip);
        break;
    case POWER_SUPPLY_PROP_CAPACITY:
        val->intval = smb358_get_prop_batt_capacity(chip);
        break;
    case POWER_SUPPLY_PROP_CHARGING_ENABLED:
        val->intval = !(chip->charging_disabled_status & USER);
        break;
    case POWER_SUPPLY_PROP_CHARGE_TYPE:
        val->intval = smb358_get_prop_charge_type(chip);
        break;
    case POWER_SUPPLY_PROP_HEALTH:
        val->intval = smb358_get_prop_batt_health(chip);
        break;
    case POWER_SUPPLY_PROP_TECHNOLOGY:
        val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
        break;
    case POWER_SUPPLY_PROP_MODEL_NAME:
        val->strval = "SMB358";
        break;
    case POWER_SUPPLY_PROP_TEMP:
        val->intval = smb358_get_prop_batt_temp(chip);
        break;
    case POWER_SUPPLY_PROP_VOLTAGE_NOW:
        val->intval = smb358_get_prop_battery_voltage_now(chip);
        break;
    default:
        return -EINVAL;
    }
    return 0;
}

static int apsd_complete(struct smb358_charger *chip, u8 status)
{
    int rc;
    u8 reg = 0;
    enum power_supply_type type = POWER_SUPPLY_TYPE_UNKNOWN;

    /*
     * If apsd is disabled, charger detection is done by
     * DCIN UV irq.
     * status = ZERO - indicates charger removed, handled
     * by DCIN UV irq
     */
    if (chip->disable_apsd || status == 0) {
        dev_dbg(chip->dev, "APSD %s, status = %d\n",
            chip->disable_apsd ? "disabled" : "enabled", !!status);
        return 0;
    }

    rc = smb358_read_reg(chip, STATUS_D_REG, ®);
    if (rc) {
        dev_err(chip->dev, "Couldn't read STATUS D rc = %d\n", rc);
        return rc;
    }

    dev_dbg(chip->dev, "%s: STATUS_D_REG=%x\n", __func__, reg);

    switch (reg & STATUS_D_CHARGING_PORT_MASK) {
    case STATUS_D_PORT_ACA_DOCK:
    case STATUS_D_PORT_ACA_C:
    case STATUS_D_PORT_ACA_B:
    case STATUS_D_PORT_ACA_A:
        type = POWER_SUPPLY_TYPE_USB_ACA;
        break;
    case STATUS_D_PORT_CDP:
        type = POWER_SUPPLY_TYPE_USB_CDP;
        break;
    case STATUS_D_PORT_DCP:
        type = POWER_SUPPLY_TYPE_USB_DCP;
        break;
    case STATUS_D_PORT_SDP:
        type = POWER_SUPPLY_TYPE_USB;
        break;
    case STATUS_D_PORT_OTHER:
        type = POWER_SUPPLY_TYPE_USB_DCP;
        break;
    default:
        type = POWER_SUPPLY_TYPE_USB;
        break;
    }

    chip->chg_present = !!status;

    dev_dbg(chip->dev, "APSD complete. USB type detected=%d chg_present=%d",
                        type, chip->chg_present);

    power_supply_set_supply_type(chip->usb_psy, type);

     /* SMB is now done sampling the D+/D- lines, indicate USB driver */
    dev_dbg(chip->dev, "%s updating usb_psy present=%d", __func__,
            chip->chg_present);
    power_supply_set_present(chip->usb_psy, chip->chg_present);

    return 0;
}

static int check_tmep(struct smb358_charger *chip, u8 status)
{
    int temp_vol = 0;

    if (!chip->battery_missing) {
        pr_err("the status is:%#x\n", status);
        temp_vol = smb358_get_prop_batt_temp(chip);
        if (temp_vol > TEMP_OVER_COLD_VOLTAGE)
            chip->battery_missing = true;
    }

    return 0;
}

static int chg_uv(struct smb358_charger *chip, u8 status)
{
    int rc;
    /* use this to detect USB insertion only if !apsd */
    if (chip->disable_apsd && status == 0) {
        chip->chg_present = true;
        dev_dbg(chip->dev, "%s updating usb_psy present=%d",
                __func__, chip->chg_present);
        power_supply_set_supply_type(chip->usb_psy,
                        POWER_SUPPLY_TYPE_USB);
        power_supply_set_present(chip->usb_psy, chip->chg_present);

        if (chip->bms_controlled_charging) {
            /*
            * Disable SOC based USB suspend to enable charging on
            * USB insertion.
            */
            rc = smb358_charging_disable(chip, SOC, false);
            if (rc < 0)
                dev_err(chip->dev,
                "Couldn't disable usb suspend rc = %d\n",
                                rc);
        }
    }

    if (status != 0) {
        chip->chg_present = false;
        dev_dbg(chip->dev, "%s updating usb_psy present=%d",
                __func__, chip->chg_present);
    /* we can't set usb_psy as UNKNOWN here, will lead USERSPACE issue */
        power_supply_set_present(chip->usb_psy, chip->chg_present);
    }

    power_supply_changed(chip->usb_psy);
    dev_dbg(chip->dev, "chip->chg_present = %d\n", chip->chg_present);

    return 0;
}

static int chg_ov(struct smb358_charger *chip, u8 status)
{
    u8 psy_health_sts;
    if (status)
        psy_health_sts = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
    else
        psy_health_sts = POWER_SUPPLY_HEALTH_GOOD;

    power_supply_set_health_state(
                chip->usb_psy, psy_health_sts);
    power_supply_changed(chip->usb_psy);

    return 0;
}

#define STATUS_FAST_CHARGING BIT(6)
static int fast_chg(struct smb358_charger *chip, u8 status)
{
    dev_dbg(chip->dev, "%s\n", __func__);

    if (status & STATUS_FAST_CHARGING)
        chip->batt_full = false;
    return 0;
}

static int chg_term(struct smb358_charger *chip, u8 status)
{
    dev_dbg(chip->dev, "%s\n", __func__);
    if (!chip->iterm_disabled)
        chip->batt_full = !!status;
    return 0;
}

static int taper_chg(struct smb358_charger *chip, u8 status)
{
    dev_dbg(chip->dev, "%s\n", __func__);
    return 0;
}

static int chg_recharge(struct smb358_charger *chip, u8 status)
{
    dev_dbg(chip->dev, "%s, status = %d\n", __func__, !!status);
    /* to check the status mean */
    chip->batt_full = !status;
    return 0;
}

static void smb358_chg_set_appropriate_battery_current(
                struct smb358_charger *chip)
{
    int rc;
    unsigned int current_max = chip->fastchg_current_max_ma;

    if (chip->batt_cool)
        current_max =
            min(current_max, chip->cool_bat_ma);
    if (chip->batt_warm)
        current_max =
            min(current_max, chip->warm_bat_ma);
    dev_dbg(chip->dev, "setting %dmA", current_max);
    rc = smb358_fastchg_current_set(chip, current_max);
    if (rc)
        dev_err(chip->dev,
            "Couldn't set charging current rc = %d\n", rc);
}

static void smb358_chg_set_appropriate_vddmax(
                struct smb358_charger *chip)
{
    int rc;
    unsigned int vddmax = chip->vfloat_mv;

    if (chip->batt_cool)
        vddmax = min(vddmax, chip->cool_bat_mv);
    if (chip->batt_warm)
        vddmax = min(vddmax, chip->warm_bat_mv);

    dev_dbg(chip->dev, "setting %dmV\n", vddmax);
    rc = smb358_float_voltage_set(chip, vddmax);
    if (rc)
        dev_err(chip->dev,
            "Couldn't set float voltage rc = %d\n", rc);
}

#define HYSTERESIS_DECIDEGC 20
static void smb_chg_adc_notification(enum qpnp_tm_state state, void *ctx)
{
    struct smb358_charger *chip = ctx;
    bool bat_hot = 0, bat_cold = 0, bat_present = 0, bat_warm = 0,
                            bat_cool = 0;
    int temp;

    if (state >= ADC_TM_STATE_NUM) {
        pr_err("invallid state parameter %d\n", state);
        return;
    }

    temp = smb358_get_prop_batt_temp(chip);

    pr_debug("temp = %d state = %s\n", temp,
                state == ADC_TM_WARM_STATE ? "hot" : "cold");

    if (state == ADC_TM_WARM_STATE) {
        if (temp >= chip->hot_bat_decidegc) {
            bat_hot = true;
            bat_warm = false;
            bat_cold = false;
            bat_cool = false;
            bat_present = true;

            chip->adc_param.low_temp =
                chip->hot_bat_decidegc - HYSTERESIS_DECIDEGC;
            chip->adc_param.state_request =
                ADC_TM_COOL_THR_ENABLE;
        } else if (temp >=
            chip->warm_bat_decidegc && chip->jeita_supported) {
            bat_hot = false;
            bat_warm = true;
            bat_cold = false;
            bat_cool = false;
            bat_present = true;

            chip->adc_param.low_temp =
                chip->warm_bat_decidegc - HYSTERESIS_DECIDEGC;
            chip->adc_param.high_temp =
                chip->hot_bat_decidegc;
        } else if (temp >=
            chip->cool_bat_decidegc && chip->jeita_supported) {
            bat_hot = false;
            bat_warm = false;
            bat_cold = false;
            bat_cool = false;
            bat_present = true;

            chip->adc_param.low_temp =
                chip->cool_bat_decidegc - HYSTERESIS_DECIDEGC;
            chip->adc_param.high_temp =
                chip->warm_bat_decidegc;
        } else if (temp >=
            chip->cold_bat_decidegc) {
            bat_hot = false;
            bat_warm = false;
            bat_cold = false;
            bat_cool = true;
            bat_present = true;

            chip->adc_param.low_temp =
                chip->cold_bat_decidegc - HYSTERESIS_DECIDEGC;
            if (chip->jeita_supported)
                chip->adc_param.high_temp =
                        chip->cool_bat_decidegc;
            else
                chip->adc_param.high_temp =
                        chip->hot_bat_decidegc;
            chip->adc_param.state_request =
                    ADC_TM_HIGH_LOW_THR_ENABLE;
        } else if (temp >= chip->bat_present_decidegc) {
            bat_hot = false;
            bat_warm = false;
            bat_cold = true;
            bat_cool = false;
            bat_present = true;

            chip->adc_param.high_temp = chip->cold_bat_decidegc;
            chip->adc_param.low_temp = chip->bat_present_decidegc
                            - HYSTERESIS_DECIDEGC;
            chip->adc_param.state_request =
                    ADC_TM_HIGH_LOW_THR_ENABLE;
        }
    } else {
        if (temp <= chip->bat_present_decidegc) {
            bat_cold = true;
            bat_cool = false;
            bat_hot = false;
            bat_warm = false;
            bat_present = false;
            chip->adc_param.high_temp = chip->bat_present_decidegc
                            + HYSTERESIS_DECIDEGC;
            chip->adc_param.state_request =
                ADC_TM_WARM_THR_ENABLE;
        } else if (temp <= chip->cold_bat_decidegc) {
            bat_hot = false;
            bat_warm = false;
            bat_cold = true;
            bat_cool = false;
            bat_present = true;
            chip->adc_param.high_temp =
                chip->cold_bat_decidegc + HYSTERESIS_DECIDEGC;
            /* add low_temp to enable batt present check */
            chip->adc_param.low_temp =
                chip->bat_present_decidegc;
            chip->adc_param.state_request =
                ADC_TM_HIGH_LOW_THR_ENABLE;
        } else if (temp <= chip->cool_bat_decidegc &&
                    chip->jeita_supported) {
            bat_hot = false;
            bat_warm = false;
            bat_cold = false;
            bat_cool = true;
            bat_present = true;
            chip->adc_param.high_temp =
                chip->cool_bat_decidegc + HYSTERESIS_DECIDEGC;
            chip->adc_param.low_temp =
                chip->cold_bat_decidegc;
            chip->adc_param.state_request =
                ADC_TM_HIGH_LOW_THR_ENABLE;
        } else if (temp <= chip->warm_bat_decidegc &&
                    chip->jeita_supported) {
            bat_hot = false;
            bat_warm = false;
            bat_cold = false;
            bat_cool = false;
            bat_present = true;
            chip->adc_param.high_temp =
                chip->warm_bat_decidegc + HYSTERESIS_DECIDEGC;
            chip->adc_param.low_temp =
                chip->cool_bat_decidegc;
            chip->adc_param.state_request =
                ADC_TM_HIGH_LOW_THR_ENABLE;
        } else if (temp <= chip->hot_bat_decidegc) {
            bat_hot = false;
            bat_warm = true;
            bat_cold = false;
            bat_cool = false;
            bat_present = true;
            if (chip->jeita_supported)
                chip->adc_param.low_temp =
                    chip->warm_bat_decidegc;
            else
                chip->adc_param.low_temp =
                    chip->cold_bat_decidegc;
            chip->adc_param.high_temp =
                chip->hot_bat_decidegc + HYSTERESIS_DECIDEGC;
            chip->adc_param.state_request =
                    ADC_TM_HIGH_LOW_THR_ENABLE;
        }
    }

    if (bat_present)
        chip->battery_missing = false;
    else
        chip->battery_missing = true;

    if (bat_hot ^ chip->batt_hot || bat_cold ^ chip->batt_cold) {
        chip->batt_hot = bat_hot;
        chip->batt_cold = bat_cold;
        /* stop charging explicitly since we use PMIC thermal pin*/
        if (bat_hot || bat_cold || chip->battery_missing)
            smb358_charging_disable(chip, THERMAL, 1);
        else
            smb358_charging_disable(chip, THERMAL, 0);
    }

    if ((chip->batt_warm ^ bat_warm || chip->batt_cool ^ bat_cool)
                        && chip->jeita_supported) {
        chip->batt_warm = bat_warm;
        chip->batt_cool = bat_cool;
        smb358_chg_set_appropriate_battery_current(chip);
        smb358_chg_set_appropriate_vddmax(chip);
    }

    pr_debug("hot %d, cold %d, warm %d, cool %d, jeita supported %d, missing %d, low = %d deciDegC, high = %d deciDegC\n",
        chip->batt_hot, chip->batt_cold, chip->batt_warm,
        chip->batt_cool, chip->jeita_supported, chip->battery_missing,
        chip->adc_param.low_temp, chip->adc_param.high_temp);
    if (qpnp_adc_tm_channel_measure(chip->adc_tm_dev, &chip->adc_param))
        pr_err("request ADC error\n");
}

/* only for SMB thermal */
static int hot_hard_handler(struct smb358_charger *chip, u8 status)
{
    pr_debug("status = 0x%02x\n", status);
    chip->batt_hot = !!status;
    return 0;
}
static int cold_hard_handler(struct smb358_charger *chip, u8 status)
{
    pr_debug("status = 0x%02x\n", status);
    chip->batt_cold = !!status;
    return 0;
}
static int hot_soft_handler(struct smb358_charger *chip, u8 status)
{
    pr_debug("status = 0x%02x\n", status);
    chip->batt_warm = !!status;
    return 0;
}
static int cold_soft_handler(struct smb358_charger *chip, u8 status)
{
    pr_debug("status = 0x%02x\n", status);
    chip->batt_cool = !!status;
    return 0;
}

static int battery_missing(struct smb358_charger *chip, u8 status)
{
    int temp_vol = 0;
    pr_err("=========the status is:%#x\n", status);
    if (status) {
        temp_vol = smb358_get_prop_batt_temp(chip);
        if (temp_vol > TEMP_OVER_COLD_VOLTAGE)
            chip->battery_missing = true;
    }
    else
        chip->battery_missing = !!status;
    return 0;
}

static struct irq_handler_info handlers[] = {
    [0] = {
        .stat_reg   = IRQ_A_REG,
        .val        = 0,
        .prev_val   = 0,
        .irq_info   = {
            {
                .name       = "cold_soft",
                .smb_irq    = cold_soft_handler,
            },
            {
                .name       = "hot_soft",
                .smb_irq    = hot_soft_handler,
            },
            {
                .name       = "cold_hard",
                .smb_irq    = cold_hard_handler,
            },
            {
                .name       = "hot_hard",
                .smb_irq    = hot_hard_handler,
            },
        },
    },
    [1] = {
        .stat_reg   = IRQ_B_REG,
        .val        = 0,
        .prev_val   = 0,
        .irq_info   = {
            {
                .name       = "chg_hot",
            },
            {
                .name       = "vbat_low",
            },
            {
                .name       = "battery_missing",
                .smb_irq    = battery_missing
            },
            {
                .name       = "battery_ov",
            },
        },
    },
    [2] = {
        .stat_reg   = IRQ_C_REG,
        .val        = 0,
        .prev_val   = 0,
        .irq_info   = {
            {
                .name       = "chg_term",
                .smb_irq    = chg_term,
            },
            {
                .name       = "taper",
                .smb_irq    = taper_chg,
            },
            {
                .name       = "recharge",
                .smb_irq    = chg_recharge,
            },
            {
                .name       = "fast_chg",
                .smb_irq    = fast_chg,
            },
        },
    },
    [3] = {
        .stat_reg   = IRQ_D_REG,
        .val        = 0,
        .prev_val   = 0,
        .irq_info   = {
            {
                .name       = "prechg_timeout",
            },
            {
                .name       = "safety_timeout",
            },
            {
                .name       = "aicl_complete",
            },
            {
                .name       = "src_detect",
                .smb_irq    = apsd_complete,
            },
        },
    },
    [4] = {
        .stat_reg   = IRQ_E_REG,
        .val        = 0,
        .prev_val   = 0,
        .irq_info   = {
            {
                .name       = "usbin_uv",
                .smb_irq        = chg_uv,
            },
            {
                .name       = "usbin_ov",
                .smb_irq    = chg_ov,
            },
            {
                .name       = "unknown",
            },
            {
                .name       = "unknown",
                //.name     = "check_tmep",
                //.smb_irq  = check_tmep,
            },
        },
    },
    [5] = {
        .stat_reg   = IRQ_F_REG,
        .val        = 0,
        .prev_val   = 0,
        .irq_info   = {
            {
                .name       = "power_ok",
            },
            {
                .name       = "otg_det",
            },
            {
                .name       = "otg_batt_uv",
            },
            {
                .name       = "otg_oc",
            },
        },
    },
};

#define IRQ_LATCHED_MASK   0x02
#define IRQ_STATUS_MASK        0x01
#define BITS_PER_IRQ       2
static irqreturn_t smb358_chg_stat_handler(int irq, void *dev_id)
{
    struct smb358_charger *chip = dev_id;
    int i, j;
    u8 triggered;
    u8 changed;
    u8 rt_stat, prev_rt_stat;
    int rc;
    int handler_count = 0;

    mutex_lock(&chip->irq_complete);

    chip->irq_waiting = true;
    if (!chip->resume_completed) {
        dev_dbg(chip->dev, "IRQ triggered before device-resume\n");
        disable_irq_nosync(irq);
        mutex_unlock(&chip->irq_complete);
        return IRQ_HANDLED;
    }
    chip->irq_waiting = false;

    for (i = 0; i < ARRAY_SIZE(handlers); i++) {
        rc = smb358_read_reg(chip, handlers[i].stat_reg,
                        &handlers[i].val);
        if (rc < 0) {
            dev_err(chip->dev, "Couldn't read %d rc = %d\n",
                    handlers[i].stat_reg, rc);
            continue;
        }

        for (j = 0; j < ARRAY_SIZE(handlers[i].irq_info); j++) {
            triggered = handlers[i].val
                   & (IRQ_LATCHED_MASK << (j * BITS_PER_IRQ));
            rt_stat = handlers[i].val
                & (IRQ_STATUS_MASK << (j * BITS_PER_IRQ));
            prev_rt_stat = handlers[i].prev_val
                & (IRQ_STATUS_MASK << (j * BITS_PER_IRQ));
            changed = prev_rt_stat ^ rt_stat;

            if (triggered || changed)
                rt_stat ? handlers[i].irq_info[j].high++ :
                        handlers[i].irq_info[j].low++;

            if ((triggered || changed)
                && handlers[i].irq_info[j].smb_irq != NULL) {
                handler_count++;
                rc = handlers[i].irq_info[j].smb_irq(chip,
                                rt_stat);
                if (rc < 0)
                    dev_err(chip->dev,
                        "Couldn't handle %d irq for reg 0x%02x rc = %d\n",
                        j, handlers[i].stat_reg, rc);
            }
        }
        handlers[i].prev_val = handlers[i].val;
    }

    pr_debug("handler count = %d\n", handler_count);
    if (handler_count) {
        pr_debug("batt psy changed\n");
        power_supply_changed(&chip->batt_psy);
    }

    mutex_unlock(&chip->irq_complete);

    return IRQ_HANDLED;
}

static irqreturn_t smb358_chg_valid_handler(int irq, void *dev_id)
{
    struct smb358_charger *chip = dev_id;
    int present;

    present = gpio_get_value_cansleep(chip->chg_valid_gpio);
    if (present < 0) {
        dev_err(chip->dev, "Couldn't read chg_valid gpio=%d\n",
                        chip->chg_valid_gpio);
        return IRQ_HANDLED;
    }
    present ^= chip->chg_valid_act_low;

    dev_dbg(chip->dev, "%s: chg_present = %d\n", __func__, present);

    if (present != chip->chg_present) {
        chip->chg_present = present;
        dev_dbg(chip->dev, "%s updating usb_psy present=%d",
                __func__, chip->chg_present);
        power_supply_set_present(chip->usb_psy, chip->chg_present);
    }

    return IRQ_HANDLED;
}

static void smb358_external_power_changed(struct power_supply *psy)
{
    struct smb358_charger *chip = container_of(psy,
                struct smb358_charger, batt_psy);
    union power_supply_propval prop = {0,};
    int rc, current_limit = 0;

    if (chip->bms_psy_name)
        chip->bms_psy =
            power_supply_get_by_name((char *)chip->bms_psy_name);

    rc = chip->usb_psy->get_property(chip->usb_psy,
                POWER_SUPPLY_PROP_CURRENT_MAX, &prop);
    if (rc)
        dev_err(chip->dev,
            "Couldn't read USB current_max property, rc=%d\n", rc);
    else
        current_limit = prop.intval / 1000;


    smb358_enable_volatile_writes(chip);
    smb358_set_usb_chg_current(chip, current_limit);

    dev_dbg(chip->dev, "current_limit = %d\n", current_limit);
}

#if defined(CONFIG_DEBUG_FS)
#define LAST_CNFG_REG  0x13
static int show_cnfg_regs(struct seq_file *m, void *data)
{
    struct smb358_charger *chip = m->private;
    int rc;
    u8 reg;
    u8 addr;

    for (addr = 0; addr <= LAST_CNFG_REG; addr++) {
        rc = smb358_read_reg(chip, addr, ®);
        if (!rc)
            seq_printf(m, "0x%02x = 0x%02x\n", addr, reg);
    }

    return 0;
}

static int cnfg_debugfs_open(struct inode *inode, struct file *file)
{
    struct smb358_charger *chip = inode->i_private;

    return single_open(file, show_cnfg_regs, chip);
}

static const struct file_operations cnfg_debugfs_ops = {
    .owner      = THIS_MODULE,
    .open       = cnfg_debugfs_open,
    .read       = seq_read,
    .llseek     = seq_lseek,
    .release    = single_release,
};

#define FIRST_CMD_REG  0x30
#define LAST_CMD_REG   0x33
static int show_cmd_regs(struct seq_file *m, void *data)
{
    struct smb358_charger *chip = m->private;
    int rc;
    u8 reg;
    u8 addr;

    for (addr = FIRST_CMD_REG; addr <= LAST_CMD_REG; addr++) {
        rc = smb358_read_reg(chip, addr, ®);
        if (!rc)
            seq_printf(m, "0x%02x = 0x%02x\n", addr, reg);
    }

    return 0;
}

static int cmd_debugfs_open(struct inode *inode, struct file *file)
{
    struct smb358_charger *chip = inode->i_private;

    return single_open(file, show_cmd_regs, chip);
}

static const struct file_operations cmd_debugfs_ops = {
    .owner      = THIS_MODULE,
    .open       = cmd_debugfs_open,
    .read       = seq_read,
    .llseek     = seq_lseek,
    .release    = single_release,
};

#define FIRST_STATUS_REG   0x35
#define LAST_STATUS_REG        0x3F
static int show_status_regs(struct seq_file *m, void *data)
{
    struct smb358_charger *chip = m->private;
    int rc;
    u8 reg;
    u8 addr;

    for (addr = FIRST_STATUS_REG; addr <= LAST_STATUS_REG; addr++) {
        rc = smb358_read_reg(chip, addr, ®);
        if (!rc)
            seq_printf(m, "0x%02x = 0x%02x\n", addr, reg);
    }

    return 0;
}

static int status_debugfs_open(struct inode *inode, struct file *file)
{
    struct smb358_charger *chip = inode->i_private;

    return single_open(file, show_status_regs, chip);
}

static const struct file_operations status_debugfs_ops = {
    .owner      = THIS_MODULE,
    .open       = status_debugfs_open,
    .read       = seq_read,
    .llseek     = seq_lseek,
    .release    = single_release,
};

static int show_irq_count(struct seq_file *m, void *data)
{
    int i, j, total = 0;

    for (i = 0; i < ARRAY_SIZE(handlers); i++)
        for (j = 0; j < 4; j++) {
            seq_printf(m, "%s=%d\t(high=%d low=%d)\n",
                        handlers[i].irq_info[j].name,
                        handlers[i].irq_info[j].high
                        + handlers[i].irq_info[j].low,
                        handlers[i].irq_info[j].high,
                        handlers[i].irq_info[j].low);
            total += (handlers[i].irq_info[j].high
                    + handlers[i].irq_info[j].low);
        }

    seq_printf(m, "\n\tTotal = %d\n", total);

    return 0;
}

static int irq_count_debugfs_open(struct inode *inode, struct file *file)
{
    struct smb358_charger *chip = inode->i_private;

    return single_open(file, show_irq_count, chip);
}

static const struct file_operations irq_count_debugfs_ops = {
    .owner      = THIS_MODULE,
    .open       = irq_count_debugfs_open,
    .read       = seq_read,
    .llseek     = seq_lseek,
    .release    = single_release,
};

static int get_reg(void *data, u64 *val)
{
    struct smb358_charger *chip = data;
    int rc;
    u8 temp;

    rc = smb358_read_reg(chip, chip->peek_poke_address, &temp);
    if (rc < 0) {
        dev_err(chip->dev,
            "Couldn't read reg %x rc = %d\n",
            chip->peek_poke_address, rc);
        return -EAGAIN;
    }
    *val = temp;
    return 0;
}

static int set_reg(void *data, u64 val)
{
    struct smb358_charger *chip = data;
    int rc;
    u8 temp;

    temp = (u8) val;
    rc = smb358_write_reg(chip, chip->peek_poke_address, temp);
    if (rc < 0) {
        dev_err(chip->dev,
            "Couldn't write 0x%02x to 0x%02x rc= %d\n",
            chip->peek_poke_address, temp, rc);
        return -EAGAIN;
    }
    return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(poke_poke_debug_ops, get_reg, set_reg, "0x%02llx\n");

static int force_irq_set(void *data, u64 val)
{
    struct smb358_charger *chip = data;

    smb358_chg_stat_handler(chip->client->irq, data);
    return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(force_irq_ops, NULL, force_irq_set, "0x%02llx\n");
#endif

#ifdef DEBUG
static void dump_regs(struct smb358_charger *chip)
{
    int rc;
    u8 reg;
    u8 addr;

    for (addr = 0; addr <= LAST_CNFG_REG; addr++) {
        rc = smb358_read_reg(chip, addr, ®);
        if (rc)
            dev_err(chip->dev, "Couldn't read 0x%02x rc = %d\n",
                    addr, rc);
        else
            pr_debug("0x%02x = 0x%02x\n", addr, reg);
    }

    for (addr = FIRST_STATUS_REG; addr <= LAST_STATUS_REG; addr++) {
        rc = smb358_read_reg(chip, addr, ®);
        if (rc)
            dev_err(chip->dev, "Couldn't read 0x%02x rc = %d\n",
                    addr, rc);
        else
            pr_debug("0x%02x = 0x%02x\n", addr, reg);
    }

    for (addr = FIRST_CMD_REG; addr <= LAST_CMD_REG; addr++) {
        rc = smb358_read_reg(chip, addr, ®);
        if (rc)
            dev_err(chip->dev, "Couldn't read 0x%02x rc = %d\n",
                    addr, rc);
        else
            pr_debug("0x%02x = 0x%02x\n", addr, reg);
    }
}
#else
static void dump_regs(struct smb358_charger *chip)
{
}
#endif

static int smb_parse_batt_id(struct smb358_charger *chip)
{
    int rc = 0, rpull = 0, vref = 0;
    int64_t denom, batt_id_uv, numerator;
    struct device_node *node = chip->dev->of_node;
    struct qpnp_vadc_result result;

    rc = of_property_read_u32(node, "qcom,batt-id-vref-uv", &vref);
    if (rc < 0) {
        dev_err(chip->dev,
            "Couldn't read batt-id-vref-uv rc=%d\n", rc);
        return rc;
    }

    rc = of_property_read_u32(node, "qcom,batt-id-rpullup-kohm", &rpull);
    if (rc < 0) {
        dev_err(chip->dev,
            "Couldn't read batt-id-rpullup-kohm rc=%d\n", rc);
        return rc;
    }

    /* read battery ID */
    rc = qpnp_vadc_read(chip->vadc_dev, LR_MUX2_BAT_ID, &result);
    if (rc) {
        dev_err(chip->dev,
            "Couldn't read batt id channel=%d, rc=%d\n",
            LR_MUX2_BAT_ID, rc);
        return rc;
    }
    batt_id_uv = result.physical;

    if (batt_id_uv == 0) {
        /*vadc not correct or batt id line grounded, report 0 kohms */
        dev_warn(chip->dev, "batt_id_uv=0, batt-id grounded\n");
        return 0;
    }

    numerator = batt_id_uv * rpull * 1000;
    denom = vref  - batt_id_uv;

    /* batt id connector might be open, return 0 kohms */
    if (denom == 0)
        return 0;

    chip->connected_rid = div64_s64(numerator, denom);

    dev_dbg(chip->dev,
        "batt_id_voltage=%lld numerator=%lld denom=%lld connected_rid=%d\n",
        batt_id_uv, numerator, denom, chip->connected_rid);

    return 0;
}

static int smb_parse_dt(struct smb358_charger *chip)
{
    int rc;
    enum of_gpio_flags gpio_flags;
    struct device_node *node = chip->dev->of_node;
    int batt_present_degree_negative;

    if (!node) {
        dev_err(chip->dev, "device tree info. missing\n");
        return -EINVAL;
    }

    chip->charging_disabled = of_property_read_bool(node,
                    "qcom,charger-disabled");

    chip->inhibit_disabled = of_property_read_bool(node,
                    "qcom,chg-inhibit-disabled");
    chip->chg_autonomous_mode = of_property_read_bool(node,
                    "qcom,chg-autonomous-mode");

    chip->disable_apsd = of_property_read_bool(node, "qcom,disable-apsd");

    chip->using_pmic_therm = of_property_read_bool(node,
                        "qcom,using-pmic-therm");
    chip->pmic_vbat_sns = of_property_read_bool(node,
                    "qcom,using-vbat-sns");
    chip->bms_controlled_charging = of_property_read_bool(node,
                        "qcom,bms-controlled-charging");

    rc = of_property_read_string(node, "qcom,bms-psy-name",
                        &chip->bms_psy_name);
    if (rc)
        chip->bms_psy_name = NULL;

    chip->chg_valid_gpio = of_get_named_gpio_flags(node,
                "qcom,chg-valid-gpio", 0, &gpio_flags);
    if (!gpio_is_valid(chip->chg_valid_gpio))
        dev_dbg(chip->dev, "Invalid chg-valid-gpio");
    else
        chip->chg_valid_act_low = gpio_flags & OF_GPIO_ACTIVE_LOW;

    rc = of_property_read_u32(node, "qcom,fastchg-current-max-ma",
                        &chip->fastchg_current_max_ma);
    if (rc)
        chip->fastchg_current_max_ma = SMB358_FAST_CHG_MAX_MA;

    chip->iterm_disabled = of_property_read_bool(node,
                    "qcom,iterm-disabled");

    rc = of_property_read_u32(node, "qcom,iterm-ma", &chip->iterm_ma);
    if (rc < 0)
        chip->iterm_ma = -EINVAL;

    rc = of_property_read_u32(node, "qcom,float-voltage-mv",
                        &chip->vfloat_mv);
    if (rc < 0) {
        chip->vfloat_mv = -EINVAL;
        pr_err("float-voltage-mv property missing, exit\n");
        return -EINVAL;
    }

    rc = of_property_read_u32(node, "qcom,recharge-mv",
                        &chip->recharge_mv);
    if (rc < 0)
        chip->recharge_mv = -EINVAL;

    chip->recharge_disabled = of_property_read_bool(node,
                    "qcom,recharge-disabled");

    rc = of_property_read_u32(node, "qcom,cold-bat-decidegc",
                        &chip->cold_bat_decidegc);
    if (rc < 0)
        chip->cold_bat_decidegc = -EINVAL;

    rc = of_property_read_u32(node, "qcom,hot-bat-decidegc",
                        &chip->hot_bat_decidegc);
    if (rc < 0)
        chip->hot_bat_decidegc = -EINVAL;

    rc = of_property_read_u32(node, "qcom,warm-bat-decidegc",
                        &chip->warm_bat_decidegc);

    rc |= of_property_read_u32(node, "qcom,cool-bat-decidegc",
                        &chip->cool_bat_decidegc);

    if (!rc) {
        rc = of_property_read_u32(node, "qcom,cool-bat-mv",
                        &chip->cool_bat_mv);

        rc |= of_property_read_u32(node, "qcom,warm-bat-mv",
                        &chip->warm_bat_mv);

        rc |= of_property_read_u32(node, "qcom,cool-bat-ma",
                        &chip->cool_bat_ma);

        rc |= of_property_read_u32(node, "qcom,warm-bat-ma",
                        &chip->warm_bat_ma);
        if (rc)
            chip->jeita_supported = false;
        else
            chip->jeita_supported = true;
    }

    pr_debug("jeita_supported = %d", chip->jeita_supported);

    rc = of_property_read_u32(node, "qcom,bat-present-decidegc",
                        &batt_present_degree_negative);
    if (rc < 0)
        chip->bat_present_decidegc = -EINVAL;
    else
        chip->bat_present_decidegc = -batt_present_degree_negative;

    if (of_get_property(node, "qcom,vcc-i2c-supply", NULL)) {
        chip->vcc_i2c = devm_regulator_get(chip->dev, "vcc-i2c");
        if (IS_ERR(chip->vcc_i2c)) {
            dev_err(chip->dev,
                "%s: Failed to get vcc_i2c regulator\n",
                                __func__);
            return PTR_ERR(chip->vcc_i2c);
        }
    }

    chip->skip_usb_suspend_for_fake_battery = of_property_read_bool(node,
                "qcom,skip-usb-suspend-for-fake-battery");
    if (!chip->skip_usb_suspend_for_fake_battery) {
        if (!chip->vadc_dev) {
            dev_err(chip->dev,
                "VADC device not present with usb suspend on fake battery\n");
            return -EINVAL;
        }

        rc = smb_parse_batt_id(chip);
        if (rc) {
            dev_err(chip->dev,
                "failed to read batt-id rc=%d\n", rc);
            return rc;
        }
    }

    pr_debug("inhibit-disabled = %d, recharge-disabled = %d, recharge-mv = %d,",
        chip->inhibit_disabled, chip->recharge_disabled,
                        chip->recharge_mv);
    pr_debug("vfloat-mv = %d, iterm-disabled = %d,",
            chip->vfloat_mv, chip->iterm_disabled);
    pr_debug("fastchg-current = %d, charging-disabled = %d,",
            chip->fastchg_current_max_ma,
                    chip->charging_disabled);
    pr_debug("disable-apsd = %d bms = %s cold-bat-degree = %d,",
        chip->disable_apsd, chip->bms_psy_name,
                    chip->cold_bat_decidegc);
    pr_debug("hot-bat-degree = %d, bat-present-decidegc = %d\n",
        chip->hot_bat_decidegc, chip->bat_present_decidegc);
    return 0;
}

static int determine_initial_state(struct smb358_charger *chip)
{
    int rc;
    u8 reg = 0;

    rc = smb358_read_reg(chip, IRQ_B_REG, ®);
    if (rc) {
        dev_err(chip->dev, "Couldn't read IRQ_B rc = %d\n", rc);
        goto fail_init_status;
    }

    rc = smb358_read_reg(chip, IRQ_C_REG, ®);
    if (rc) {
        dev_err(chip->dev, "Couldn't read IRQ_C rc = %d\n", rc);
        goto fail_init_status;
    }
    chip->batt_full = (reg & IRQ_C_TERM_BIT) ? true : false;

    rc = smb358_read_reg(chip, IRQ_A_REG, ®);
    if (rc < 0) {
        dev_err(chip->dev, "Couldn't read irq A rc = %d\n", rc);
        return rc;
    }

#if 0
    /* For current design, can ignore this */
    if (reg & IRQ_A_HOT_HARD_BIT)
        chip->batt_hot = true;
    if (reg & IRQ_A_COLD_HARD_BIT)
        chip->batt_cold = true;
    if (reg & IRQ_A_HOT_SOFT_BIT)
        chip->batt_warm = true;
    if (reg & IRQ_A_COLD_SOFT_BIT)
        chip->batt_cool = true;
#endif

    rc = smb358_read_reg(chip, IRQ_E_REG, ®);
    if (rc) {
        dev_err(chip->dev, "Couldn't read IRQ_E rc = %d\n", rc);
        goto fail_init_status;
    }

    if (reg & IRQ_E_INPUT_UV_BIT) {
        chg_uv(chip, 1);
    } else {
        chg_uv(chip, 0);
        apsd_complete(chip, 1);
    }

    return 0;

fail_init_status:
    dev_err(chip->dev, "Couldn't determine initial status\n");
    return rc;
}

#if defined(CONFIG_DEBUG_FS)
static void smb358_debugfs_init(struct smb358_charger *chip)
{
    int rc;
    chip->debug_root = debugfs_create_dir("smb358", NULL);
    if (!chip->debug_root)
        dev_err(chip->dev, "Couldn't create debug dir\n");

    if (chip->debug_root) {
        struct dentry *ent;

        ent = debugfs_create_file("config_registers", S_IFREG | S_IRUGO,
                      chip->debug_root, chip,
                      &cnfg_debugfs_ops);
        if (!ent || IS_ERR(ent)) {
            rc = PTR_ERR(ent);
            dev_err(chip->dev,
                "Couldn't create cnfg debug file rc = %d\n",
                rc);
        }

        ent = debugfs_create_file("status_registers", S_IFREG | S_IRUGO,
                      chip->debug_root, chip,
                      &status_debugfs_ops);
        if (!ent || IS_ERR(ent)) {
            rc = PTR_ERR(ent);
            dev_err(chip->dev,
                "Couldn't create status debug file rc = %d\n",
                rc);
        }

        ent = debugfs_create_file("cmd_registers", S_IFREG | S_IRUGO,
                      chip->debug_root, chip,
                      &cmd_debugfs_ops);
        if (!ent || IS_ERR(ent)) {
            rc = PTR_ERR(ent);
            dev_err(chip->dev,
                "Couldn't create cmd debug file rc = %d\n",
                rc);
        }

        ent = debugfs_create_x32("address", S_IFREG | S_IWUSR | S_IRUGO,
                      chip->debug_root,
                      &(chip->peek_poke_address));
        if (!ent || IS_ERR(ent)) {
            rc = PTR_ERR(ent);
            dev_err(chip->dev,
                "Couldn't create address debug file rc = %d\n",
                rc);
        }

        ent = debugfs_create_file("data", S_IFREG | S_IWUSR | S_IRUGO,
                      chip->debug_root, chip,
                      &poke_poke_debug_ops);
        if (!ent || IS_ERR(ent)) {
            rc = PTR_ERR(ent);
            dev_err(chip->dev,
                "Couldn't create data debug file rc = %d\n",
                rc);
        }

        ent = debugfs_create_file("force_irq",
                      S_IFREG | S_IWUSR | S_IRUGO,
                      chip->debug_root, chip,
                      &force_irq_ops);
        if (!ent || IS_ERR(ent)) {
            rc = PTR_ERR(ent);
            dev_err(chip->dev,
                "Couldn't create force_irq debug file rc =%d\n",
                rc);
        }

        ent = debugfs_create_file("irq_count", S_IFREG | S_IRUGO,
                      chip->debug_root, chip,
                      &irq_count_debugfs_ops);
        if (!ent || IS_ERR(ent)) {
            rc = PTR_ERR(ent);
            dev_err(chip->dev,
                "Couldn't create cnfg irq_count file rc = %d\n",
                rc);
        }
    }
}
#else
static void smb358_debugfs_init(struct smb358_charger *chip)
{
}
#endif

#define SMB_I2C_VTG_MIN_UV 1800000
#define SMB_I2C_VTG_MAX_UV 1800000
static int smb358_charger_probe(struct i2c_client *client,
                const struct i2c_device_id *id)
{
    int rc, irq;
    struct smb358_charger *chip;
    struct power_supply *usb_psy;
    u8 reg = 0;

    usb_psy = power_supply_get_by_name("usb");
    if (!usb_psy) {
        dev_dbg(&client->dev, "USB psy not found; deferring probe\n");
        return -EPROBE_DEFER;
    }

    chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
    if (!chip) {
        dev_err(&client->dev, "Couldn't allocate memory\n");
        return -ENOMEM;
    }

    chip->client = client;
    chip->dev = &client->dev;
    chip->usb_psy = usb_psy;
    chip->fake_battery_soc = -EINVAL;

    if (of_find_property(chip->dev->of_node, "qcom,chg-vadc", NULL)) {
        /* early for VADC get, defer probe if needed */
        chip->vadc_dev = qpnp_get_vadc(chip->dev, "chg");
        if (IS_ERR(chip->vadc_dev)) {
            rc = PTR_ERR(chip->vadc_dev);
            if (rc != -EPROBE_DEFER)
                pr_err("vadc property configured incorrectly\n");
            return rc;
        }
    }

    rc = smb_parse_dt(chip);
    if (rc) {
        dev_err(&client->dev, "Couldn't parse DT nodes rc=%d\n", rc);
        return rc;
    }
    /* i2c pull up regulator configuration */
    if (chip->vcc_i2c) {
        if (regulator_count_voltages(chip->vcc_i2c) > 0) {
            rc = regulator_set_voltage(chip->vcc_i2c,
                SMB_I2C_VTG_MIN_UV, SMB_I2C_VTG_MAX_UV);
            if (rc) {
                dev_err(&client->dev,
                "regulator vcc_i2c set failed, rc = %d\n",
                                rc);
                return rc;
            }
        }

        rc = regulator_enable(chip->vcc_i2c);
        if (rc) {
            dev_err(&client->dev,
                "Regulator vcc_i2c enable failed rc = %d\n",
                                    rc);
            goto err_set_vtg_i2c;
        }
    }

    mutex_init(&chip->irq_complete);
    mutex_init(&chip->read_write_lock);
    mutex_init(&chip->path_suspend_lock);


    //addeb by zhengtianxing     // removed this config, because that make power_supply_register hangup sometime in fllowing. zhangdongyang 20170206
    //power_supply_set_charge_type(chip->usb_psy, POWER_SUPPLY_TYPE_USB);
    //power_supply_set_present(chip->usb_psy, 1);

    /* probe the device to check if its actually connected */
    rc = smb358_read_reg(chip, CHG_OTH_CURRENT_CTRL_REG, ®);
    if (rc) {
        pr_err("Failed to detect SMB358, device absent, rc = %d\n", rc);
        goto err_set_vtg_i2c;
    }

    /* using adc_tm for implementing pmic therm */
    if (chip->using_pmic_therm) {
        chip->adc_tm_dev = qpnp_get_adc_tm(chip->dev, "chg");
        if (IS_ERR(chip->adc_tm_dev)) {
            rc = PTR_ERR(chip->adc_tm_dev);
            if (rc != -EPROBE_DEFER)
                pr_err("adc_tm property missing\n");
            return rc;
        }
    }

    i2c_set_clientdata(client, chip);

    chip->batt_psy.name     = "battery";
    chip->batt_psy.type     = POWER_SUPPLY_TYPE_BATTERY;
    chip->batt_psy.get_property = smb358_battery_get_property;
    chip->batt_psy.set_property = smb358_battery_set_property;
    chip->batt_psy.property_is_writeable =
                    smb358_batt_property_is_writeable;
    chip->batt_psy.properties   = smb358_battery_properties;
    chip->batt_psy.num_properties   = ARRAY_SIZE(smb358_battery_properties);
    chip->batt_psy.external_power_changed = smb358_external_power_changed;
    chip->batt_psy.supplied_to = pm_batt_supplied_to;
    chip->batt_psy.num_supplicants = ARRAY_SIZE(pm_batt_supplied_to);

    chip->resume_completed = true;

    rc = power_supply_register(chip->dev, &chip->batt_psy);
    if (rc < 0) {
        dev_err(&client->dev, "Couldn't register batt psy rc = %d\n",
                rc);
        goto err_set_vtg_i2c;
    }

    dump_regs(chip);

    rc = smb358_regulator_init(chip);
    if  (rc) {
        dev_err(&client->dev,
            "Couldn't initialize smb358 ragulator rc=%d\n", rc);
        goto fail_regulator_register;
    }

    smb358_chg_stat_handler(0, (void *)chip);

    rc = smb358_hw_init(chip);
    if (rc) {
        dev_err(&client->dev,
            "Couldn't intialize hardware rc=%d\n", rc);
        goto fail_smb358_hw_init;
    }

    rc = determine_initial_state(chip);
    if (rc) {
        dev_err(&client->dev,
            "Couldn't determine initial state rc=%d\n", rc);
        goto fail_smb358_hw_init;
    }

    /* We will not use it by default */
    if (gpio_is_valid(chip->chg_valid_gpio)) {
        rc = gpio_request(chip->chg_valid_gpio, "smb358_chg_valid");
        if (rc) {
            dev_err(&client->dev,
                "gpio_request for %d failed rc=%d\n",
                chip->chg_valid_gpio, rc);
            goto fail_chg_valid_irq;
        }
        irq = gpio_to_irq(chip->chg_valid_gpio);
        if (irq < 0) {
            dev_err(&client->dev,
                "Invalid chg_valid irq = %d\n", irq);
            goto fail_chg_valid_irq;
        }
        rc = devm_request_threaded_irq(&client->dev, irq,
                NULL, smb358_chg_valid_handler,
                IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
                "smb358_chg_valid_irq", chip);
        if (rc) {
            dev_err(&client->dev,
                "Failed request_irq irq=%d, gpio=%d rc=%d\n",
                irq, chip->chg_valid_gpio, rc);
            goto fail_chg_valid_irq;
        }
        smb358_chg_valid_handler(irq, chip);
        enable_irq_wake(irq);
    }

    chip->irq_gpio = of_get_named_gpio_flags(chip->dev->of_node,
                "qcom,irq-gpio", 0, NULL);

    /* STAT irq configuration */
    if (gpio_is_valid(chip->irq_gpio)) {
        rc = gpio_request(chip->irq_gpio, "smb358_irq");
        if (rc) {
            dev_err(&client->dev,
                    "irq gpio request failed, rc=%d", rc);
            goto fail_smb358_hw_init;
        }
        rc = gpio_direction_input(chip->irq_gpio);
        if (rc) {
            dev_err(&client->dev,
                    "set_direction for irq gpio failed\n");
            goto fail_irq_gpio;
        }

        irq = gpio_to_irq(chip->irq_gpio);
        if (irq < 0) {
            dev_err(&client->dev,
                "Invalid irq_gpio irq = %d\n", irq);
            goto fail_irq_gpio;
        }
        rc = devm_request_threaded_irq(&client->dev, irq, NULL,
                smb358_chg_stat_handler,
                IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
                "smb358_chg_stat_irq", chip);
        if (rc) {
            dev_err(&client->dev,
                "Failed STAT irq=%d request rc = %d\n",
                irq, rc);
            goto fail_irq_gpio;
        }
        enable_irq_wake(irq);
    } else {
        goto fail_irq_gpio;
    }

    if (chip->using_pmic_therm) {
        if (!chip->jeita_supported) {
            /* add hot/cold temperature monitor */
            chip->adc_param.low_temp = chip->cold_bat_decidegc;
            chip->adc_param.high_temp = chip->hot_bat_decidegc;
        } else {
            chip->adc_param.low_temp = chip->cool_bat_decidegc;
            chip->adc_param.high_temp = chip->warm_bat_decidegc;
        }
        chip->adc_param.timer_interval = ADC_MEAS2_INTERVAL_1S;
        chip->adc_param.state_request = ADC_TM_HIGH_LOW_THR_ENABLE;
        chip->adc_param.btm_ctx = chip;
        chip->adc_param.threshold_notification =
                smb_chg_adc_notification;
#ifdef CONFIG_FOR_BROBDMOBI_R700
        chip->adc_param.channel = P_MUX6_1_1;
#else
        chip->adc_param.channel = LR_MUX1_BATT_THERM;
#endif

        /* update battery missing info in tm_channel_measure*/
        rc = qpnp_adc_tm_channel_measure(chip->adc_tm_dev,
                            &chip->adc_param);
        if (rc)
            pr_err("requesting ADC error %d\n", rc);
    }

    smb358_debugfs_init(chip);

    dump_regs(chip);

    dev_info(chip->dev, "SMB358 successfully probed. charger=%d, batt=%d\n",
            chip->chg_present, smb358_get_prop_batt_present(chip));
    return 0;

fail_chg_valid_irq:
    if (gpio_is_valid(chip->chg_valid_gpio))
        gpio_free(chip->chg_valid_gpio);
fail_irq_gpio:
    if (gpio_is_valid(chip->irq_gpio))
        gpio_free(chip->irq_gpio);
fail_smb358_hw_init:
    regulator_unregister(chip->otg_vreg.rdev);
fail_regulator_register:
    power_supply_unregister(&chip->batt_psy);
err_set_vtg_i2c:
    if (chip->vcc_i2c)
        if (regulator_count_voltages(chip->vcc_i2c) > 0)
            regulator_set_voltage(chip->vcc_i2c, 0,
                        SMB_I2C_VTG_MAX_UV);
    return rc;
}

static int smb358_charger_remove(struct i2c_client *client)
{
    struct smb358_charger *chip = i2c_get_clientdata(client);

    power_supply_unregister(&chip->batt_psy);
    if (gpio_is_valid(chip->chg_valid_gpio))
        gpio_free(chip->chg_valid_gpio);

    if (chip->vcc_i2c)
        regulator_disable(chip->vcc_i2c);

    mutex_destroy(&chip->irq_complete);
    debugfs_remove_recursive(chip->debug_root);
    return 0;
}

static int smb358_suspend(struct device *dev)
{
    struct i2c_client *client = to_i2c_client(dev);
    struct smb358_charger *chip = i2c_get_clientdata(client);
    int rc;
    int i;

    for (i = 0; i < 2; i++) {
        rc = smb358_read_reg(chip, FAULT_INT_REG + i,
                    &chip->irq_cfg_mask[i]);
        if (rc)
            dev_err(chip->dev,
                "Couldn't save irq cfg regs rc = %d\n", rc);
    }

    /* enable wake up IRQs */
    rc = smb358_write_reg(chip, FAULT_INT_REG,
            FAULT_INT_HOT_COLD_HARD_BIT | FAULT_INT_INPUT_UV_BIT);
    if (rc < 0)
        dev_err(chip->dev, "Couldn't set fault_irq_cfg rc = %d\n", rc);

    rc = smb358_write_reg(chip, STATUS_INT_REG,
            STATUS_INT_LOW_BATT_BIT | STATUS_INT_MISSING_BATT_BIT |
            STATUS_INT_CHGING_BIT | STATUS_INT_INOK_BIT |
            STATUS_INT_OTG_DETECT_BIT | STATUS_INT_CHG_INHI_BIT);
    if (rc < 0)
        dev_err(chip->dev,
            "Couldn't set status_irq_cfg rc = %d\n", rc);

    mutex_lock(&chip->irq_complete);
    if (chip->vcc_i2c) {
        rc = regulator_disable(chip->vcc_i2c);
        if (rc) {
            dev_err(chip->dev,
                "Regulator vcc_i2c disable failed rc=%d\n", rc);
            mutex_unlock(&chip->irq_complete);
            return rc;
        }
    }

    chip->resume_completed = false;
    mutex_unlock(&chip->irq_complete);
    return 0;
}

static int smb358_suspend_noirq(struct device *dev)
{
    struct i2c_client *client = to_i2c_client(dev);
    struct smb358_charger *chip = i2c_get_clientdata(client);

    if (chip->irq_waiting) {
        pr_err_ratelimited("Aborting suspend, an interrupt was detected while suspending\n");
        return -EBUSY;
    }
    return 0;
}

static int smb358_resume(struct device *dev)
{
    struct i2c_client *client = to_i2c_client(dev);
    struct smb358_charger *chip = i2c_get_clientdata(client);
    int rc;
    int i;

    if (chip->vcc_i2c) {
        rc = regulator_enable(chip->vcc_i2c);
        if (rc) {
            dev_err(chip->dev,
                "Regulator vcc_i2c enable failed rc=%d\n", rc);
            return rc;
        }
    }
    /* Restore IRQ config */
    for (i = 0; i < 2; i++) {
        rc = smb358_write_reg(chip, FAULT_INT_REG + i,
                    chip->irq_cfg_mask[i]);
        if (rc)
            dev_err(chip->dev,
                "Couldn't restore irq cfg regs rc=%d\n", rc);
    }

    mutex_lock(&chip->irq_complete);
    chip->resume_completed = true;
    mutex_unlock(&chip->irq_complete);
    if (chip->irq_waiting) {
        smb358_chg_stat_handler(client->irq, chip);
        enable_irq(client->irq);
    }
    return 0;
}

static const struct dev_pm_ops smb358_pm_ops = {
    .suspend    = smb358_suspend,
    .suspend_noirq  = smb358_suspend_noirq,
    .resume     = smb358_resume,
};

static struct of_device_id smb358_match_table[] = {
    { .compatible = "qcom,smb358-charger",},
    { },
};

static const struct i2c_device_id smb358_charger_id[] = {
    {"smb358-charger", 0},
    {},
};
MODULE_DEVICE_TABLE(i2c, smb358_charger_id);

static struct i2c_driver smb358_charger_driver = {
    .driver     = {
        .name       = "smb358-charger",
        .owner      = THIS_MODULE,
        .of_match_table = smb358_match_table,
        .pm     = &smb358_pm_ops,
    },
    .probe      = smb358_charger_probe,
    .remove     = smb358_charger_remove,
    .id_table   = smb358_charger_id,
};

module_i2c_driver(smb358_charger_driver);

MODULE_DESCRIPTION("SMB358 Charger");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("i2c:smb358-charger");

剑气纵横三万里

“为什么要努力?” “想去的地方很远,想要的东西很贵,喜欢的人很优秀,父母的白发,朋友的约定,周围人的嘲笑,以及,天生傲骨。”

留下你的评论

*评论支持代码高亮<pre class="prettyprint linenums">代码</pre>

相关推荐

暂无内容!