// SPDX-License-Identifier: GPL-2.0 /* * Charger driver for SGM4154x * * Copyright (c) 2026 Rockchip Electronics Co., Ltd. * * Author: Xu Shengfei */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define SGM4154X_MANUFACTURER "SGMICRO" #define SGM4154X_NAME "sgm41542" #define SGM4154X_CHRG_CTRL_0 0x00 #define SGM4154X_HIZ_EN BIT(7) #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_CHRG_CTRL_1 0x01 #define SGM4154X_WDT_RST BIT(6) #define SGM4154X_OTG_EN BIT(5) #define SGM4154X_CHRG_EN BIT(4) #define SGM4154X_CHRG_CTRL_2 0x02 #define SGM4154X_BOOST_LIM BIT(7) #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 3780000 #define SGM4154X_ICHRG_I_DEF_UA 2040000 #define SGM4154X_CHRG_CTRL_3 0x03 #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 #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 #define SGM4154X_CHRG_CTRL_4 0x04 #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 #define SGM4154X_VRECHARGE BIT(0) #define SGM4154X_VRECHRG_STEP_UV 100000 #define SGM4154X_VRECHRG_OFFSET_UV 100000 #define SGM4154X_CHRG_CTRL_5 0x05 #define SGM4154X_TERM_EN BIT(7) #define SGM4154X_WDT_TIMER_MASK GENMASK(5, 4) #define SGM4154X_CHRG_CTRL_6 0x06 #define SGM4154X_VAC_OVP_MASK GENMASK(7, 6) #define SGM4154X_OVP_14V (BIT(7) | BIT(6)) #define SGM4154X_OVP_10_5V BIT(7) #define SGM4154X_OVP_6_5V BIT(6) #define SGM4154X_OVP_5_5V 0 #define SGM4154X_OVP_DEFAULT SGM4154X_OVP_14V #define SGM4154X_BOOSTV GENMASK(5, 4) #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_CHRG_CTRL_7 0x07 #define SGM4154X_CHRG_STAT 0x08 #define SGM4154X_VBUS_STAT_MASK GENMASK(7, 5) #define SGM4154X_OTG_MODE (BIT(7) | BIT(6) | BIT(5)) #define SGM4154X_NON_STANDARD (BIT(7) | BIT(6)) #define SGM4154X_UNKNOWN (BIT(7) | BIT(5)) #define SGM4154X_USB_DCP (BIT(6) | BIT(5)) #define SGM4154X_USB_CDP BIT(6) #define SGM4154X_USB_SDP BIT(5) #define SGM4154X_NOT_CHRGING 0 #define SGM4154X_CHG_STAT_MASK GENMASK(4, 3) #define SGM4154X_TERM_CHRG (BIT(4) | BIT(3)) #define SGM4154X_FAST_CHRG BIT(4) #define SGM4154X_PRECHRG BIT(3) #define SGM4154X_PG_STAT BIT(2) #define SGM4154X_THERM_STAT BIT(1) #define SGM4154X_VSYS_STAT BIT(0) #define SGM4154X_CHRG_FAULT 0x09 #define SGM4154X_TEMP_MASK GENMASK(2, 0) #define SGM4154X_TEMP_HOT (BIT(2) | BIT(1)) #define SGM4154X_TEMP_COLD (BIT(2) | BIT(0)) #define SGM4154X_TEMP_COOL (BIT(1) | BIT(0)) #define SGM4154X_TEMP_WARM BIT(1) #define SGM4154X_TEMP_NORMAL BIT(0) #define SGM4154X_CHRG_CTRL_A 0x0a #define SGM4154X_VBUS_GOOD BIT(7) #define SGM4154X_VINDPM_INT_MASK BIT(1) #define SGM4154X_IINDPM_INT_MASK BIT(0) #define SGM4154X_CHRG_CTRL_B 0x0b #define SGM4154X_PN_ID (BIT(6) | BIT(5) | BIT(3)) #define SGM4154X_PN_MASK GENMASK(6, 3) #define SGM4154X_CHRG_CTRL_C 0x0c #define SGM4154X_CHRG_CTRL_D 0x0d #define SGM4154X_JEITA_EN BIT(0) #define SGM4154X_INPUT_DET 0x0e #define SGM4154X_DPDM_ONGOING BIT(7) #define SGM4154X_CHRG_CTRL_F 0x0f #define SGM4154X_VINDPM_OS_MASK GENMASK(1, 0) #define SGM4154X_DEFAULT_INPUT_CUR (500 * 1000) struct sgm4154x_init_data { int ilim; /* input current limit */ int vlim; /* minimum system voltage limit */ int iterm; /* termination current */ int iprechg; /* precharge current */ int max_ichg; /* maximum charge current */ int max_vreg; /* maximum charge voltage */ }; struct sgm4154x_state { bool vsys_stat; bool therm_stat; bool online; u8 chrg_stat; bool chrg_en; bool vbus_gd; u8 chrg_type; u8 health; u8 chrg_fault; u8 ntc_fault; }; struct sgm4154x_device { struct device *dev; struct power_supply *charger; struct regmap *regmap; struct sgm4154x_init_data init_data; struct sgm4154x_state state; struct regulator_dev *otg_rdev; struct mutex lock; bool watchdog_enable; struct workqueue_struct *sgm_monitor_wq; struct delayed_work sgm_delay_work; }; enum SGM4154X_VINDPM_OS { VINDPM_OS_3900MV, VINDPM_OS_5900MV, VINDPM_OS_7500MV, VINDPM_OS_10500MV, }; enum sgm4154x_wdt_values { SGM4154X_WDT_TIMER_DISABLE = 0, SGM4154X_WDT_TIMER_40S = 1, SGM4154X_WDT_TIMER_80S = 2, SGM4154X_WDT_TIMER_160S = 3, }; static int sgm4154x_set_term_curr(struct sgm4154x_device *sgm, int cur_ua) { int reg_val; int ret; cur_ua = clamp(cur_ua, SGM4154X_TERMCHRG_I_MIN_UA, SGM4154X_TERMCHRG_I_MAX_UA); reg_val = (cur_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 cur_ua) { int reg_val; int ret; cur_ua = clamp(cur_ua, SGM4154X_PRECHRG_I_MIN_UA, SGM4154X_PRECHRG_I_MAX_UA); reg_val = (cur_ua - SGM4154X_PRECHRG_I_MIN_UA) / SGM4154X_PRECHRG_CURRENT_STEP_UA; reg_val = FIELD_PREP(SGM4154X_PRECHRG_CUR_MASK, reg_val); 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 cur_ua) { int reg_val; int ret; cur_ua = clamp(cur_ua, SGM4154X_ICHRG_I_MIN_UA, sgm->init_data.max_ichg); reg_val = cur_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_get_ichrg_curr(struct sgm4154x_device *sgm) { u32 reg; int ret, val; ret = regmap_read(sgm->regmap, SGM4154X_CHRG_CTRL_2, ®); if (ret) { dev_err(sgm->dev, "get charge current error!\n"); return ret; } val = FIELD_GET(SGM4154X_ICHRG_CUR_MASK, reg); return val * SGM4154X_ICHRG_I_STEP_UA; } static int sgm4154x_set_chrg_volt(struct sgm4154x_device *sgm, int chrg_volt) { int reg_val; int ret; /* * Note that the value of 0x01111 represents a "special value" * corresponding to 4352000uV instead of the expected 4336000uV, * per the datasheet. All other values are as expected. So not * only do we need to clamp between max and min values, but * also clamp anything below 4352000uv to 4304000uv to prevent * overcharging. */ chrg_volt = clamp(chrg_volt, SGM4154X_VREG_V_MIN_UV, sgm->init_data.max_vreg); if (chrg_volt < 4352000) chrg_volt = clamp(chrg_volt, SGM4154X_VREG_V_MIN_UV, 4304000); reg_val = (chrg_volt - SGM4154X_VREG_V_MIN_UV) / SGM4154X_VREG_V_STEP_UV; reg_val = FIELD_PREP(SGM4154X_VREG_V_MASK, reg_val); 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_get_chrg_volt(struct sgm4154x_device *sgm) { u32 reg; int ret, val; ret = regmap_read(sgm->regmap, SGM4154X_CHRG_CTRL_4, ®); if (ret) { dev_err(sgm->dev, "get charge voltage error!\n"); return ret; } val = FIELD_GET(SGM4154X_VREG_V_MASK, reg); /* * 0x01111 is a special value meaning 4352000uV, all other * values are as expected based on the offset and step values. */ if (val == 0x0f) return 4352000; return val * SGM4154X_VREG_V_STEP_UV + SGM4154X_VREG_V_MIN_UV; } 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) { dev_err(sgm->dev, "input voltage limit %u outside range\n", vindpm); return -EINVAL; } /* * Supported ranges per the datasheet are as follows: * 3.9v - 5.4v with a 3.9v offset * 5.9v - 7.4v with a 5.9v offset * 7.5v - 9.0v with a 7.5v offset * 10.5v - 12.0v with a 10.5v offset * Step size is a constant 100mv */ if (vindpm < 5900000) { offset = 3900000; vindpm = clamp(vindpm, offset, 5400000); os_val = VINDPM_OS_3900MV; } else if (vindpm < 7500000) { offset = 5900000; vindpm = clamp(vindpm, offset, 7400000); os_val = VINDPM_OS_5900MV; } else if (vindpm < 10500000) { offset = 7500000; vindpm = clamp(vindpm, offset, 9000000); os_val = VINDPM_OS_7500MV; } else { offset = 10500000; vindpm = clamp(vindpm, offset, 12000000); os_val = VINDPM_OS_10500MV; } ret = regmap_update_bits(sgm->regmap, SGM4154X_CHRG_CTRL_F, SGM4154X_VINDPM_OS_MASK, 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; } 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; /* * Protect against a timing-issue edge case if a sysfs write occurs in the middle * of a probe (very unlikely). */ if (sgm->init_data.ilim < SGM4154X_IINDPM_I_MIN_UA) return -EINVAL; /* * Per the datasheet, values between 100000uA and 3100000uA work * as expected with the register defined as having a step of * 100000 and a min/max of 100000 (0x00) through 3100000 (0x1e). * The register value of 0x1f however corresponds to 3800000uA not * 3200000uA as one would expect. */ if ((iindpm > SGM4154X_IINDPM_I_MAX_UA) || (iindpm > sgm->init_data.ilim)) iindpm = min(SGM4154X_IINDPM_I_MAX_UA, sgm->init_data.ilim); if (iindpm > 3100000 && iindpm < SGM4154X_IINDPM_I_MAX_UA) iindpm = 3100000; if (iindpm == SGM4154X_IINDPM_I_MAX_UA) reg_val = 0x1f; else 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; } ilim &= SGM4154X_IINDPM_I_MASK; /* Max value is not 3200000uA as expected but is 3800000uA */ if (ilim == SGM4154X_IINDPM_I_MASK) return SGM4154X_IINDPM_I_MAX_UA; ilim = ilim * 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, 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, u8 time) { int ret; if (time > SGM4154X_WDT_TIMER_160S) return -EINVAL; time = FIELD_PREP(SGM4154X_WDT_TIMER_MASK, time); ret = regmap_update_bits(sgm->regmap, SGM4154X_CHRG_CTRL_5, SGM4154X_WDT_TIMER_MASK, time); if (ret) { dev_err(sgm->dev, "set watchdog timer error!\n"); return ret; } if (time) { 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 { 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 = SGM4154X_OVP_DEFAULT & 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_ua(struct sgm4154x_device *sgm, int recharge_volt) { int reg_val; int ret; reg_val = (recharge_volt - SGM4154X_VRECHRG_OFFSET_UV) / SGM4154X_VRECHRG_STEP_UV; 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) { unsigned int reg; int ret; ret = regmap_read(sgm->regmap, SGM4154X_CHRG_STAT, ®); if (ret) { dev_err(sgm->dev, "read SGM4154X_CHRG_STAT fail\n"); return ret; } state->chrg_type = reg & SGM4154X_VBUS_STAT_MASK; state->chrg_stat = reg & SGM4154X_CHG_STAT_MASK; state->online = !!(reg & SGM4154X_PG_STAT); state->therm_stat = !!(reg & SGM4154X_THERM_STAT); state->vsys_stat = !!(reg & SGM4154X_VSYS_STAT); ret = regmap_read(sgm->regmap, SGM4154X_CHRG_FAULT, ®); if (ret) { dev_err(sgm->dev, "read SGM4154X_CHRG_FAULT fail\n"); return ret; } state->chrg_fault = reg; state->ntc_fault = reg & SGM4154X_TEMP_MASK; state->health = state->ntc_fault; ret = regmap_read(sgm->regmap, SGM4154X_CHRG_CTRL_A, ®); if (ret) { dev_err(sgm->dev, "read SGM4154X_CHRG_CTRL_A fail\n"); return ret; } state->vbus_gd = !!(reg & SGM4154X_VBUS_GOOD); 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; guard(mutex)(&sgm->lock); 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, SGM4154X_WDT_TIMER_DISABLE); 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; scoped_guard(mutex, &sgm->lock) { ret = sgm4154x_get_state(sgm, &state); if (ret) { dev_err(sgm->dev, "get state error!\n"); return ret; } sgm->state = state; } 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_CONSTANT_CHARGE_VOLTAGE: val->intval = sgm4154x_get_chrg_volt(sgm); if (val->intval < 0) return -EINVAL; break; case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: val->intval = sgm4154x_get_ichrg_curr(sgm); if (val->intval < 0) return -EINVAL; break; case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT: val->intval = sgm->init_data.vlim; 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 irqreturn_t sgm4154x_irq_handler_thread(int irq, void *private) { struct sgm4154x_device *sgm = private; struct sgm4154x_state oldstate, state; int ret; guard(mutex)(&sgm->lock); oldstate = sgm->state; ret = sgm4154x_get_state(sgm, &state); if (ret) { dev_err(sgm->dev, "get state error!\n"); return IRQ_NONE; } sgm->state = state; if (state.vbus_gd && !oldstate.vbus_gd) { if (sgm->init_data.ilim >= SGM4154X_DEFAULT_INPUT_CUR) { ret = sgm4154x_set_input_curr_lim(sgm, sgm->init_data.ilim); if (ret) { dev_err(sgm->dev, "set input current error!\n"); /* * Reading IRQ clears interrupt, so return handled * even if error. */ } } } power_supply_changed(sgm->charger); return IRQ_HANDLED; } static int sgm4154x_hw_init(struct power_supply *psy) { struct sgm4154x_device *sgm = power_supply_get_drvdata(psy); struct power_supply_battery_info *bat_info; int ret; u32 val; /* * If unable to read devicetree info, use default/reset * values from hardware. If the devicetree info has data out * of range for any of the values, use the default value. */ sgm->init_data.iprechg = SGM4154X_PRECHRG_I_DEF_UA; sgm->init_data.iterm = SGM4154X_TERMCHRG_I_DEF_UA; sgm->init_data.max_ichg = SGM4154X_ICHRG_I_DEF_UA; sgm->init_data.max_vreg = SGM4154X_VREG_V_DEF_UV; sgm->init_data.vlim = SGM4154X_VINDPM_DEF_UV; sgm->init_data.ilim = SGM4154X_IINDPM_DEF_UA; ret = power_supply_get_battery_info(psy, &bat_info); if (ret) dev_warn(sgm->dev, "sgm4154x: cannot read battery info\n"); else { if ((bat_info->constant_charge_current_max_ua >= SGM4154X_ICHRG_I_MIN_UA) && (bat_info->constant_charge_current_max_ua <= SGM4154X_ICHRG_I_MAX_UA)) sgm->init_data.max_ichg = bat_info->constant_charge_current_max_ua; if ((bat_info->constant_charge_voltage_max_uv >= SGM4154X_VREG_V_MIN_UV) && (bat_info->constant_charge_voltage_max_uv <= SGM4154X_VREG_V_MAX_UV)) sgm->init_data.max_vreg = bat_info->constant_charge_voltage_max_uv; if ((bat_info->charge_term_current_ua >= SGM4154X_TERMCHRG_I_MIN_UA) && (bat_info->charge_term_current_ua <= SGM4154X_TERMCHRG_I_MAX_UA)) sgm->init_data.iterm = bat_info->charge_term_current_ua; if ((bat_info->precharge_current_ua >= SGM4154X_PRECHRG_I_MIN_UA) && (bat_info->precharge_current_ua <= SGM4154X_PRECHRG_I_MAX_UA)) sgm->init_data.iprechg = bat_info->precharge_current_ua; power_supply_put_battery_info(psy, bat_info); } ret = device_property_read_u32(sgm->dev, "input-voltage-limit-microvolt", &val); if (!ret) sgm->init_data.vlim = clamp(val, SGM4154X_VINDPM_V_MIN_UV, SGM4154X_VINDPM_V_MAX_UV); ret = device_property_read_u32(sgm->dev, "input-current-limit-microamp", &val); if (!ret) sgm->init_data.ilim = clamp(val, SGM4154X_IINDPM_I_MIN_UA, SGM4154X_IINDPM_I_MAX_UA); ret = sgm4154x_set_watchdog_timer(sgm, SGM4154X_WDT_TIMER_DISABLE); if (ret) return ret; ret = sgm4154x_set_prechrg_curr(sgm, sgm->init_data.iprechg); if (ret) return ret; ret = sgm4154x_set_chrg_volt(sgm, sgm->init_data.max_vreg); if (ret) return ret; ret = sgm4154x_set_term_curr(sgm, sgm->init_data.iterm); if (ret) return ret; ret = sgm4154x_set_ichrg_curr(sgm, sgm->init_data.max_ichg); if (ret) return ret; ret = sgm4154x_set_input_volt_lim(sgm, sgm->init_data.vlim); if (ret) return ret; ret = sgm4154x_set_input_curr_lim(sgm, sgm->init_data.ilim); if (ret) return ret; ret = sgm4154x_set_vac_ovp(sgm); if (ret) return ret; ret = regmap_update_bits(sgm->regmap, SGM4154X_CHRG_CTRL_D, SGM4154X_JEITA_EN, 0); if (ret) return ret; ret = regmap_update_bits(sgm->regmap, SGM4154X_CHRG_CTRL_A, SGM4154X_IINDPM_INT_MASK, SGM4154X_IINDPM_INT_MASK); if (ret) return ret; ret = regmap_update_bits(sgm->regmap, SGM4154X_CHRG_CTRL_A, SGM4154X_VINDPM_INT_MASK, SGM4154X_VINDPM_INT_MASK); if (ret) return ret; /* * Recharge microvolt set to 200000 by BSP driver instead of hardware * default value of 100000. */ ret = sgm4154x_set_recharge_volt_ua(sgm, 200000); return ret; } static enum power_supply_property sgm4154x_power_supply_props[] = { 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, POWER_SUPPLY_PROP_PRESENT }; static struct power_supply_desc sgm4154x_power_supply_desc = { .name = "sgm4154x-charger", .type = POWER_SUPPLY_TYPE_USB, .init = sgm4154x_hw_init, .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 const struct regmap_config sgm4154x_regmap_config = { .reg_bits = 8, .val_bits = 8, .max_register = SGM4154X_CHRG_CTRL_F, .cache_type = REGCACHE_NONE, }; static const u32 sgm4154x_chg_otg_cur_ua[] = { 1200000, 2000000, }; static const struct regulator_ops sgm4154x_vbus_ops = { .list_voltage = regulator_list_voltage_linear, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, .is_enabled = regulator_is_enabled_regmap, .set_voltage_sel = regulator_set_voltage_sel_regmap, .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_current_limit = regulator_set_current_limit_regmap, .get_current_limit = regulator_get_current_limit_regmap, }; static const 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, .min_uV = 4850000, .uV_step = 150000, .n_voltages = 4, .vsel_reg = SGM4154X_CHRG_CTRL_6, .vsel_mask = SGM4154X_BOOSTV, .enable_reg = SGM4154X_CHRG_CTRL_1, .enable_mask = SGM4154X_OTG_EN, .curr_table = sgm4154x_chg_otg_cur_ua, .n_current_limits = ARRAY_SIZE(sgm4154x_chg_otg_cur_ua), .csel_reg = SGM4154X_CHRG_CTRL_2, .csel_mask = SGM4154X_BOOST_LIM, }; static int sgm4154x_vbus_regulator_register(struct sgm4154x_device *sgm) { struct regulator_config config = { .dev = sgm->dev, .regmap = sgm->regmap, .driver_data = sgm, }; sgm->otg_rdev = devm_regulator_register(sgm->dev, &sgm4154x_otg_rdesc, &config); return PTR_ERR_OR_ZERO(sgm->otg_rdev); } static int sgm4154x_hw_chipid_detect(struct sgm4154x_device *sgm) { int ret; int val; ret = regmap_read(sgm->regmap, SGM4154X_CHRG_CTRL_B, &val); if (ret) return ret; if ((val & SGM4154X_PN_MASK) != SGM4154X_PN_ID) dev_warn(sgm->dev, "sgm4154x device ID mismatch\n"); return 0; } 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 void sgm4154x_disable_irq_wake(void *data) { struct sgm4154x_device *sgm = data; struct i2c_client *client = to_i2c_client(sgm->dev); disable_irq_wake(client->irq); } static int sgm4154x_probe(struct i2c_client *client) { struct power_supply_config psy_cfg = {}; struct device *dev = &client->dev; struct sgm4154x_device *sgm; int ret; sgm = devm_kzalloc(dev, sizeof(*sgm), GFP_KERNEL); if (!sgm) return -ENOMEM; sgm->dev = dev; sgm->regmap = devm_regmap_init_i2c(client, &sgm4154x_regmap_config); if (IS_ERR(sgm->regmap)) return dev_err_probe(dev, PTR_ERR(sgm->regmap), "Failed to allocate register map\n"); i2c_set_clientdata(client, sgm); ret = devm_mutex_init(dev, &sgm->lock); if (ret) return ret; ret = sgm4154x_hw_chipid_detect(sgm); if (ret) return dev_err_probe(dev, ret, "Unable to read HW ID\n"); devm_device_init_wakeup(dev); sgm->sgm_monitor_wq = devm_alloc_ordered_workqueue(dev, "sgm-monitor-wq", WQ_MEM_RECLAIM | WQ_FREEZABLE); if (!sgm->sgm_monitor_wq) return -EINVAL; ret = devm_delayed_work_autocancel(dev, &sgm->sgm_delay_work, sgm_charger_work); if (ret) return dev_err_probe(dev, ret, "Unable to register delayed work\n"); psy_cfg.drv_data = sgm; psy_cfg.fwnode = dev_fwnode(dev); sgm->charger = devm_power_supply_register(sgm->dev, &sgm4154x_power_supply_desc, &psy_cfg); if (IS_ERR(sgm->charger)) return dev_err_probe(dev, PTR_ERR(sgm->charger), "Failed to register power supply\n"); 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) return ret; ret = enable_irq_wake(client->irq); if (!ret) { ret = devm_add_action_or_reset(dev, sgm4154x_disable_irq_wake, sgm); if (ret) return ret; } } ret = sgm4154x_vbus_regulator_register(sgm); if (ret) { return dev_err_probe(dev, ret, "Unable to register VBUS regulator\n"); } return 0; } static int __maybe_unused sgm4154x_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct sgm4154x_device *sgm = i2c_get_clientdata(client); bool watchdog = sgm->watchdog_enable; int ret; /* * Disable watchdog during suspend and stop delayed work. When * delayed work is restarted after resume watchdog will be * re-enabled if it was previously enabled. Retain the current * state of the watchdog timer though, so it can be re-enabled * if necessary by the work queue once resume is triggered. */ ret = sgm4154x_set_watchdog_timer(sgm, SGM4154X_WDT_TIMER_DISABLE); if (ret) return ret; cancel_delayed_work_sync(&sgm->sgm_delay_work); sgm->watchdog_enable = watchdog; return 0; } static int __maybe_unused sgm4154x_resume(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct sgm4154x_device *sgm = i2c_get_clientdata(client); int ret; if (sgm->watchdog_enable) { ret = sgm4154x_set_watchdog_timer(sgm, SGM4154X_WDT_TIMER_40S); if (ret) return ret; } queue_delayed_work(sgm->sgm_monitor_wq, &sgm->sgm_delay_work, 0); return 0; } static SIMPLE_DEV_PM_OPS(sgm4154x_pm_ops, sgm4154x_suspend, sgm4154x_resume); static const struct i2c_device_id sgm4154x_i2c_ids[] = { { "sgm41542" }, { }, }; MODULE_DEVICE_TABLE(i2c, sgm4154x_i2c_ids); static const struct of_device_id sgm4154x_of_match[] = { { .compatible = "sgmicro,sgm41542", }, { }, }; MODULE_DEVICE_TABLE(of, sgm4154x_of_match); static struct i2c_driver sgm4154x_driver = { .driver = { .name = "sgm4154x-charger", .of_match_table = sgm4154x_of_match, .pm = &sgm4154x_pm_ops, }, .probe = sgm4154x_probe, .id_table = sgm4154x_i2c_ids, }; module_i2c_driver(sgm4154x_driver); MODULE_AUTHOR("Xu Shengfei "); MODULE_DESCRIPTION("sgm4154x charger driver"); MODULE_LICENSE("GPL");