Loading Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb5.txt +25 −0 Original line number Diff line number Diff line Loading @@ -281,6 +281,31 @@ Charger specific properties: Definition: Boolean flag which when present disables USB-PD operation. Not applicable for PMI632, where PD is not supported. - qcom,hw-die-temp-mitigation Usage: optional Value type: bool Definition: Boolean flag which when present enables h/w based thermal mitigation. - qcom,hw-connector-mitigation Usage: optional Value type: bool Definition: Boolean flag which when present enables h/w based connector temperature mitigation. - qcom,hw-skin-temp-mitigation Usage: optional Value type: bool Definition: Boolean flag which when present enables h/w based skin temperature mitigation. - qcom,connector-internal-pull-kohm Usage: optional Value type: <u32> Definition: Specifies internal pull-up configuration to be applied to connector THERM, only valid values are (0/30/100/400). If not specified 100K is used as default pull-up. ============================================= Second Level Nodes - SMB5 Charger Peripherals ============================================= Loading drivers/power/supply/qcom/qpnp-smb5.c +191 −17 Original line number Diff line number Diff line Loading @@ -61,7 +61,7 @@ static struct smb_params smb5_pmi632_params = { }, .icl_stat = { .name = "input current limit status", .reg = AICL_ICL_STATUS_REG, .reg = ICL_STATUS_REG, .min_u = 0, .max_u = 3000000, .step_u = 50000, Loading Loading @@ -95,6 +95,22 @@ static struct smb_params smb5_pmi632_params = { .step_u = 400, .set_proc = smblib_set_chg_freq, }, .aicl_5v_threshold = { .name = "AICL 5V threshold", .reg = USBIN_5V_AICL_THRESHOLD_REG, .min_u = 4000, .max_u = 4700, .step_u = 100, }, .aicl_cont_threshold = { .name = "AICL CONT threshold", .reg = USBIN_CONT_AICL_THRESHOLD_REG, .min_u = 4000, .max_u = 8800, .step_u = 100, .get_proc = smblib_get_aicl_cont_threshold, .set_proc = smblib_set_aicl_cont_threshold, }, }; static struct smb_params smb5_pm8150b_params = { Loading Loading @@ -171,6 +187,22 @@ static struct smb_params smb5_pm8150b_params = { .step_u = 400, .set_proc = smblib_set_chg_freq, }, .aicl_5v_threshold = { .name = "AICL 5V threshold", .reg = USBIN_5V_AICL_THRESHOLD_REG, .min_u = 4000, .max_u = 4700, .step_u = 100, }, .aicl_cont_threshold = { .name = "AICL CONT threshold", .reg = USBIN_CONT_AICL_THRESHOLD_REG, .min_u = 4000, .max_u = 11800, .step_u = 100, .get_proc = smblib_get_aicl_cont_threshold, .set_proc = smblib_set_aicl_cont_threshold, }, }; struct smb_dt_props { Loading Loading @@ -214,6 +246,13 @@ module_param_named( weak_chg_icl_ua, __weak_chg_icl_ua, int, 0600 ); enum { BAT_THERM = 0, MISC_THERM, CONN_THERM, SMB_THERM, }; #define PMI632_MAX_ICL_UA 3000000 #define PM6150_MAX_FCC_UA 3000000 static int smb5_chg_config_init(struct smb5 *chip) Loading Loading @@ -258,6 +297,7 @@ static int smb5_chg_config_init(struct smb5 *chip) break; case PMI632_SUBTYPE: chip->chg.smb_version = PMI632_SUBTYPE; chg->wa_flags |= WEAK_ADAPTER_WA | USBIN_OV_WA; chg->param = smb5_pmi632_params; chg->use_extcon = true; chg->name = "pmi632_charger"; Loading Loading @@ -287,6 +327,45 @@ static int smb5_chg_config_init(struct smb5 *chip) return rc; } #define PULL_NO_PULL 0 #define PULL_30K 30 #define PULL_100K 100 #define PULL_400K 400 static int get_valid_pullup(int pull_up) { /* pull up can only be 0/30K/100K/400K) */ switch (pull_up) { case PULL_NO_PULL: return INTERNAL_PULL_NO_PULL; case PULL_30K: return INTERNAL_PULL_30K_PULL; case PULL_100K: return INTERNAL_PULL_100K_PULL; case PULL_400K: return INTERNAL_PULL_400K_PULL; default: return INTERNAL_PULL_100K_PULL; } } #define INTERNAL_PULL_UP_MASK 0x3 static int smb5_configure_internal_pull(struct smb_charger *chg, int type, int pull) { int rc; int shift = type * 2; u8 mask = INTERNAL_PULL_UP_MASK << shift; u8 val = pull << shift; rc = smblib_masked_write(chg, BATIF_ADC_INTERNAL_PULL_UP_REG, mask, val); if (rc < 0) dev_err(chg->dev, "Couldn't configure ADC pull-up reg rc=%d\n", rc); return rc; } #define MICRO_1P5A 1500000 #define MICRO_P1A 100000 #define MICRO_1PA 1000000 Loading Loading @@ -454,6 +533,19 @@ static int smb5_parse_dt(struct smb5 *chip) of_property_read_bool(node, "qcom,uusb-moisture-protection-enable"); chg->hw_die_temp_mitigation = of_property_read_bool(node, "qcom,hw-die-temp-mitigation"); chg->hw_connector_mitigation = of_property_read_bool(node, "qcom,hw-connector-mitigation"); chg->hw_skin_temp_mitigation = of_property_read_bool(node, "qcom,hw-skin-temp-mitigation"); chg->connector_pull_up = -EINVAL; of_property_read_u32(node, "qcom,connector-internal-pull-kohm", &chg->connector_pull_up); /* Extract ADC channels */ rc = smblib_get_iio_channel(chg, "mid_voltage", &chg->iio.mid_chan); if (rc < 0) Loading Loading @@ -542,6 +634,7 @@ static enum power_supply_property smb5_usb_props[] = { POWER_SUPPLY_PROP_SCOPE, POWER_SUPPLY_PROP_MOISTURE_DETECTED, POWER_SUPPLY_PROP_HVDCP_OPTI_ALLOWED, POWER_SUPPLY_PROP_QC_OPTI_DISABLE, }; static int smb5_usb_get_prop(struct power_supply *psy, Loading @@ -552,6 +645,7 @@ static int smb5_usb_get_prop(struct power_supply *psy, struct smb_charger *chg = &chip->chg; union power_supply_propval pval; int rc = 0; val->intval = 0; switch (psp) { case POWER_SUPPLY_PROP_PRESENT: Loading Loading @@ -689,6 +783,13 @@ static int smb5_usb_get_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_HVDCP_OPTI_ALLOWED: val->intval = !chg->flash_active; break; case POWER_SUPPLY_PROP_QC_OPTI_DISABLE: if (chg->hw_die_temp_mitigation) val->intval = POWER_SUPPLY_QC_THERMAL_BALANCE_DISABLE | POWER_SUPPLY_QC_INOV_THERMAL_DISABLE; if (chg->hw_connector_mitigation) val->intval |= POWER_SUPPLY_QC_CTM_DISABLE; break; default: pr_err("get prop %d is not supported in usb\n", psp); rc = -EINVAL; Loading Loading @@ -1453,7 +1554,7 @@ static int smb5_batt_set_prop(struct power_supply *psy, rc = smblib_set_prop_ship_mode(chg, val); break; case POWER_SUPPLY_PROP_RERUN_AICL: rc = smblib_rerun_aicl(chg); rc = smblib_run_aicl(chg, RERUN_AICL); break; case POWER_SUPPLY_PROP_DP_DM: if (!chg->flash_active) Loading Loading @@ -1795,30 +1896,37 @@ static int smb5_configure_micro_usb(struct smb_charger *chg) return rc; } #define RAW_ITERM(iterm_ma, max_range) \ div_s64((int64_t)iterm_ma * ADC_CHG_ITERM_MASK, max_range) static int smb5_configure_iterm_thresholds_adc(struct smb5 *chip) { u8 *buf; int rc = 0; s16 raw_hi_thresh, raw_lo_thresh; s16 raw_hi_thresh, raw_lo_thresh, max_limit_ma; struct smb_charger *chg = &chip->chg; if (chip->dt.term_current_thresh_hi_ma < -10000 || chip->dt.term_current_thresh_hi_ma > 10000 || chip->dt.term_current_thresh_lo_ma < -10000 || chip->dt.term_current_thresh_lo_ma > 10000) { if (chip->chg.smb_version == PMI632_SUBTYPE) max_limit_ma = ITERM_LIMITS_PMI632_MA; else max_limit_ma = ITERM_LIMITS_PM8150B_MA; if (chip->dt.term_current_thresh_hi_ma < (-1 * max_limit_ma) || chip->dt.term_current_thresh_hi_ma > max_limit_ma || chip->dt.term_current_thresh_lo_ma < (-1 * max_limit_ma) || chip->dt.term_current_thresh_lo_ma > max_limit_ma) { dev_err(chg->dev, "ITERM threshold out of range rc=%d\n", rc); return -EINVAL; } /* * Conversion: * raw (A) = (scaled_mA * ADC_CHG_TERM_MASK) / (10 * 1000) * raw (A) = (term_current * ADC_CHG_ITERM_MASK) / max_limit_ma * Note: raw needs to be converted to big-endian format. */ if (chip->dt.term_current_thresh_hi_ma) { raw_hi_thresh = ((chip->dt.term_current_thresh_hi_ma * ADC_CHG_TERM_MASK) / 10000); raw_hi_thresh = RAW_ITERM(chip->dt.term_current_thresh_hi_ma, max_limit_ma); raw_hi_thresh = sign_extend32(raw_hi_thresh, 15); buf = (u8 *)&raw_hi_thresh; raw_hi_thresh = buf[1] | (buf[0] << 8); Loading @@ -1833,8 +1941,8 @@ static int smb5_configure_iterm_thresholds_adc(struct smb5 *chip) } if (chip->dt.term_current_thresh_lo_ma) { raw_lo_thresh = ((chip->dt.term_current_thresh_lo_ma * ADC_CHG_TERM_MASK) / 10000); raw_lo_thresh = RAW_ITERM(chip->dt.term_current_thresh_lo_ma, max_limit_ma); raw_lo_thresh = sign_extend32(raw_lo_thresh, 15); buf = (u8 *)&raw_lo_thresh; raw_lo_thresh = buf[1] | (buf[0] << 8); Loading Loading @@ -1866,6 +1974,54 @@ static int smb5_configure_iterm_thresholds(struct smb5 *chip) return rc; } static int smb5_configure_mitigation(struct smb_charger *chg) { int rc; u8 chan = 0, src_cfg = 0; if (!chg->hw_die_temp_mitigation && !chg->hw_connector_mitigation && !chg->hw_skin_temp_mitigation) { src_cfg = THERMREG_SW_ICL_ADJUST_BIT; } else { if (chg->hw_die_temp_mitigation) { chan = DIE_TEMP_CHANNEL_EN_BIT; src_cfg = THERMREG_DIE_ADC_SRC_EN_BIT | THERMREG_DIE_CMP_SRC_EN_BIT; } if (chg->hw_connector_mitigation) { chan |= CONN_THM_CHANNEL_EN_BIT; src_cfg |= THERMREG_CONNECTOR_ADC_SRC_EN_BIT; } if (chg->hw_skin_temp_mitigation) { chan |= MISC_THM_CHANNEL_EN_BIT; src_cfg |= THERMREG_SKIN_ADC_SRC_EN_BIT; } rc = smblib_masked_write(chg, BATIF_ADC_CHANNEL_EN_REG, CONN_THM_CHANNEL_EN_BIT | DIE_TEMP_CHANNEL_EN_BIT | MISC_THM_CHANNEL_EN_BIT, chan); if (rc < 0) { dev_err(chg->dev, "Couldn't enable ADC channel rc=%d\n", rc); return rc; } } rc = smblib_masked_write(chg, MISC_THERMREG_SRC_CFG_REG, THERMREG_SW_ICL_ADJUST_BIT | THERMREG_DIE_ADC_SRC_EN_BIT | THERMREG_DIE_CMP_SRC_EN_BIT | THERMREG_SKIN_ADC_SRC_EN_BIT | SKIN_ADC_CFG_BIT | THERMREG_CONNECTOR_ADC_SRC_EN_BIT, src_cfg); if (rc < 0) { dev_err(chg->dev, "Couldn't configure THERM_SRC reg rc=%d\n", rc); return rc; } return 0; } static int smb5_init_dc_peripheral(struct smb_charger *chg) { int rc = 0; Loading Loading @@ -1913,6 +2069,12 @@ static int smb5_init_hw(struct smb5 *chip) smblib_get_charge_param(chg, &chg->param.usb_icl, &chg->default_icl_ua); smblib_get_charge_param(chg, &chg->param.aicl_5v_threshold, &chg->default_aicl_5v_threshold_mv); chg->aicl_5v_threshold_mv = chg->default_aicl_5v_threshold_mv; smblib_get_charge_param(chg, &chg->param.aicl_cont_threshold, &chg->default_aicl_cont_threshold_mv); chg->aicl_cont_threshold_mv = chg->default_aicl_cont_threshold_mv; if (chg->charger_temp_max == -EINVAL) { rc = smblib_get_thermal_threshold(chg, Loading @@ -1939,11 +2101,10 @@ static int smb5_init_hw(struct smb5 *chip) return rc; } } else { /* Allows software thermal regulation only */ rc = smblib_write(chg, MISC_THERMREG_SRC_CFG_REG, THERMREG_SW_ICL_ADJUST_BIT); /* configure temperature mitigation */ rc = smb5_configure_mitigation(chg); if (rc < 0) { dev_err(chg->dev, "Couldn't configure SMB thermal regulation rc=%d\n", dev_err(chg->dev, "Couldn't configure mitigation rc=%d\n", rc); return rc; } Loading Loading @@ -2288,6 +2449,17 @@ static int smb5_init_hw(struct smb5 *chip) return rc; } if (chg->connector_pull_up != -EINVAL) { rc = smb5_configure_internal_pull(chg, CONN_THERM, get_valid_pullup(chg->connector_pull_up)); if (rc < 0) { dev_err(chg->dev, "Couldn't configure CONN_THERM pull-up rc=%d\n", rc); return rc; } } return rc; } Loading Loading @@ -2459,10 +2631,12 @@ static struct smb_irq_info smb5_irqs[] = { [USBIN_UV_IRQ] = { .name = "usbin-uv", .handler = usbin_uv_irq_handler, .wake = true, .storm_data = {true, 3000, 5}, }, [USBIN_OV_IRQ] = { .name = "usbin-ov", .handler = default_irq_handler, .handler = usbin_ov_irq_handler, }, [USBIN_PLUGIN_IRQ] = { .name = "usbin-plugin", Loading drivers/power/supply/qcom/schgm-flash.c +26 −1 Original line number Diff line number Diff line Loading @@ -152,6 +152,31 @@ int schgm_flash_get_vreg_ok(struct smb_charger *chg, int *val) return 0; } void schgm_flash_torch_priority(struct smb_charger *chg, enum torch_mode mode) { int rc; u8 reg; /* * If torch is configured in default BOOST mode, skip any update in the * mode configuration. */ if (chg->headroom_mode == FIXED_MODE) return; if ((mode != TORCH_BOOST_MODE) && (mode != TORCH_BUCK_MODE)) return; reg = mode; rc = smblib_masked_write(chg, SCHGM_TORCH_PRIORITY_CONTROL_REG, TORCH_PRIORITY_CONTROL_BIT, reg); if (rc < 0) pr_err("Couldn't configure Torch priority control rc=%d\n", rc); pr_debug("Torch priority changed to: %d\n", mode); } int schgm_flash_init(struct smb_charger *chg) { int rc; Loading Loading @@ -195,7 +220,7 @@ int schgm_flash_init(struct smb_charger *chg) reg = (chg->headroom_mode == FIXED_MODE) ? TORCH_PRIORITY_CONTROL_BIT : 0; rc = smblib_write(chg, SCHGM_TORCH_PRIORITY_CONTROL, reg); rc = smblib_write(chg, SCHGM_TORCH_PRIORITY_CONTROL_REG, reg); if (rc < 0) { pr_err("Couldn't force 5V boost in torch mode rc=%d\n", rc); Loading drivers/power/supply/qcom/schgm-flash.h +7 −1 Original line number Diff line number Diff line Loading @@ -37,7 +37,7 @@ #define SCHGM_FLASH_CONTROL_REG (SCHGM_FLASH_BASE + 0x60) #define SOC_LOW_FOR_FLASH_EN_BIT BIT(7) #define SCHGM_TORCH_PRIORITY_CONTROL (SCHGM_FLASH_BASE + 0x63) #define SCHGM_TORCH_PRIORITY_CONTROL_REG (SCHGM_FLASH_BASE + 0x63) #define TORCH_PRIORITY_CONTROL_BIT BIT(0) #define SCHGM_SOC_BASED_FLASH_DERATE_TH_CFG_REG (SCHGM_FLASH_BASE + 0x67) Loading @@ -45,7 +45,13 @@ #define SCHGM_SOC_BASED_FLASH_DISABLE_TH_CFG_REG \ (SCHGM_FLASH_BASE + 0x68) enum torch_mode { TORCH_BUCK_MODE = 0, TORCH_BOOST_MODE, }; int schgm_flash_get_vreg_ok(struct smb_charger *chg, int *val); void schgm_flash_torch_priority(struct smb_charger *chg, enum torch_mode mode); int schgm_flash_init(struct smb_charger *chg); bool is_flash_active(struct smb_charger *chg); Loading drivers/power/supply/qcom/smb5-lib.c +255 −7 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ #include "schgm-flash.h" #include "step-chg-jeita.h" #include "storm-watch.h" #include "schgm-flash.h" #define smblib_err(chg, fmt, ...) \ pr_err("%s: %s: " fmt, chg->name, \ Loading Loading @@ -595,6 +596,25 @@ static int smblib_is_input_present(struct smb_charger *chg, return 0; } #define AICL_RANGE2_MIN_MV 5600 #define AICL_RANGE2_STEP_DELTA_MV 200 #define AICL_RANGE2_OFFSET 16 int smblib_get_aicl_cont_threshold(struct smb_chg_param *param, u8 val_raw) { int base = param->min_u; u8 reg = val_raw; int step = param->step_u; if (val_raw >= AICL_RANGE2_OFFSET) { reg = val_raw - AICL_RANGE2_OFFSET; base = AICL_RANGE2_MIN_MV; step = AICL_RANGE2_STEP_DELTA_MV; } return base + (reg * step); } /******************** * REGISTER SETTERS * ********************/ Loading Loading @@ -833,6 +853,29 @@ static int smblib_set_usb_pd_allowed_voltage(struct smb_charger *chg, return rc; } int smblib_set_aicl_cont_threshold(struct smb_chg_param *param, int val_u, u8 *val_raw) { int base = param->min_u; int offset = 0; int step = param->step_u; if (val_u > param->max_u) val_u = param->max_u; if (val_u < param->min_u) val_u = param->min_u; if (val_u >= AICL_RANGE2_MIN_MV) { base = AICL_RANGE2_MIN_MV; step = AICL_RANGE2_STEP_DELTA_MV; offset = AICL_RANGE2_OFFSET; }; *val_raw = ((val_u - base) / step) + offset; return 0; } /******************** * HELPER FUNCTIONS * ********************/ Loading Loading @@ -1079,6 +1122,11 @@ static void smblib_uusb_removal(struct smb_charger *chg) smblib_err(chg, "Couldn't set USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V rc=%d\n", rc); /* reset USBOV votes and cancel work */ cancel_delayed_work_sync(&chg->usbov_dbc_work); vote(chg->awake_votable, USBOV_DBC_VOTER, false, 0); chg->dbc_usbov = false; chg->voltage_min_uv = MICRO_5V; chg->voltage_max_uv = MICRO_5V; chg->usb_icl_delta_ua = 0; Loading Loading @@ -1313,8 +1361,7 @@ int smblib_set_icl_current(struct smb_charger *chg, int icl_ua) /* Re-run AICL */ if (icl_override != SW_OVERRIDE_HC_MODE) rc = smblib_rerun_aicl(chg); rc = smblib_run_aicl(chg, RERUN_AICL); out: return rc; } Loading Loading @@ -1703,7 +1750,32 @@ int smblib_get_prop_batt_status(struct smb_charger *chg, union power_supply_propval pval = {0, }; bool usb_online, dc_online; u8 stat; int rc; int rc, suspend = 0; if (chg->dbc_usbov) { rc = smblib_get_prop_usb_present(chg, &pval); if (rc < 0) { smblib_err(chg, "Couldn't get usb present prop rc=%d\n", rc); return rc; } rc = smblib_get_usb_suspend(chg, &suspend); if (rc < 0) { smblib_err(chg, "Couldn't get usb suspend rc=%d\n", rc); return rc; } /* * Report charging as long as USBOV is not debounced and * charging path is un-suspended. */ if (pval.intval && !suspend) { val->intval = POWER_SUPPLY_STATUS_CHARGING; return 0; } } rc = smblib_get_prop_usb_online(chg, &pval); if (rc < 0) { Loading Loading @@ -1946,7 +2018,14 @@ int smblib_get_prop_batt_iterm(struct smb_charger *chg, temp = buf[1] | (buf[0] << 8); temp = sign_extend32(temp, 15); temp = DIV_ROUND_CLOSEST(temp * 10000, ADC_CHG_TERM_MASK); if (chg->smb_version == PMI632_SUBTYPE) temp = DIV_ROUND_CLOSEST(temp * ITERM_LIMITS_PMI632_MA, ADC_CHG_ITERM_MASK); else temp = DIV_ROUND_CLOSEST(temp * ITERM_LIMITS_PM8150B_MA, ADC_CHG_ITERM_MASK); val->intval = temp; return rc; Loading Loading @@ -2075,7 +2154,7 @@ int smblib_set_prop_rechg_soc_thresh(struct smb_charger *chg, return rc; } int smblib_rerun_aicl(struct smb_charger *chg) int smblib_run_aicl(struct smb_charger *chg, int type) { int rc; u8 stat; Loading @@ -2093,8 +2172,8 @@ int smblib_rerun_aicl(struct smb_charger *chg) smblib_dbg(chg, PR_MISC, "re-running AICL\n"); rc = smblib_masked_write(chg, AICL_CMD_REG, RERUN_AICL_BIT, RERUN_AICL_BIT); stat = (type == RERUN_AICL) ? RERUN_AICL_BIT : RESTART_AICL_BIT; rc = smblib_masked_write(chg, AICL_CMD_REG, stat, stat); if (rc < 0) smblib_err(chg, "Couldn't write to AICL_CMD_REG rc=%d\n", rc); Loading Loading @@ -4092,6 +4171,8 @@ irqreturn_t batt_psy_changed_irq_handler(int irq, void *data) return IRQ_HANDLED; } #define AICL_STEP_MV 200 #define MAX_AICL_THRESHOLD_MV 4800 irqreturn_t usbin_uv_irq_handler(int irq, void *data) { struct smb_irq_data *irq_data = data; Loading @@ -4102,6 +4183,70 @@ irqreturn_t usbin_uv_irq_handler(int irq, void *data) u8 stat = 0, max_pulses = 0; smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s\n", irq_data->name); if ((chg->wa_flags & WEAK_ADAPTER_WA) && is_storming(&irq_data->storm_data)) { if (chg->aicl_max_reached) { smblib_dbg(chg, PR_MISC, "USBIN_UV storm at max AICL threshold\n"); return IRQ_HANDLED; } smblib_dbg(chg, PR_MISC, "USBIN_UV storm at threshold %d\n", chg->aicl_5v_threshold_mv); /* suspend USBIN before updating AICL threshold */ vote(chg->usb_icl_votable, AICL_THRESHOLD_VOTER, true, 0); /* delay for VASHDN deglitch */ msleep(20); if (chg->aicl_5v_threshold_mv > MAX_AICL_THRESHOLD_MV) { /* reached max AICL threshold */ chg->aicl_max_reached = true; goto unsuspend_input; } /* Increase AICL threshold by 200mV */ rc = smblib_set_charge_param(chg, &chg->param.aicl_5v_threshold, chg->aicl_5v_threshold_mv + AICL_STEP_MV); if (rc < 0) dev_err(chg->dev, "Error in setting AICL threshold rc=%d\n", rc); else chg->aicl_5v_threshold_mv += AICL_STEP_MV; rc = smblib_set_charge_param(chg, &chg->param.aicl_cont_threshold, chg->aicl_cont_threshold_mv + AICL_STEP_MV); if (rc < 0) dev_err(chg->dev, "Error in setting AICL threshold rc=%d\n", rc); else chg->aicl_cont_threshold_mv += AICL_STEP_MV; unsuspend_input: /* Force torch in boost mode to ensure it works with low ICL */ if (chg->smb_version == PMI632_SUBTYPE) schgm_flash_torch_priority(chg, TORCH_BOOST_MODE); if (chg->aicl_max_reached) { smblib_dbg(chg, PR_MISC, "Reached max AICL threshold resctricting ICL to 100mA\n"); vote(chg->usb_icl_votable, AICL_THRESHOLD_VOTER, true, USBIN_100MA); smblib_run_aicl(chg, RESTART_AICL); } else { smblib_run_aicl(chg, RESTART_AICL); vote(chg->usb_icl_votable, AICL_THRESHOLD_VOTER, false, 0); } wdata = &chg->irq_info[USBIN_UV_IRQ].irq_data->storm_data; reset_storm_count(wdata); } if (!chg->irq_info[SWITCHER_POWER_OK_IRQ].irq_data) return IRQ_HANDLED; Loading Loading @@ -4170,6 +4315,24 @@ irqreturn_t icl_change_irq_handler(int irq, void *data) struct smb_charger *chg = irq_data->parent_data; if (chg->mode == PARALLEL_MASTER) { /* * Ignore if change in ICL is due to DIE temp mitigation. * This is to prevent any further ICL split. */ if (chg->hw_die_temp_mitigation) { rc = smblib_read(chg, DIE_TEMP_STATUS_REG, &stat); if (rc < 0) { smblib_err(chg, "Couldn't read DIE_TEMP rc=%d\n", rc); return IRQ_HANDLED; } if (stat & (DIE_TEMP_UB_BIT | DIE_TEMP_LB_BIT)) { smblib_dbg(chg, PR_PARALLEL, "skip ICL change DIE_TEMP %x\n", stat); return IRQ_HANDLED; } } rc = smblib_read(chg, AICL_STATUS_REG, &stat); if (rc < 0) { smblib_err(chg, "Couldn't read AICL_STATUS rc=%d\n", Loading Loading @@ -4313,6 +4476,33 @@ void smblib_usb_plugin_locked(struct smb_charger *chg) vote(chg->fcc_votable, FCC_STEPPER_VOTER, true, 1500000); if (chg->wa_flags & WEAK_ADAPTER_WA) { chg->aicl_5v_threshold_mv = chg->default_aicl_5v_threshold_mv; chg->aicl_cont_threshold_mv = chg->default_aicl_cont_threshold_mv; smblib_set_charge_param(chg, &chg->param.aicl_5v_threshold, chg->aicl_5v_threshold_mv); smblib_set_charge_param(chg, &chg->param.aicl_cont_threshold, chg->aicl_cont_threshold_mv); chg->aicl_max_reached = false; if (chg->smb_version == PMI632_SUBTYPE) schgm_flash_torch_priority(chg, TORCH_BUCK_MODE); data = chg->irq_info[USBIN_UV_IRQ].irq_data; if (data) { wdata = &data->storm_data; reset_storm_count(wdata); } vote(chg->usb_icl_votable, AICL_THRESHOLD_VOTER, false, 0); } rc = smblib_request_dpdm(chg, false); if (rc < 0) smblib_err(chg, "Couldn't disable DPDM rc=%d\n", rc); Loading Loading @@ -4770,6 +4960,11 @@ static void typec_src_removal(struct smb_charger *chg) vote(chg->cp_disable_votable, SW_THERM_REGULATION_VOTER, false, 0); /* reset USBOV votes and cancel work */ cancel_delayed_work_sync(&chg->usbov_dbc_work); vote(chg->awake_votable, USBOV_DBC_VOTER, false, 0); chg->dbc_usbov = false; chg->pulse_cnt = 0; chg->usb_icl_delta_ua = 0; chg->voltage_min_uv = MICRO_5V; Loading Loading @@ -5218,6 +5413,57 @@ irqreturn_t temp_change_irq_handler(int irq, void *data) return IRQ_HANDLED; } static void smblib_usbov_dbc_work(struct work_struct *work) { struct smb_charger *chg = container_of(work, struct smb_charger, usbov_dbc_work.work); smblib_dbg(chg, PR_MISC, "Resetting USBOV debounce\n"); chg->dbc_usbov = false; vote(chg->awake_votable, USBOV_DBC_VOTER, false, 0); } #define USB_OV_DBC_PERIOD_MS 1000 irqreturn_t usbin_ov_irq_handler(int irq, void *data) { struct smb_irq_data *irq_data = data; struct smb_charger *chg = irq_data->parent_data; u8 stat; int rc; smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s\n", irq_data->name); if (!(chg->wa_flags & USBIN_OV_WA)) return IRQ_HANDLED; rc = smblib_read(chg, USBIN_BASE + INT_RT_STS_OFFSET, &stat); if (rc < 0) { smblib_err(chg, "Couldn't read USB_INT_RT_STS rc=%d\n", rc); return IRQ_HANDLED; } /* * On specific PMICs, OV IRQ triggers for very small duration in * interim periods affecting charging status reflection. In order to * differentiate between OV IRQ glitch and real OV_IRQ, add a debounce * period for evaluation. */ if (stat & USBIN_OV_RT_STS_BIT) { chg->dbc_usbov = true; vote(chg->awake_votable, USBOV_DBC_VOTER, true, 0); schedule_delayed_work(&chg->usbov_dbc_work, msecs_to_jiffies(USB_OV_DBC_PERIOD_MS)); } else { cancel_delayed_work_sync(&chg->usbov_dbc_work); chg->dbc_usbov = false; vote(chg->awake_votable, USBOV_DBC_VOTER, false, 0); } smblib_dbg(chg, PR_MISC, "USBOV debounce status %d\n", chg->dbc_usbov); return IRQ_HANDLED; } /************** * Additional USB PSY getters/setters * that call interrupt functions Loading Loading @@ -5885,6 +6131,7 @@ int smblib_init(struct smb_charger *chg) INIT_DELAYED_WORK(&chg->lpd_detach_work, smblib_lpd_detach_work); INIT_DELAYED_WORK(&chg->thermal_regulation_work, smblib_thermal_regulation_work); INIT_DELAYED_WORK(&chg->usbov_dbc_work, smblib_usbov_dbc_work); if (chg->uusb_moisture_protection_enabled) { INIT_WORK(&chg->moisture_protection_work, Loading Loading @@ -6006,6 +6253,7 @@ int smblib_deinit(struct smb_charger *chg) cancel_delayed_work_sync(&chg->lpd_ra_open_work); cancel_delayed_work_sync(&chg->lpd_detach_work); cancel_delayed_work_sync(&chg->thermal_regulation_work); cancel_delayed_work_sync(&chg->usbov_dbc_work); power_supply_unreg_notifier(&chg->nb); smblib_destroy_votables(chg); qcom_step_chg_deinit(); Loading Loading
Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb5.txt +25 −0 Original line number Diff line number Diff line Loading @@ -281,6 +281,31 @@ Charger specific properties: Definition: Boolean flag which when present disables USB-PD operation. Not applicable for PMI632, where PD is not supported. - qcom,hw-die-temp-mitigation Usage: optional Value type: bool Definition: Boolean flag which when present enables h/w based thermal mitigation. - qcom,hw-connector-mitigation Usage: optional Value type: bool Definition: Boolean flag which when present enables h/w based connector temperature mitigation. - qcom,hw-skin-temp-mitigation Usage: optional Value type: bool Definition: Boolean flag which when present enables h/w based skin temperature mitigation. - qcom,connector-internal-pull-kohm Usage: optional Value type: <u32> Definition: Specifies internal pull-up configuration to be applied to connector THERM, only valid values are (0/30/100/400). If not specified 100K is used as default pull-up. ============================================= Second Level Nodes - SMB5 Charger Peripherals ============================================= Loading
drivers/power/supply/qcom/qpnp-smb5.c +191 −17 Original line number Diff line number Diff line Loading @@ -61,7 +61,7 @@ static struct smb_params smb5_pmi632_params = { }, .icl_stat = { .name = "input current limit status", .reg = AICL_ICL_STATUS_REG, .reg = ICL_STATUS_REG, .min_u = 0, .max_u = 3000000, .step_u = 50000, Loading Loading @@ -95,6 +95,22 @@ static struct smb_params smb5_pmi632_params = { .step_u = 400, .set_proc = smblib_set_chg_freq, }, .aicl_5v_threshold = { .name = "AICL 5V threshold", .reg = USBIN_5V_AICL_THRESHOLD_REG, .min_u = 4000, .max_u = 4700, .step_u = 100, }, .aicl_cont_threshold = { .name = "AICL CONT threshold", .reg = USBIN_CONT_AICL_THRESHOLD_REG, .min_u = 4000, .max_u = 8800, .step_u = 100, .get_proc = smblib_get_aicl_cont_threshold, .set_proc = smblib_set_aicl_cont_threshold, }, }; static struct smb_params smb5_pm8150b_params = { Loading Loading @@ -171,6 +187,22 @@ static struct smb_params smb5_pm8150b_params = { .step_u = 400, .set_proc = smblib_set_chg_freq, }, .aicl_5v_threshold = { .name = "AICL 5V threshold", .reg = USBIN_5V_AICL_THRESHOLD_REG, .min_u = 4000, .max_u = 4700, .step_u = 100, }, .aicl_cont_threshold = { .name = "AICL CONT threshold", .reg = USBIN_CONT_AICL_THRESHOLD_REG, .min_u = 4000, .max_u = 11800, .step_u = 100, .get_proc = smblib_get_aicl_cont_threshold, .set_proc = smblib_set_aicl_cont_threshold, }, }; struct smb_dt_props { Loading Loading @@ -214,6 +246,13 @@ module_param_named( weak_chg_icl_ua, __weak_chg_icl_ua, int, 0600 ); enum { BAT_THERM = 0, MISC_THERM, CONN_THERM, SMB_THERM, }; #define PMI632_MAX_ICL_UA 3000000 #define PM6150_MAX_FCC_UA 3000000 static int smb5_chg_config_init(struct smb5 *chip) Loading Loading @@ -258,6 +297,7 @@ static int smb5_chg_config_init(struct smb5 *chip) break; case PMI632_SUBTYPE: chip->chg.smb_version = PMI632_SUBTYPE; chg->wa_flags |= WEAK_ADAPTER_WA | USBIN_OV_WA; chg->param = smb5_pmi632_params; chg->use_extcon = true; chg->name = "pmi632_charger"; Loading Loading @@ -287,6 +327,45 @@ static int smb5_chg_config_init(struct smb5 *chip) return rc; } #define PULL_NO_PULL 0 #define PULL_30K 30 #define PULL_100K 100 #define PULL_400K 400 static int get_valid_pullup(int pull_up) { /* pull up can only be 0/30K/100K/400K) */ switch (pull_up) { case PULL_NO_PULL: return INTERNAL_PULL_NO_PULL; case PULL_30K: return INTERNAL_PULL_30K_PULL; case PULL_100K: return INTERNAL_PULL_100K_PULL; case PULL_400K: return INTERNAL_PULL_400K_PULL; default: return INTERNAL_PULL_100K_PULL; } } #define INTERNAL_PULL_UP_MASK 0x3 static int smb5_configure_internal_pull(struct smb_charger *chg, int type, int pull) { int rc; int shift = type * 2; u8 mask = INTERNAL_PULL_UP_MASK << shift; u8 val = pull << shift; rc = smblib_masked_write(chg, BATIF_ADC_INTERNAL_PULL_UP_REG, mask, val); if (rc < 0) dev_err(chg->dev, "Couldn't configure ADC pull-up reg rc=%d\n", rc); return rc; } #define MICRO_1P5A 1500000 #define MICRO_P1A 100000 #define MICRO_1PA 1000000 Loading Loading @@ -454,6 +533,19 @@ static int smb5_parse_dt(struct smb5 *chip) of_property_read_bool(node, "qcom,uusb-moisture-protection-enable"); chg->hw_die_temp_mitigation = of_property_read_bool(node, "qcom,hw-die-temp-mitigation"); chg->hw_connector_mitigation = of_property_read_bool(node, "qcom,hw-connector-mitigation"); chg->hw_skin_temp_mitigation = of_property_read_bool(node, "qcom,hw-skin-temp-mitigation"); chg->connector_pull_up = -EINVAL; of_property_read_u32(node, "qcom,connector-internal-pull-kohm", &chg->connector_pull_up); /* Extract ADC channels */ rc = smblib_get_iio_channel(chg, "mid_voltage", &chg->iio.mid_chan); if (rc < 0) Loading Loading @@ -542,6 +634,7 @@ static enum power_supply_property smb5_usb_props[] = { POWER_SUPPLY_PROP_SCOPE, POWER_SUPPLY_PROP_MOISTURE_DETECTED, POWER_SUPPLY_PROP_HVDCP_OPTI_ALLOWED, POWER_SUPPLY_PROP_QC_OPTI_DISABLE, }; static int smb5_usb_get_prop(struct power_supply *psy, Loading @@ -552,6 +645,7 @@ static int smb5_usb_get_prop(struct power_supply *psy, struct smb_charger *chg = &chip->chg; union power_supply_propval pval; int rc = 0; val->intval = 0; switch (psp) { case POWER_SUPPLY_PROP_PRESENT: Loading Loading @@ -689,6 +783,13 @@ static int smb5_usb_get_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_HVDCP_OPTI_ALLOWED: val->intval = !chg->flash_active; break; case POWER_SUPPLY_PROP_QC_OPTI_DISABLE: if (chg->hw_die_temp_mitigation) val->intval = POWER_SUPPLY_QC_THERMAL_BALANCE_DISABLE | POWER_SUPPLY_QC_INOV_THERMAL_DISABLE; if (chg->hw_connector_mitigation) val->intval |= POWER_SUPPLY_QC_CTM_DISABLE; break; default: pr_err("get prop %d is not supported in usb\n", psp); rc = -EINVAL; Loading Loading @@ -1453,7 +1554,7 @@ static int smb5_batt_set_prop(struct power_supply *psy, rc = smblib_set_prop_ship_mode(chg, val); break; case POWER_SUPPLY_PROP_RERUN_AICL: rc = smblib_rerun_aicl(chg); rc = smblib_run_aicl(chg, RERUN_AICL); break; case POWER_SUPPLY_PROP_DP_DM: if (!chg->flash_active) Loading Loading @@ -1795,30 +1896,37 @@ static int smb5_configure_micro_usb(struct smb_charger *chg) return rc; } #define RAW_ITERM(iterm_ma, max_range) \ div_s64((int64_t)iterm_ma * ADC_CHG_ITERM_MASK, max_range) static int smb5_configure_iterm_thresholds_adc(struct smb5 *chip) { u8 *buf; int rc = 0; s16 raw_hi_thresh, raw_lo_thresh; s16 raw_hi_thresh, raw_lo_thresh, max_limit_ma; struct smb_charger *chg = &chip->chg; if (chip->dt.term_current_thresh_hi_ma < -10000 || chip->dt.term_current_thresh_hi_ma > 10000 || chip->dt.term_current_thresh_lo_ma < -10000 || chip->dt.term_current_thresh_lo_ma > 10000) { if (chip->chg.smb_version == PMI632_SUBTYPE) max_limit_ma = ITERM_LIMITS_PMI632_MA; else max_limit_ma = ITERM_LIMITS_PM8150B_MA; if (chip->dt.term_current_thresh_hi_ma < (-1 * max_limit_ma) || chip->dt.term_current_thresh_hi_ma > max_limit_ma || chip->dt.term_current_thresh_lo_ma < (-1 * max_limit_ma) || chip->dt.term_current_thresh_lo_ma > max_limit_ma) { dev_err(chg->dev, "ITERM threshold out of range rc=%d\n", rc); return -EINVAL; } /* * Conversion: * raw (A) = (scaled_mA * ADC_CHG_TERM_MASK) / (10 * 1000) * raw (A) = (term_current * ADC_CHG_ITERM_MASK) / max_limit_ma * Note: raw needs to be converted to big-endian format. */ if (chip->dt.term_current_thresh_hi_ma) { raw_hi_thresh = ((chip->dt.term_current_thresh_hi_ma * ADC_CHG_TERM_MASK) / 10000); raw_hi_thresh = RAW_ITERM(chip->dt.term_current_thresh_hi_ma, max_limit_ma); raw_hi_thresh = sign_extend32(raw_hi_thresh, 15); buf = (u8 *)&raw_hi_thresh; raw_hi_thresh = buf[1] | (buf[0] << 8); Loading @@ -1833,8 +1941,8 @@ static int smb5_configure_iterm_thresholds_adc(struct smb5 *chip) } if (chip->dt.term_current_thresh_lo_ma) { raw_lo_thresh = ((chip->dt.term_current_thresh_lo_ma * ADC_CHG_TERM_MASK) / 10000); raw_lo_thresh = RAW_ITERM(chip->dt.term_current_thresh_lo_ma, max_limit_ma); raw_lo_thresh = sign_extend32(raw_lo_thresh, 15); buf = (u8 *)&raw_lo_thresh; raw_lo_thresh = buf[1] | (buf[0] << 8); Loading Loading @@ -1866,6 +1974,54 @@ static int smb5_configure_iterm_thresholds(struct smb5 *chip) return rc; } static int smb5_configure_mitigation(struct smb_charger *chg) { int rc; u8 chan = 0, src_cfg = 0; if (!chg->hw_die_temp_mitigation && !chg->hw_connector_mitigation && !chg->hw_skin_temp_mitigation) { src_cfg = THERMREG_SW_ICL_ADJUST_BIT; } else { if (chg->hw_die_temp_mitigation) { chan = DIE_TEMP_CHANNEL_EN_BIT; src_cfg = THERMREG_DIE_ADC_SRC_EN_BIT | THERMREG_DIE_CMP_SRC_EN_BIT; } if (chg->hw_connector_mitigation) { chan |= CONN_THM_CHANNEL_EN_BIT; src_cfg |= THERMREG_CONNECTOR_ADC_SRC_EN_BIT; } if (chg->hw_skin_temp_mitigation) { chan |= MISC_THM_CHANNEL_EN_BIT; src_cfg |= THERMREG_SKIN_ADC_SRC_EN_BIT; } rc = smblib_masked_write(chg, BATIF_ADC_CHANNEL_EN_REG, CONN_THM_CHANNEL_EN_BIT | DIE_TEMP_CHANNEL_EN_BIT | MISC_THM_CHANNEL_EN_BIT, chan); if (rc < 0) { dev_err(chg->dev, "Couldn't enable ADC channel rc=%d\n", rc); return rc; } } rc = smblib_masked_write(chg, MISC_THERMREG_SRC_CFG_REG, THERMREG_SW_ICL_ADJUST_BIT | THERMREG_DIE_ADC_SRC_EN_BIT | THERMREG_DIE_CMP_SRC_EN_BIT | THERMREG_SKIN_ADC_SRC_EN_BIT | SKIN_ADC_CFG_BIT | THERMREG_CONNECTOR_ADC_SRC_EN_BIT, src_cfg); if (rc < 0) { dev_err(chg->dev, "Couldn't configure THERM_SRC reg rc=%d\n", rc); return rc; } return 0; } static int smb5_init_dc_peripheral(struct smb_charger *chg) { int rc = 0; Loading Loading @@ -1913,6 +2069,12 @@ static int smb5_init_hw(struct smb5 *chip) smblib_get_charge_param(chg, &chg->param.usb_icl, &chg->default_icl_ua); smblib_get_charge_param(chg, &chg->param.aicl_5v_threshold, &chg->default_aicl_5v_threshold_mv); chg->aicl_5v_threshold_mv = chg->default_aicl_5v_threshold_mv; smblib_get_charge_param(chg, &chg->param.aicl_cont_threshold, &chg->default_aicl_cont_threshold_mv); chg->aicl_cont_threshold_mv = chg->default_aicl_cont_threshold_mv; if (chg->charger_temp_max == -EINVAL) { rc = smblib_get_thermal_threshold(chg, Loading @@ -1939,11 +2101,10 @@ static int smb5_init_hw(struct smb5 *chip) return rc; } } else { /* Allows software thermal regulation only */ rc = smblib_write(chg, MISC_THERMREG_SRC_CFG_REG, THERMREG_SW_ICL_ADJUST_BIT); /* configure temperature mitigation */ rc = smb5_configure_mitigation(chg); if (rc < 0) { dev_err(chg->dev, "Couldn't configure SMB thermal regulation rc=%d\n", dev_err(chg->dev, "Couldn't configure mitigation rc=%d\n", rc); return rc; } Loading Loading @@ -2288,6 +2449,17 @@ static int smb5_init_hw(struct smb5 *chip) return rc; } if (chg->connector_pull_up != -EINVAL) { rc = smb5_configure_internal_pull(chg, CONN_THERM, get_valid_pullup(chg->connector_pull_up)); if (rc < 0) { dev_err(chg->dev, "Couldn't configure CONN_THERM pull-up rc=%d\n", rc); return rc; } } return rc; } Loading Loading @@ -2459,10 +2631,12 @@ static struct smb_irq_info smb5_irqs[] = { [USBIN_UV_IRQ] = { .name = "usbin-uv", .handler = usbin_uv_irq_handler, .wake = true, .storm_data = {true, 3000, 5}, }, [USBIN_OV_IRQ] = { .name = "usbin-ov", .handler = default_irq_handler, .handler = usbin_ov_irq_handler, }, [USBIN_PLUGIN_IRQ] = { .name = "usbin-plugin", Loading
drivers/power/supply/qcom/schgm-flash.c +26 −1 Original line number Diff line number Diff line Loading @@ -152,6 +152,31 @@ int schgm_flash_get_vreg_ok(struct smb_charger *chg, int *val) return 0; } void schgm_flash_torch_priority(struct smb_charger *chg, enum torch_mode mode) { int rc; u8 reg; /* * If torch is configured in default BOOST mode, skip any update in the * mode configuration. */ if (chg->headroom_mode == FIXED_MODE) return; if ((mode != TORCH_BOOST_MODE) && (mode != TORCH_BUCK_MODE)) return; reg = mode; rc = smblib_masked_write(chg, SCHGM_TORCH_PRIORITY_CONTROL_REG, TORCH_PRIORITY_CONTROL_BIT, reg); if (rc < 0) pr_err("Couldn't configure Torch priority control rc=%d\n", rc); pr_debug("Torch priority changed to: %d\n", mode); } int schgm_flash_init(struct smb_charger *chg) { int rc; Loading Loading @@ -195,7 +220,7 @@ int schgm_flash_init(struct smb_charger *chg) reg = (chg->headroom_mode == FIXED_MODE) ? TORCH_PRIORITY_CONTROL_BIT : 0; rc = smblib_write(chg, SCHGM_TORCH_PRIORITY_CONTROL, reg); rc = smblib_write(chg, SCHGM_TORCH_PRIORITY_CONTROL_REG, reg); if (rc < 0) { pr_err("Couldn't force 5V boost in torch mode rc=%d\n", rc); Loading
drivers/power/supply/qcom/schgm-flash.h +7 −1 Original line number Diff line number Diff line Loading @@ -37,7 +37,7 @@ #define SCHGM_FLASH_CONTROL_REG (SCHGM_FLASH_BASE + 0x60) #define SOC_LOW_FOR_FLASH_EN_BIT BIT(7) #define SCHGM_TORCH_PRIORITY_CONTROL (SCHGM_FLASH_BASE + 0x63) #define SCHGM_TORCH_PRIORITY_CONTROL_REG (SCHGM_FLASH_BASE + 0x63) #define TORCH_PRIORITY_CONTROL_BIT BIT(0) #define SCHGM_SOC_BASED_FLASH_DERATE_TH_CFG_REG (SCHGM_FLASH_BASE + 0x67) Loading @@ -45,7 +45,13 @@ #define SCHGM_SOC_BASED_FLASH_DISABLE_TH_CFG_REG \ (SCHGM_FLASH_BASE + 0x68) enum torch_mode { TORCH_BUCK_MODE = 0, TORCH_BOOST_MODE, }; int schgm_flash_get_vreg_ok(struct smb_charger *chg, int *val); void schgm_flash_torch_priority(struct smb_charger *chg, enum torch_mode mode); int schgm_flash_init(struct smb_charger *chg); bool is_flash_active(struct smb_charger *chg); Loading
drivers/power/supply/qcom/smb5-lib.c +255 −7 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ #include "schgm-flash.h" #include "step-chg-jeita.h" #include "storm-watch.h" #include "schgm-flash.h" #define smblib_err(chg, fmt, ...) \ pr_err("%s: %s: " fmt, chg->name, \ Loading Loading @@ -595,6 +596,25 @@ static int smblib_is_input_present(struct smb_charger *chg, return 0; } #define AICL_RANGE2_MIN_MV 5600 #define AICL_RANGE2_STEP_DELTA_MV 200 #define AICL_RANGE2_OFFSET 16 int smblib_get_aicl_cont_threshold(struct smb_chg_param *param, u8 val_raw) { int base = param->min_u; u8 reg = val_raw; int step = param->step_u; if (val_raw >= AICL_RANGE2_OFFSET) { reg = val_raw - AICL_RANGE2_OFFSET; base = AICL_RANGE2_MIN_MV; step = AICL_RANGE2_STEP_DELTA_MV; } return base + (reg * step); } /******************** * REGISTER SETTERS * ********************/ Loading Loading @@ -833,6 +853,29 @@ static int smblib_set_usb_pd_allowed_voltage(struct smb_charger *chg, return rc; } int smblib_set_aicl_cont_threshold(struct smb_chg_param *param, int val_u, u8 *val_raw) { int base = param->min_u; int offset = 0; int step = param->step_u; if (val_u > param->max_u) val_u = param->max_u; if (val_u < param->min_u) val_u = param->min_u; if (val_u >= AICL_RANGE2_MIN_MV) { base = AICL_RANGE2_MIN_MV; step = AICL_RANGE2_STEP_DELTA_MV; offset = AICL_RANGE2_OFFSET; }; *val_raw = ((val_u - base) / step) + offset; return 0; } /******************** * HELPER FUNCTIONS * ********************/ Loading Loading @@ -1079,6 +1122,11 @@ static void smblib_uusb_removal(struct smb_charger *chg) smblib_err(chg, "Couldn't set USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V rc=%d\n", rc); /* reset USBOV votes and cancel work */ cancel_delayed_work_sync(&chg->usbov_dbc_work); vote(chg->awake_votable, USBOV_DBC_VOTER, false, 0); chg->dbc_usbov = false; chg->voltage_min_uv = MICRO_5V; chg->voltage_max_uv = MICRO_5V; chg->usb_icl_delta_ua = 0; Loading Loading @@ -1313,8 +1361,7 @@ int smblib_set_icl_current(struct smb_charger *chg, int icl_ua) /* Re-run AICL */ if (icl_override != SW_OVERRIDE_HC_MODE) rc = smblib_rerun_aicl(chg); rc = smblib_run_aicl(chg, RERUN_AICL); out: return rc; } Loading Loading @@ -1703,7 +1750,32 @@ int smblib_get_prop_batt_status(struct smb_charger *chg, union power_supply_propval pval = {0, }; bool usb_online, dc_online; u8 stat; int rc; int rc, suspend = 0; if (chg->dbc_usbov) { rc = smblib_get_prop_usb_present(chg, &pval); if (rc < 0) { smblib_err(chg, "Couldn't get usb present prop rc=%d\n", rc); return rc; } rc = smblib_get_usb_suspend(chg, &suspend); if (rc < 0) { smblib_err(chg, "Couldn't get usb suspend rc=%d\n", rc); return rc; } /* * Report charging as long as USBOV is not debounced and * charging path is un-suspended. */ if (pval.intval && !suspend) { val->intval = POWER_SUPPLY_STATUS_CHARGING; return 0; } } rc = smblib_get_prop_usb_online(chg, &pval); if (rc < 0) { Loading Loading @@ -1946,7 +2018,14 @@ int smblib_get_prop_batt_iterm(struct smb_charger *chg, temp = buf[1] | (buf[0] << 8); temp = sign_extend32(temp, 15); temp = DIV_ROUND_CLOSEST(temp * 10000, ADC_CHG_TERM_MASK); if (chg->smb_version == PMI632_SUBTYPE) temp = DIV_ROUND_CLOSEST(temp * ITERM_LIMITS_PMI632_MA, ADC_CHG_ITERM_MASK); else temp = DIV_ROUND_CLOSEST(temp * ITERM_LIMITS_PM8150B_MA, ADC_CHG_ITERM_MASK); val->intval = temp; return rc; Loading Loading @@ -2075,7 +2154,7 @@ int smblib_set_prop_rechg_soc_thresh(struct smb_charger *chg, return rc; } int smblib_rerun_aicl(struct smb_charger *chg) int smblib_run_aicl(struct smb_charger *chg, int type) { int rc; u8 stat; Loading @@ -2093,8 +2172,8 @@ int smblib_rerun_aicl(struct smb_charger *chg) smblib_dbg(chg, PR_MISC, "re-running AICL\n"); rc = smblib_masked_write(chg, AICL_CMD_REG, RERUN_AICL_BIT, RERUN_AICL_BIT); stat = (type == RERUN_AICL) ? RERUN_AICL_BIT : RESTART_AICL_BIT; rc = smblib_masked_write(chg, AICL_CMD_REG, stat, stat); if (rc < 0) smblib_err(chg, "Couldn't write to AICL_CMD_REG rc=%d\n", rc); Loading Loading @@ -4092,6 +4171,8 @@ irqreturn_t batt_psy_changed_irq_handler(int irq, void *data) return IRQ_HANDLED; } #define AICL_STEP_MV 200 #define MAX_AICL_THRESHOLD_MV 4800 irqreturn_t usbin_uv_irq_handler(int irq, void *data) { struct smb_irq_data *irq_data = data; Loading @@ -4102,6 +4183,70 @@ irqreturn_t usbin_uv_irq_handler(int irq, void *data) u8 stat = 0, max_pulses = 0; smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s\n", irq_data->name); if ((chg->wa_flags & WEAK_ADAPTER_WA) && is_storming(&irq_data->storm_data)) { if (chg->aicl_max_reached) { smblib_dbg(chg, PR_MISC, "USBIN_UV storm at max AICL threshold\n"); return IRQ_HANDLED; } smblib_dbg(chg, PR_MISC, "USBIN_UV storm at threshold %d\n", chg->aicl_5v_threshold_mv); /* suspend USBIN before updating AICL threshold */ vote(chg->usb_icl_votable, AICL_THRESHOLD_VOTER, true, 0); /* delay for VASHDN deglitch */ msleep(20); if (chg->aicl_5v_threshold_mv > MAX_AICL_THRESHOLD_MV) { /* reached max AICL threshold */ chg->aicl_max_reached = true; goto unsuspend_input; } /* Increase AICL threshold by 200mV */ rc = smblib_set_charge_param(chg, &chg->param.aicl_5v_threshold, chg->aicl_5v_threshold_mv + AICL_STEP_MV); if (rc < 0) dev_err(chg->dev, "Error in setting AICL threshold rc=%d\n", rc); else chg->aicl_5v_threshold_mv += AICL_STEP_MV; rc = smblib_set_charge_param(chg, &chg->param.aicl_cont_threshold, chg->aicl_cont_threshold_mv + AICL_STEP_MV); if (rc < 0) dev_err(chg->dev, "Error in setting AICL threshold rc=%d\n", rc); else chg->aicl_cont_threshold_mv += AICL_STEP_MV; unsuspend_input: /* Force torch in boost mode to ensure it works with low ICL */ if (chg->smb_version == PMI632_SUBTYPE) schgm_flash_torch_priority(chg, TORCH_BOOST_MODE); if (chg->aicl_max_reached) { smblib_dbg(chg, PR_MISC, "Reached max AICL threshold resctricting ICL to 100mA\n"); vote(chg->usb_icl_votable, AICL_THRESHOLD_VOTER, true, USBIN_100MA); smblib_run_aicl(chg, RESTART_AICL); } else { smblib_run_aicl(chg, RESTART_AICL); vote(chg->usb_icl_votable, AICL_THRESHOLD_VOTER, false, 0); } wdata = &chg->irq_info[USBIN_UV_IRQ].irq_data->storm_data; reset_storm_count(wdata); } if (!chg->irq_info[SWITCHER_POWER_OK_IRQ].irq_data) return IRQ_HANDLED; Loading Loading @@ -4170,6 +4315,24 @@ irqreturn_t icl_change_irq_handler(int irq, void *data) struct smb_charger *chg = irq_data->parent_data; if (chg->mode == PARALLEL_MASTER) { /* * Ignore if change in ICL is due to DIE temp mitigation. * This is to prevent any further ICL split. */ if (chg->hw_die_temp_mitigation) { rc = smblib_read(chg, DIE_TEMP_STATUS_REG, &stat); if (rc < 0) { smblib_err(chg, "Couldn't read DIE_TEMP rc=%d\n", rc); return IRQ_HANDLED; } if (stat & (DIE_TEMP_UB_BIT | DIE_TEMP_LB_BIT)) { smblib_dbg(chg, PR_PARALLEL, "skip ICL change DIE_TEMP %x\n", stat); return IRQ_HANDLED; } } rc = smblib_read(chg, AICL_STATUS_REG, &stat); if (rc < 0) { smblib_err(chg, "Couldn't read AICL_STATUS rc=%d\n", Loading Loading @@ -4313,6 +4476,33 @@ void smblib_usb_plugin_locked(struct smb_charger *chg) vote(chg->fcc_votable, FCC_STEPPER_VOTER, true, 1500000); if (chg->wa_flags & WEAK_ADAPTER_WA) { chg->aicl_5v_threshold_mv = chg->default_aicl_5v_threshold_mv; chg->aicl_cont_threshold_mv = chg->default_aicl_cont_threshold_mv; smblib_set_charge_param(chg, &chg->param.aicl_5v_threshold, chg->aicl_5v_threshold_mv); smblib_set_charge_param(chg, &chg->param.aicl_cont_threshold, chg->aicl_cont_threshold_mv); chg->aicl_max_reached = false; if (chg->smb_version == PMI632_SUBTYPE) schgm_flash_torch_priority(chg, TORCH_BUCK_MODE); data = chg->irq_info[USBIN_UV_IRQ].irq_data; if (data) { wdata = &data->storm_data; reset_storm_count(wdata); } vote(chg->usb_icl_votable, AICL_THRESHOLD_VOTER, false, 0); } rc = smblib_request_dpdm(chg, false); if (rc < 0) smblib_err(chg, "Couldn't disable DPDM rc=%d\n", rc); Loading Loading @@ -4770,6 +4960,11 @@ static void typec_src_removal(struct smb_charger *chg) vote(chg->cp_disable_votable, SW_THERM_REGULATION_VOTER, false, 0); /* reset USBOV votes and cancel work */ cancel_delayed_work_sync(&chg->usbov_dbc_work); vote(chg->awake_votable, USBOV_DBC_VOTER, false, 0); chg->dbc_usbov = false; chg->pulse_cnt = 0; chg->usb_icl_delta_ua = 0; chg->voltage_min_uv = MICRO_5V; Loading Loading @@ -5218,6 +5413,57 @@ irqreturn_t temp_change_irq_handler(int irq, void *data) return IRQ_HANDLED; } static void smblib_usbov_dbc_work(struct work_struct *work) { struct smb_charger *chg = container_of(work, struct smb_charger, usbov_dbc_work.work); smblib_dbg(chg, PR_MISC, "Resetting USBOV debounce\n"); chg->dbc_usbov = false; vote(chg->awake_votable, USBOV_DBC_VOTER, false, 0); } #define USB_OV_DBC_PERIOD_MS 1000 irqreturn_t usbin_ov_irq_handler(int irq, void *data) { struct smb_irq_data *irq_data = data; struct smb_charger *chg = irq_data->parent_data; u8 stat; int rc; smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s\n", irq_data->name); if (!(chg->wa_flags & USBIN_OV_WA)) return IRQ_HANDLED; rc = smblib_read(chg, USBIN_BASE + INT_RT_STS_OFFSET, &stat); if (rc < 0) { smblib_err(chg, "Couldn't read USB_INT_RT_STS rc=%d\n", rc); return IRQ_HANDLED; } /* * On specific PMICs, OV IRQ triggers for very small duration in * interim periods affecting charging status reflection. In order to * differentiate between OV IRQ glitch and real OV_IRQ, add a debounce * period for evaluation. */ if (stat & USBIN_OV_RT_STS_BIT) { chg->dbc_usbov = true; vote(chg->awake_votable, USBOV_DBC_VOTER, true, 0); schedule_delayed_work(&chg->usbov_dbc_work, msecs_to_jiffies(USB_OV_DBC_PERIOD_MS)); } else { cancel_delayed_work_sync(&chg->usbov_dbc_work); chg->dbc_usbov = false; vote(chg->awake_votable, USBOV_DBC_VOTER, false, 0); } smblib_dbg(chg, PR_MISC, "USBOV debounce status %d\n", chg->dbc_usbov); return IRQ_HANDLED; } /************** * Additional USB PSY getters/setters * that call interrupt functions Loading Loading @@ -5885,6 +6131,7 @@ int smblib_init(struct smb_charger *chg) INIT_DELAYED_WORK(&chg->lpd_detach_work, smblib_lpd_detach_work); INIT_DELAYED_WORK(&chg->thermal_regulation_work, smblib_thermal_regulation_work); INIT_DELAYED_WORK(&chg->usbov_dbc_work, smblib_usbov_dbc_work); if (chg->uusb_moisture_protection_enabled) { INIT_WORK(&chg->moisture_protection_work, Loading Loading @@ -6006,6 +6253,7 @@ int smblib_deinit(struct smb_charger *chg) cancel_delayed_work_sync(&chg->lpd_ra_open_work); cancel_delayed_work_sync(&chg->lpd_detach_work); cancel_delayed_work_sync(&chg->thermal_regulation_work); cancel_delayed_work_sync(&chg->usbov_dbc_work); power_supply_unreg_notifier(&chg->nb); smblib_destroy_votables(chg); qcom_step_chg_deinit(); Loading