// SPDX-License-Identifier: GPL-2.0 /* * Chrager driver for Sgm4154x * * Copyright (c) 2022 Rockchip Electronics Co., Ltd. * * Author: Xu Shengfei */ #include #include #include #include #include #include #include #include #include #include static int dbg_enable; module_param_named(dbg_level, dbg_enable, int, 0644); #define DBG(args...) \ do { \ if (dbg_enable) { \ pr_info(args); \ } \ } while (0) #define SGM4154x_MANUFACTURER "SGMICRO" #define SGM4154x_NAME "sgm41542" #define SGM4154x_PN_ID (BIT(6) | BIT(5) | BIT(3)) /* define register */ #define SGM4154x_CHRG_CTRL_0 0x00 #define SGM4154x_CHRG_CTRL_1 0x01 #define SGM4154x_CHRG_CTRL_2 0x02 #define SGM4154x_CHRG_CTRL_3 0x03 #define SGM4154x_CHRG_CTRL_4 0x04 #define SGM4154x_CHRG_CTRL_5 0x05 #define SGM4154x_CHRG_CTRL_6 0x06 #define SGM4154x_CHRG_CTRL_7 0x07 #define SGM4154x_CHRG_STAT 0x08 #define SGM4154x_CHRG_FAULT 0x09 #define SGM4154x_CHRG_CTRL_a 0x0a #define SGM4154x_CHRG_CTRL_b 0x0b #define SGM4154x_CHRG_CTRL_c 0x0c #define SGM4154x_CHRG_CTRL_d 0x0d #define SGM4154x_INPUT_DET 0x0e #define SGM4154x_CHRG_CTRL_f 0x0f /* charge status flags */ #define SGM4154x_CHRG_EN BIT(4) #define SGM4154x_HIZ_EN BIT(7) #define SGM4154x_TERM_EN BIT(7) #define SGM4154x_VAC_OVP_MASK GENMASK(7, 6) #define SGM4154x_DPDM_ONGOING BIT(7) #define SGM4154x_VBUS_GOOD BIT(7) #define SGM4154x_BOOSTV GENMASK(5, 4) #define SGM4154x_BOOST_LIM BIT(7) #define SGM4154x_OTG_EN BIT(5) /* Part ID */ #define SGM4154x_PN_MASK GENMASK(6, 3) /* WDT TIMER SET */ #define SGM4154x_WDT_TIMER_MASK GENMASK(5, 4) #define SGM4154x_WDT_TIMER_DISABLE 0 #define SGM4154x_WDT_TIMER_40S BIT(4) #define SGM4154x_WDT_TIMER_80S BIT(5) #define SGM4154x_WDT_TIMER_160S (BIT(4) | BIT(5)) #define SGM4154x_WDT_RST_MASK BIT(6) #define SGM4154x_WDT_RST BIT(6) /* SAFETY TIMER SET */ #define SGM4154x_SAFETY_TIMER_MASK GENMASK(3, 3) #define SGM4154x_SAFETY_TIMER_DISABLE 0 #define SGM4154x_SAFETY_TIMER_EN BIT(3) #define SGM4154x_SAFETY_TIMER_5H 0 #define SGM4154x_SAFETY_TIMER_10H BIT(2) /* recharge voltage */ #define SGM4154x_VRECHARGE BIT(0) #define SGM4154x_VRECHRG_STEP_mV 100 #define SGM4154x_VRECHRG_OFFSET_mV 100 /* charge status */ #define SGM4154x_VSYS_STAT BIT(0) #define SGM4154x_THERM_STAT BIT(1) #define SGM4154x_PG_STAT BIT(2) #define SGM4154x_CHG_STAT_MASK GENMASK(4, 3) #define SGM4154x_PRECHRG BIT(3) #define SGM4154x_FAST_CHRG BIT(4) #define SGM4154x_TERM_CHRG (BIT(3) | BIT(4)) /* charge type */ #define SGM4154x_VBUS_STAT_MASK GENMASK(7, 5) #define SGM4154x_NOT_CHRGING 0 #define SGM4154x_USB_SDP BIT(5) #define SGM4154x_USB_CDP BIT(6) #define SGM4154x_USB_DCP (BIT(5) | BIT(6)) #define SGM4154x_UNKNOWN (BIT(7) | BIT(5)) #define SGM4154x_NON_STANDARD (BIT(7) | BIT(6)) #define SGM4154x_OTG_MODE (BIT(7) | BIT(6) | BIT(5)) /* TEMP Status */ #define SGM4154x_TEMP_MASK GENMASK(2, 0) #define SGM4154x_TEMP_NORMAL BIT(0) #define SGM4154x_TEMP_WARM BIT(1) #define SGM4154x_TEMP_COOL (BIT(0) | BIT(1)) #define SGM4154x_TEMP_COLD (BIT(0) | BIT(3)) #define SGM4154x_TEMP_HOT (BIT(2) | BIT(3)) /* precharge current */ #define SGM4154x_PRECHRG_CUR_MASK GENMASK(7, 4) #define SGM4154x_PRECHRG_CURRENT_STEP_uA 60000 #define SGM4154x_PRECHRG_I_MIN_uA 60000 #define SGM4154x_PRECHRG_I_MAX_uA 780000 #define SGM4154x_PRECHRG_I_DEF_uA 180000 /* termination current */ #define SGM4154x_TERMCHRG_CUR_MASK GENMASK(3, 0) #define SGM4154x_TERMCHRG_CURRENT_STEP_uA 60000 #define SGM4154x_TERMCHRG_I_MIN_uA 60000 #define SGM4154x_TERMCHRG_I_MAX_uA 960000 #define SGM4154x_TERMCHRG_I_DEF_uA 180000 /* charge current */ #define SGM4154x_ICHRG_CUR_MASK GENMASK(5, 0) #define SGM4154x_ICHRG_I_STEP_uA 60000 #define SGM4154x_ICHRG_I_MIN_uA 0 #define SGM4154x_ICHRG_I_MAX_uA 3000000 #define SGM4154x_ICHRG_I_DEF_uA 2040000 /* charge voltage */ #define SGM4154x_VREG_V_MASK GENMASK(7, 3) #define SGM4154x_VREG_V_MAX_uV 4624000 #define SGM4154x_VREG_V_MIN_uV 3856000 #define SGM4154x_VREG_V_DEF_uV 4208000 #define SGM4154x_VREG_V_STEP_uV 32000 /* VREG Fine Tuning */ #define SGM4154x_VREG_FT_MASK GENMASK(7, 6) #define SGM4154x_VREG_FT_UP_8mV BIT(6) #define SGM4154x_VREG_FT_DN_8mV BIT(7) #define SGM4154x_VREG_FT_DN_16mV (BIT(7) | BIT(6)) /* iindpm current */ #define SGM4154x_IINDPM_I_MASK GENMASK(4, 0) #define SGM4154x_IINDPM_I_MIN_uA 100000 #define SGM4154x_IINDPM_I_MAX_uA 3800000 #define SGM4154x_IINDPM_STEP_uA 100000 #define SGM4154x_IINDPM_DEF_uA 2400000 #define SGM4154x_VINDPM_INT_MASK BIT(1) #define SGM4154x_VINDPM_INT_DIS BIT(1) #define SGM4154x_IINDPM_INT_MASK BIT(0) #define SGM4154x_IINDPM_INT_DIS BIT(0) /* vindpm voltage */ #define SGM4154x_VINDPM_V_MASK GENMASK(3, 0) #define SGM4154x_VINDPM_V_MIN_uV 3900000 #define SGM4154x_VINDPM_V_MAX_uV 12000000 #define SGM4154x_VINDPM_STEP_uV 100000 #define SGM4154x_VINDPM_DEF_uV 4500000 #define SGM4154x_VINDPM_OS_MASK GENMASK(1, 0) /* DP DM SEL */ #define SGM4154x_DP_VSEL_MASK GENMASK(4, 3) #define SGM4154x_DM_VSEL_MASK GENMASK(2, 1) /* PUMPX SET */ #define SGM4154x_EN_PUMPX BIT(7) #define SGM4154x_PUMPX_UP BIT(6) #define SGM4154x_PUMPX_DN BIT(5) #define SGM4154x_BATFET_DIS_MSK BIT(5) #define SGM4154x_BATFET_DIS_DIS BIT(5) #define SGM4154x_BATFET_DLY_MSK BIT(3) #define SGM4154x_BATFET_DLY_EN BIT(3) #define DEFAULT_INPUT_CURRENT (500 * 1000) struct sgm4154x_init_data { int ichg; /* charge current */ int ilim; /* input current */ int vreg; /* regulation voltage */ int iterm; /* termination current */ int iprechg; /* precharge current */ int vlim; /* minimum system voltage limit */ int max_ichg; int max_vreg; }; struct sgm4154x_state { bool vsys_stat; bool therm_stat; bool online; u8 chrg_stat; u8 vbus_status; bool chrg_en; bool hiz_en; bool term_en; bool vbus_gd; u8 chrg_type; u8 health; u8 chrg_fault; u8 ntc_fault; }; struct sgm4154x_device { struct i2c_client *client; struct device *dev; struct power_supply *charger; struct mutex lock; struct mutex i2c_rw_lock; struct regmap *regmap; char model_name[I2C_NAME_SIZE]; int device_id; struct sgm4154x_init_data init_data; struct sgm4154x_state state; struct regulator_dev *otg_rdev; struct notifier_block pm_nb; int input_current; bool sgm4154x_suspend_flag; bool watchdog_enable; struct workqueue_struct *sgm_monitor_wq; struct delayed_work sgm_delay_work; }; /* SGM4154x REG06 BOOST_LIM[5:4], uV */ static const unsigned int BOOST_VOLT_LIMIT[] = { 4850000, 5000000, 5150000, 5300000 }; static const unsigned int BOOST_CURRENT_LIMIT[] = { 1200000, 2000000 }; enum SGM4154x_VINDPM_OS { VINDPM_OS_3900mV, VINDPM_OS_5900mV, VINDPM_OS_7500mV, VINDPM_OS_10500mV, }; static int sgm4154x_set_term_curr(struct sgm4154x_device *sgm, int uA) { int reg_val; int ret; if (uA < SGM4154x_TERMCHRG_I_MIN_uA) uA = SGM4154x_TERMCHRG_I_MIN_uA; else if (uA > SGM4154x_TERMCHRG_I_MAX_uA) uA = SGM4154x_TERMCHRG_I_MAX_uA; reg_val = (uA - SGM4154x_TERMCHRG_I_MIN_uA) / SGM4154x_TERMCHRG_CURRENT_STEP_uA; ret = regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_3, SGM4154x_TERMCHRG_CUR_MASK, reg_val); if (ret) dev_err(sgm->dev, "set term current error!\n"); return ret; } static int sgm4154x_set_prechrg_curr(struct sgm4154x_device *sgm, int uA) { int reg_val; int ret; if (uA < SGM4154x_PRECHRG_I_MIN_uA) uA = SGM4154x_PRECHRG_I_MIN_uA; else if (uA > SGM4154x_PRECHRG_I_MAX_uA) uA = SGM4154x_PRECHRG_I_MAX_uA; reg_val = (uA - SGM4154x_PRECHRG_I_MIN_uA) / SGM4154x_PRECHRG_CURRENT_STEP_uA; reg_val = reg_val << 4; ret = regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_3, SGM4154x_PRECHRG_CUR_MASK, reg_val); if (ret) dev_err(sgm->dev, "set precharge current error!\n"); return ret; } static int sgm4154x_set_ichrg_curr(struct sgm4154x_device *sgm, int uA) { int reg_val; int ret; if (uA < SGM4154x_ICHRG_I_MIN_uA) uA = SGM4154x_ICHRG_I_MIN_uA; else { if ((sgm->init_data.max_ichg > 0) && (uA > sgm->init_data.max_ichg)) uA = sgm->init_data.max_ichg; uA = min(uA, SGM4154x_ICHRG_I_MAX_uA); } reg_val = uA / SGM4154x_ICHRG_I_STEP_uA; ret = regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_2, SGM4154x_ICHRG_CUR_MASK, reg_val); if (ret) dev_err(sgm->dev, "set icharge current error!\n"); return ret; } static int sgm4154x_set_chrg_volt(struct sgm4154x_device *sgm, int chrg_volt) { int reg_val; int ret; if (chrg_volt < SGM4154x_VREG_V_MIN_uV) chrg_volt = SGM4154x_VREG_V_MIN_uV; else { if ((sgm->init_data.max_vreg > 0) && (chrg_volt > sgm->init_data.max_vreg)) chrg_volt = sgm->init_data.max_vreg; chrg_volt = min(chrg_volt, SGM4154x_VREG_V_MAX_uV); } reg_val = (chrg_volt - SGM4154x_VREG_V_MIN_uV) / SGM4154x_VREG_V_STEP_uV; if (chrg_volt == 4200 * 1000) reg_val = 0x0b; reg_val = reg_val << 3; ret = regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_4, SGM4154x_VREG_V_MASK, reg_val); if (ret) dev_err(sgm->dev, "set charge voltage error!\n"); return ret; } static int sgm4154x_set_vindpm_offset_os(struct sgm4154x_device *sgm, enum SGM4154x_VINDPM_OS offset_os) { int ret; ret = regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_f, SGM4154x_VINDPM_OS_MASK, offset_os); if (ret) dev_err(sgm->dev, "set vindpm offset os error!\n"); return ret; } static int sgm4154x_set_input_volt_lim(struct sgm4154x_device *sgm, unsigned int vindpm) { enum SGM4154x_VINDPM_OS os_val; unsigned int offset; u8 reg_val; int ret; if (vindpm < SGM4154x_VINDPM_V_MIN_uV || vindpm > SGM4154x_VINDPM_V_MAX_uV) return -EINVAL; if (vindpm < 5900000) { os_val = VINDPM_OS_3900mV; offset = 3900000; } else if (vindpm >= 5900000 && vindpm < 7500000) { os_val = VINDPM_OS_5900mV; offset = 5900000; } else if (vindpm >= 7500000 && vindpm < 10500000) { os_val = VINDPM_OS_7500mV; offset = 7500000; } else { os_val = VINDPM_OS_10500mV; offset = 10500000; } ret = sgm4154x_set_vindpm_offset_os(sgm, os_val); if (ret) { dev_err(sgm->dev, "set vin dpm error!\n"); return ret; } reg_val = (vindpm - offset) / SGM4154x_VINDPM_STEP_uV; ret = regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_6, SGM4154x_VINDPM_V_MASK, reg_val); if (ret) { dev_err(sgm->dev, "input voltage error!\n"); return ret; } return ret; } static int sgm4154x_set_input_curr_lim(struct sgm4154x_device *sgm, int iindpm) { int reg_val; int ret; if (iindpm < SGM4154x_IINDPM_I_MIN_uA) return -EINVAL; if ((iindpm > SGM4154x_IINDPM_I_MAX_uA) || (iindpm > sgm->init_data.ilim)) iindpm = min(SGM4154x_IINDPM_I_MAX_uA, sgm->init_data.ilim); sgm->input_current = iindpm; reg_val = (iindpm-SGM4154x_IINDPM_I_MIN_uA) / SGM4154x_IINDPM_STEP_uA; ret = regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_0, SGM4154x_IINDPM_I_MASK, reg_val); if (ret) dev_err(sgm->dev, "set input current limit error!\n"); return ret; } static int sgm4154x_get_input_curr_lim(struct sgm4154x_device *sgm) { int ret; int ilim; ret = regmap_read(sgm->regmap, SGM4154x_CHRG_CTRL_0, &ilim); if (ret) { dev_err(sgm->dev, "get input current limit error!\n"); return ret; } if (SGM4154x_IINDPM_I_MASK == (ilim & SGM4154x_IINDPM_I_MASK)) return SGM4154x_IINDPM_I_MAX_uA; ilim = (ilim & SGM4154x_IINDPM_I_MASK) * SGM4154x_IINDPM_STEP_uA + SGM4154x_IINDPM_I_MIN_uA; return ilim; } static int sgm4154x_watchdog_timer_reset(struct sgm4154x_device *sgm) { int ret; ret = regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_1, SGM4154x_WDT_RST_MASK, SGM4154x_WDT_RST); if (ret) dev_err(sgm->dev, "set watchdog timer error!\n"); return ret; } static int sgm4154x_set_watchdog_timer(struct sgm4154x_device *sgm, int time) { u8 reg_val; int ret; if (time == 0) reg_val = SGM4154x_WDT_TIMER_DISABLE; else if (time == 40) reg_val = SGM4154x_WDT_TIMER_40S; else if (time == 80) reg_val = SGM4154x_WDT_TIMER_80S; else reg_val = SGM4154x_WDT_TIMER_160S; ret = regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_5, SGM4154x_WDT_TIMER_MASK, reg_val); if (ret) { dev_err(sgm->dev, "set watchdog timer error!\n"); return ret; } if (time) { DBG("sgm41542: enable watchdog\n"); if (!sgm->watchdog_enable) queue_delayed_work(sgm->sgm_monitor_wq, &sgm->sgm_delay_work, msecs_to_jiffies(1000 * 5)); sgm->watchdog_enable = true; } else { DBG("sgm41542: disable watchdog\n"); sgm->watchdog_enable = false; sgm4154x_watchdog_timer_reset(sgm); } return ret; } static int sgm4154x_enable_charger(struct sgm4154x_device *sgm) { int ret; ret = regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_1, SGM4154x_CHRG_EN, SGM4154x_CHRG_EN); if (ret) dev_err(sgm->dev, "enable charger error!\n"); return ret; } static int sgm4154x_disable_charger(struct sgm4154x_device *sgm) { int ret; ret = regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_1, SGM4154x_CHRG_EN, 0); if (ret) dev_err(sgm->dev, "disable charger error!\n"); return ret; } static int sgm4154x_set_vac_ovp(struct sgm4154x_device *sgm) { int reg_val; int ret; reg_val = 0xFF & SGM4154x_VAC_OVP_MASK; ret = regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_6, SGM4154x_VAC_OVP_MASK, reg_val); if (ret) dev_err(sgm->dev, "set vac ovp error!\n"); return ret; } static int sgm4154x_set_recharge_volt(struct sgm4154x_device *sgm, int recharge_volt) { int reg_val; int ret; reg_val = (recharge_volt - SGM4154x_VRECHRG_OFFSET_mV) / SGM4154x_VRECHRG_STEP_mV; ret = regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_4, SGM4154x_VRECHARGE, reg_val); if (ret) dev_err(sgm->dev, "set recharger error!\n"); return ret; } static int sgm4154x_get_state(struct sgm4154x_device *sgm, struct sgm4154x_state *state) { int chrg_param_0, chrg_param_1, chrg_param_2; int chrg_stat; int fault; int ret; ret = regmap_read(sgm->regmap, SGM4154x_CHRG_STAT, &chrg_stat); if (ret) { pr_err("read SGM4154x_CHRG_STAT fail\n"); return ret; } DBG("SGM4154x_CHRG_STAT[0x%x]: 0x%x\n", SGM4154x_CHRG_STAT, chrg_stat); state->chrg_type = chrg_stat & SGM4154x_VBUS_STAT_MASK; state->chrg_stat = chrg_stat & SGM4154x_CHG_STAT_MASK; state->online = !!(chrg_stat & SGM4154x_PG_STAT); state->therm_stat = !!(chrg_stat & SGM4154x_THERM_STAT); state->vsys_stat = !!(chrg_stat & SGM4154x_VSYS_STAT); ret = regmap_read(sgm->regmap, SGM4154x_CHRG_FAULT, &fault); if (ret) { pr_err("read SGM4154x_CHRG_FAULT fail\n"); return ret; } DBG("SGM4154x_CHRG_FAULT[0x%x]: 0x%x\n", SGM4154x_CHRG_FAULT, fault); state->chrg_fault = fault; state->ntc_fault = fault & SGM4154x_TEMP_MASK; state->health = state->ntc_fault; ret = regmap_read(sgm->regmap, SGM4154x_CHRG_CTRL_0, &chrg_param_0); if (ret) { pr_err("read SGM4154x_CHRG_CTRL_0 fail\n"); return ret; } state->hiz_en = !!(chrg_param_0 & SGM4154x_HIZ_EN); DBG("SGM4154x_CHRG_CTRL_0[0x%x]: 0x%x\n", SGM4154x_CHRG_CTRL_0, chrg_param_0); ret = regmap_read(sgm->regmap, SGM4154x_CHRG_CTRL_5, &chrg_param_1); if (ret) { pr_err("read SGM4154x_CHRG_CTRL_5 fail\n"); return ret; } state->term_en = !!(chrg_param_1 & SGM4154x_TERM_EN); DBG("SGM4154x_CHRG_CTRL_5[0x%x]: 0x%x\n", SGM4154x_CHRG_CTRL_5, chrg_param_1); ret = regmap_read(sgm->regmap, SGM4154x_CHRG_CTRL_a, &chrg_param_2); if (ret) { pr_err("read SGM4154x_CHRG_CTRL_a fail\n"); return ret; } state->vbus_gd = !!(chrg_param_2 & SGM4154x_VBUS_GOOD); DBG("SGM4154x_CHRG_CTRL_a[0x%x]: 0x%x\n", SGM4154x_CHRG_CTRL_a, chrg_param_2); DBG("chrg_type: 0x%x\n", state->chrg_type); DBG("chrg_stat: 0x%x\n", state->chrg_stat); DBG("online: 0x%x\n", state->online); DBG("therm_stat: 0x%x\n", state->therm_stat); DBG("vsys_stat: 0x%x\n", state->vsys_stat); DBG("chrg_fault: 0x%x\n", state->chrg_fault); DBG("ntc_fault: 0x%x\n", state->ntc_fault); DBG("health: 0x%x\n", state->health); DBG("hiz_en: 0x%x\n", state->hiz_en); DBG("term_en: 0x%x\n", state->term_en); DBG("vbus_gd: 0x%x\n", state->vbus_gd); return ret; } static int sgm4154x_property_is_writeable(struct power_supply *psy, enum power_supply_property prop) { switch (prop) { case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: case POWER_SUPPLY_PROP_ONLINE: return true; default: return false; } } static int sgm4154x_charger_set_property(struct power_supply *psy, enum power_supply_property prop, const union power_supply_propval *val) { struct sgm4154x_device *sgm = power_supply_get_drvdata(psy); int ret = -EINVAL; switch (prop) { case POWER_SUPPLY_PROP_ONLINE: if (val->intval) { ret = sgm4154x_enable_charger(sgm); sgm4154x_set_watchdog_timer(sgm, SGM4154x_WDT_TIMER_40S); } else { sgm4154x_set_watchdog_timer(sgm, 0); ret = sgm4154x_disable_charger(sgm); } break; case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: ret = sgm4154x_set_input_curr_lim(sgm, val->intval); break; case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: ret = sgm4154x_set_ichrg_curr(sgm, val->intval); break; case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: ret = sgm4154x_set_chrg_volt(sgm, val->intval); break; default: return -EINVAL; } return ret; } static int sgm4154x_charger_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { struct sgm4154x_device *sgm = power_supply_get_drvdata(psy); struct sgm4154x_state state; int ret = 0; mutex_lock(&sgm->lock); ret = sgm4154x_get_state(sgm, &state); if (ret) { dev_err(sgm->dev, "get state error!\n"); mutex_unlock(&sgm->lock); return ret; } sgm->state = state; mutex_unlock(&sgm->lock); switch (psp) { case POWER_SUPPLY_PROP_STATUS: if (!state.chrg_type || (state.chrg_type == SGM4154x_OTG_MODE)) val->intval = POWER_SUPPLY_STATUS_DISCHARGING; else if (!state.chrg_stat) val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; else if (state.chrg_stat == SGM4154x_TERM_CHRG) val->intval = POWER_SUPPLY_STATUS_FULL; else val->intval = POWER_SUPPLY_STATUS_CHARGING; break; case POWER_SUPPLY_PROP_CHARGE_TYPE: switch (state.chrg_stat) { case SGM4154x_PRECHRG: val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; break; case SGM4154x_FAST_CHRG: val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST; break; case SGM4154x_TERM_CHRG: val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; break; case SGM4154x_NOT_CHRGING: val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE; break; default: val->intval = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN; } break; case POWER_SUPPLY_PROP_MANUFACTURER: val->strval = SGM4154x_MANUFACTURER; break; case POWER_SUPPLY_PROP_MODEL_NAME: val->strval = SGM4154x_NAME; break; case POWER_SUPPLY_PROP_ONLINE: val->intval = state.online; break; case POWER_SUPPLY_PROP_PRESENT: val->intval = state.vbus_gd; break; case POWER_SUPPLY_PROP_TYPE: val->intval = POWER_SUPPLY_TYPE_USB; break; case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: val->intval = sgm->init_data.max_vreg; break; case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: val->intval = SGM4154x_ICHRG_I_MAX_uA; break; case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT: val->intval = 12 * 1000 * 1000; break; case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: val->intval = sgm4154x_get_input_curr_lim(sgm); if (val->intval < 0) return -EINVAL; break; default: return -EINVAL; } return ret; } static ssize_t registers_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sgm4154x_device *sgm4154x = dev_get_drvdata(dev); u8 tmpbuf[30]; int idx = 0; u8 addr; int val; int len; int ret; for (addr = 0x0; addr <= SGM4154x_CHRG_CTRL_f; addr++) { ret = regmap_read(sgm4154x->regmap, addr, &val); if (ret == 0) { len = snprintf(tmpbuf, 30, "Reg[%.2X] = 0x%.2x\n", addr, val); memcpy(&buf[idx], tmpbuf, len); idx += len; } } return idx; } static ssize_t registers_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct sgm4154x_device *sgm4154x = dev_get_drvdata(dev); unsigned int reg; int ret; int val; ret = sscanf(buf, "%x %x", ®, &val); if (ret == 2 && reg <= SGM4154x_CHRG_CTRL_f) regmap_write(sgm4154x->regmap, (unsigned char)reg, val); return count; } static DEVICE_ATTR_RW(registers); static void sgm4154x_create_device_node(struct device *dev) { device_create_file(dev, &dev_attr_registers); } static irqreturn_t sgm4154x_irq_handler_thread(int irq, void *private) { struct sgm4154x_device *sgm4154x = private; struct sgm4154x_state state; int ret; u8 addr; int val; for (addr = 0x0; addr <= SGM4154x_CHRG_CTRL_f; addr++) { ret = regmap_read(sgm4154x->regmap, addr, &val); if (ret) dev_err(sgm4154x->dev, "read addr[0x%x] error!\n", addr); DBG("[0x%x]: 0x%x\n", addr, val); } ret = sgm4154x_get_state(sgm4154x, &state); if (ret) { dev_err(sgm4154x->dev, "get state error!\n"); return IRQ_NONE; } sgm4154x->state = state; if (state.vbus_gd) { if (sgm4154x->input_current >= DEFAULT_INPUT_CURRENT) { ret = sgm4154x_set_input_curr_lim(sgm4154x, sgm4154x->input_current); if (ret) { dev_err(sgm4154x->dev, "set input current error!\n"); return IRQ_NONE; } } } power_supply_changed(sgm4154x->charger); return IRQ_HANDLED; } static enum power_supply_property sgm4154x_power_supply_props[] = { POWER_SUPPLY_PROP_TYPE, POWER_SUPPLY_PROP_MANUFACTURER, POWER_SUPPLY_PROP_MODEL_NAME, POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_ONLINE, POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT, POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, POWER_SUPPLY_PROP_CHARGE_TYPE, POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, POWER_SUPPLY_PROP_PRESENT }; static char *sgm4154x_charger_supplied_to[] = { "usb", }; static struct power_supply_desc sgm4154x_power_supply_desc = { .name = "sgm4154x-charger", .type = POWER_SUPPLY_TYPE_USB, .properties = sgm4154x_power_supply_props, .num_properties = ARRAY_SIZE(sgm4154x_power_supply_props), .get_property = sgm4154x_charger_get_property, .set_property = sgm4154x_charger_set_property, .property_is_writeable = sgm4154x_property_is_writeable, }; static bool sgm4154x_is_volatile_reg(struct device *dev, unsigned int reg) { switch (reg) { case SGM4154x_CHRG_CTRL_0 ... SGM4154x_CHRG_CTRL_f: return true; default: return false; } } static const struct regmap_config sgm4154x_regmap_config = { .reg_bits = 8, .val_bits = 8, .max_register = SGM4154x_CHRG_CTRL_f, .cache_type = REGCACHE_RBTREE, .volatile_reg = sgm4154x_is_volatile_reg, }; static int sgm4154x_power_supply_init(struct sgm4154x_device *sgm, struct device *dev) { struct power_supply_config psy_cfg = { .drv_data = sgm, .of_node = dev->of_node, }; psy_cfg.supplied_to = sgm4154x_charger_supplied_to; psy_cfg.num_supplicants = ARRAY_SIZE(sgm4154x_charger_supplied_to); psy_cfg.of_node = dev->of_node; sgm->charger = devm_power_supply_register(sgm->dev, &sgm4154x_power_supply_desc, &psy_cfg); if (IS_ERR(sgm->charger)) return -EINVAL; return 0; } static int sgm4154x_hw_init(struct sgm4154x_device *sgm) { struct power_supply_battery_info *bat_info; int chrg_stat, ret = 0; ret = power_supply_get_battery_info(sgm->charger, &bat_info); if (ret) { /* Allocate an empty battery */ bat_info = devm_kzalloc(sgm->dev, sizeof(*bat_info), GFP_KERNEL); if (!bat_info) return -ENOMEM; pr_info("sgm4154x: no battery information is supplied\n"); /* * If no battery information is supplied, we should set * default charge termination current to 120 mA, and default * charge termination voltage to 4.35V. */ bat_info->constant_charge_current_max_ua = SGM4154x_ICHRG_I_DEF_uA; bat_info->constant_charge_voltage_max_uv = SGM4154x_VREG_V_DEF_uV; bat_info->precharge_current_ua = SGM4154x_PRECHRG_I_DEF_uA; bat_info->charge_term_current_ua = SGM4154x_TERMCHRG_I_DEF_uA; sgm->init_data.max_ichg = SGM4154x_ICHRG_I_MAX_uA; sgm->init_data.max_vreg = SGM4154x_VREG_V_DEF_uV; } if (!bat_info->constant_charge_current_max_ua) bat_info->constant_charge_current_max_ua = SGM4154x_ICHRG_I_MAX_uA; if (!bat_info->constant_charge_voltage_max_uv) bat_info->constant_charge_voltage_max_uv = SGM4154x_VREG_V_DEF_uV; if (!bat_info->precharge_current_ua) bat_info->precharge_current_ua = SGM4154x_PRECHRG_I_DEF_uA; if (!bat_info->charge_term_current_ua) bat_info->charge_term_current_ua = SGM4154x_TERMCHRG_I_DEF_uA; if (!sgm->init_data.max_ichg) sgm->init_data.max_ichg = SGM4154x_ICHRG_I_MAX_uA; if (bat_info->constant_charge_voltage_max_uv) sgm->init_data.max_vreg = bat_info->constant_charge_voltage_max_uv; ret = sgm4154x_set_watchdog_timer(sgm, 0); if (ret) goto err_out; ret = sgm4154x_set_prechrg_curr(sgm, bat_info->precharge_current_ua); if (ret) goto err_out; ret = sgm4154x_set_chrg_volt(sgm, sgm->init_data.max_vreg); if (ret) goto err_out; ret = sgm4154x_set_term_curr(sgm, bat_info->charge_term_current_ua); if (ret) goto err_out; ret = sgm4154x_set_input_volt_lim(sgm, sgm->init_data.vlim); if (ret) goto err_out; ret = regmap_read(sgm->regmap, SGM4154x_CHRG_STAT, &chrg_stat); if (ret) { pr_err("read SGM4154x_CHRG_STAT fail\n"); goto err_out; } if (!(chrg_stat & SGM4154x_PG_STAT)) { ret = sgm4154x_set_input_curr_lim(sgm, DEFAULT_INPUT_CURRENT); if (ret) goto err_out; ret = sgm4154x_set_ichrg_curr(sgm, bat_info->constant_charge_current_max_ua); if (ret) goto err_out; ret = sgm4154x_disable_charger(sgm); if (ret) goto err_out; } ret = sgm4154x_set_vac_ovp(sgm); if (ret) goto err_out; regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_d, 0x01, 0x00); regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_a, SGM4154x_IINDPM_INT_MASK, SGM4154x_IINDPM_INT_DIS); regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_a, SGM4154x_VINDPM_INT_MASK, SGM4154x_VINDPM_INT_DIS); ret = sgm4154x_set_recharge_volt(sgm, 200); if (ret) goto err_out; DBG("ichrg_curr:%d\n" "prechrg_curr:%d\n" "chrg_vol:%d\n" "term_curr:%d\n" "input_curr_lim:%d\n", bat_info->constant_charge_current_max_ua, bat_info->precharge_current_ua, bat_info->constant_charge_voltage_max_uv, bat_info->charge_term_current_ua, sgm->init_data.ilim); return 0; err_out: return ret; } static int sgm4154x_parse_dt(struct sgm4154x_device *sgm) { int ret; ret = device_property_read_u32(sgm->dev, "input-voltage-limit-microvolt", &sgm->init_data.vlim); if (ret) sgm->init_data.vlim = SGM4154x_VINDPM_DEF_uV; if (sgm->init_data.vlim > SGM4154x_VINDPM_V_MAX_uV || sgm->init_data.vlim < SGM4154x_VINDPM_V_MIN_uV) return -EINVAL; ret = device_property_read_u32(sgm->dev, "input-current-limit-microamp", &sgm->init_data.ilim); if (ret) sgm->init_data.ilim = SGM4154x_IINDPM_DEF_uA; if (sgm->init_data.ilim > SGM4154x_IINDPM_I_MAX_uA || sgm->init_data.ilim < SGM4154x_IINDPM_I_MIN_uA) return -EINVAL; return 0; } static int sgm4154x_set_otg_voltage(struct sgm4154x_device *sgm, int uv) { int ret = 0; int reg_val = -1; int i = 0; while (i < 4) { if (uv == BOOST_VOLT_LIMIT[i]) { reg_val = i; break; } i++; } if (reg_val < 0) return reg_val; reg_val = reg_val << 4; ret = regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_6, SGM4154x_BOOSTV, reg_val); if (ret) { dev_err(sgm->dev, "set otg voltage error!\n"); return ret; } return ret; } static int sgm4154x_set_otg_current(struct sgm4154x_device *sgm, int ua) { int ret = 0; if (ua == BOOST_CURRENT_LIMIT[0]) { ret = regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_2, SGM4154x_BOOST_LIM, 0); if (ret) { dev_err(sgm->dev, "set boost current limit error!\n"); return ret; } } else if (ua == BOOST_CURRENT_LIMIT[1]) { ret = regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_2, SGM4154x_BOOST_LIM, BIT(7)); if (ret) { dev_err(sgm->dev, "set boost current limit error!\n"); return ret; } } return ret; } static int sgm4154x_enable_vbus(struct regulator_dev *rdev) { struct sgm4154x_device *sgm = rdev_get_drvdata(rdev); int ret = 0; ret = regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_1, SGM4154x_OTG_EN, SGM4154x_OTG_EN); if (ret) { dev_err(sgm->dev, "set OTG enable error!\n"); return ret; } return ret; } static int sgm4154x_disable_vbus(struct regulator_dev *rdev) { struct sgm4154x_device *sgm = rdev_get_drvdata(rdev); int ret = 0; ret = regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_1, SGM4154x_OTG_EN, 0); if (ret) { dev_err(sgm->dev, "set OTG disable error!\n"); return ret; } return ret; } static int sgm4154x_is_enabled_vbus(struct regulator_dev *rdev) { struct sgm4154x_device *sgm = rdev_get_drvdata(rdev); int temp = 0; int ret = 0; ret = regmap_read(sgm->regmap, SGM4154x_CHRG_CTRL_1, &temp); if (ret) { dev_err(sgm->dev, "get vbus status error!\n"); return ret; } return (temp & SGM4154x_OTG_EN) ? 1 : 0; } static const struct regulator_ops sgm4154x_vbus_ops = { .enable = sgm4154x_enable_vbus, .disable = sgm4154x_disable_vbus, .is_enabled = sgm4154x_is_enabled_vbus, }; static struct regulator_desc sgm4154x_otg_rdesc = { .of_match = "otg-vbus", .name = "otg-vbus", .regulators_node = of_match_ptr("regulators"), .ops = &sgm4154x_vbus_ops, .owner = THIS_MODULE, .type = REGULATOR_VOLTAGE, .fixed_uV = 5000000, .n_voltages = 1, }; static int sgm4154x_vbus_regulator_register(struct sgm4154x_device *sgm) { struct device_node *np; struct regulator_config config = {}; int ret = 0; np = of_get_child_by_name(sgm->dev->of_node, "regulators"); if (!np) { dev_warn(sgm->dev, "cannot find regulators node\n"); return -ENXIO; } /* otg regulator */ config.dev = sgm->dev; config.driver_data = sgm; sgm->otg_rdev = devm_regulator_register(sgm->dev, &sgm4154x_otg_rdesc, &config); if (IS_ERR(sgm->otg_rdev)) ret = PTR_ERR(sgm->otg_rdev); return ret; } static int sgm4154x_suspend_notifier(struct notifier_block *nb, unsigned long event, void *dummy) { struct sgm4154x_device *sgm = container_of(nb, struct sgm4154x_device, pm_nb); switch (event) { case PM_SUSPEND_PREPARE: sgm->sgm4154x_suspend_flag = 1; return NOTIFY_OK; case PM_POST_SUSPEND: sgm->sgm4154x_suspend_flag = 0; return NOTIFY_OK; default: return NOTIFY_DONE; } } static int sgm4154x_hw_chipid_detect(struct sgm4154x_device *sgm) { int ret = 0; int val = 0; ret = regmap_read(sgm->regmap, SGM4154x_CHRG_CTRL_b, &val); if (ret < 0) return ret; return val; } static void sgm_charger_work(struct work_struct *work) { struct sgm4154x_device *sgm = container_of(work, struct sgm4154x_device, sgm_delay_work.work); sgm4154x_watchdog_timer_reset(sgm); if (sgm->watchdog_enable) queue_delayed_work(sgm->sgm_monitor_wq, &sgm->sgm_delay_work, msecs_to_jiffies(1000 * 5)); } static int sgm4154x_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct device *dev = &client->dev; struct sgm4154x_device *sgm; int ret; sgm = devm_kzalloc(dev, sizeof(*sgm), GFP_KERNEL); if (!sgm) return -ENOMEM; sgm->client = client; sgm->dev = dev; mutex_init(&sgm->lock); strncpy(sgm->model_name, id->name, I2C_NAME_SIZE); sgm->regmap = devm_regmap_init_i2c(client, &sgm4154x_regmap_config); if (IS_ERR(sgm->regmap)) { dev_err(dev, "Failed to allocate register map\n"); return PTR_ERR(sgm->regmap); } i2c_set_clientdata(client, sgm); ret = sgm4154x_parse_dt(sgm); if (ret) { dev_err(dev, "Failed to read device tree properties%d\n", ret); return ret; } ret = sgm4154x_hw_chipid_detect(sgm); if ((ret & SGM4154x_PN_MASK) != SGM4154x_PN_ID) { pr_info("[%s] device not found !\n", __func__); return ret; } device_init_wakeup(dev, 1); if (client->irq) { ret = devm_request_threaded_irq(dev, client->irq, NULL, sgm4154x_irq_handler_thread, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "sgm41542-irq", sgm); if (ret) goto error_out; enable_irq_wake(client->irq); } sgm->pm_nb.notifier_call = sgm4154x_suspend_notifier; register_pm_notifier(&sgm->pm_nb); ret = sgm4154x_power_supply_init(sgm, dev); if (ret) { dev_err(dev, "Failed to register power supply\n"); goto error_out; } ret = sgm4154x_hw_init(sgm); if (ret) { dev_err(dev, "Cannot initialize the chip.\n"); goto error_out; } /* OTG setting 5V/1.2A */ ret = sgm4154x_set_otg_voltage(sgm, 5000000); if (ret) { dev_err(sgm->dev, "set OTG voltage error!\n"); return ret; } ret = sgm4154x_set_otg_current(sgm, 1200000); if (ret) { dev_err(sgm->dev, "set OTG current error!\n"); return ret; } sgm->sgm_monitor_wq = alloc_ordered_workqueue("%s", WQ_MEM_RECLAIM | WQ_FREEZABLE, "sgm-monitor-wq"); INIT_DELAYED_WORK(&sgm->sgm_delay_work, sgm_charger_work); sgm4154x_vbus_regulator_register(sgm); sgm4154x_create_device_node(sgm->dev); return ret; error_out: return ret; } static void sgm4154x_charger_remove(struct i2c_client *client) { struct sgm4154x_device *sgm = i2c_get_clientdata(client); destroy_workqueue(sgm->sgm_monitor_wq); regulator_unregister(sgm->otg_rdev); power_supply_unregister(sgm->charger); mutex_destroy(&sgm->lock); } static void sgm4154x_charger_shutdown(struct i2c_client *client) { struct sgm4154x_device *sgm = i2c_get_clientdata(client); int ret = 0; sgm4154x_set_prechrg_curr(sgm, SGM4154x_PRECHRG_I_DEF_uA); ret = sgm4154x_disable_charger(sgm); if (ret) pr_err("Failed to disable charger, ret = %d\n", ret); } static const struct i2c_device_id sgm4154x_i2c_ids[] = { { "sgm41542", 0 }, {}, }; MODULE_DEVICE_TABLE(i2c, sgm4154x_i2c_ids); static const struct of_device_id sgm4154x_of_match[] = { { .compatible = "sgm,sgm41542", }, { }, }; MODULE_DEVICE_TABLE(of, sgm4154x_of_match); static struct i2c_driver sgm4154x_driver = { .driver = { .name = "sgm4154x-charger", .of_match_table = sgm4154x_of_match, }, .probe = sgm4154x_probe, .remove = sgm4154x_charger_remove, .shutdown = sgm4154x_charger_shutdown, .id_table = sgm4154x_i2c_ids, }; module_i2c_driver(sgm4154x_driver); MODULE_AUTHOR("Xu Shengfei "); MODULE_DESCRIPTION("sgm4154x charger driver"); MODULE_LICENSE("GPL");