Loading Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb5.txt +7 −0 Original line number Diff line number Diff line Loading @@ -350,6 +350,13 @@ Charger specific properties: Value type: bool Definition: Boolean flag which when present disables FCC restriction. - qcom,wls-current-max-ua Usage: optional Value type: <u32> Definition: Upper limit of charging current supplied by the wireless charger. If left unspecified, the HW min value of 1.5 A is applied by default. ============================================= Second Level Nodes - SMB5 Charger Peripherals ============================================= Loading drivers/power/supply/qcom/qpnp-smb5.c +15 −9 Original line number Diff line number Diff line Loading @@ -160,7 +160,7 @@ static struct smb_params smb5_pm8150b_params = { .name = "DC input current limit", .reg = DCDC_CFG_REF_MAX_PSNS_REG, .min_u = 0, .max_u = 1500000, .max_u = DCIN_ICL_MAX_UA, .step_u = 50000, }, .jeita_cc_comp_hot = { Loading Loading @@ -396,7 +396,7 @@ static int smb5_parse_dt(struct smb5 *chip) { struct smb_charger *chg = &chip->chg; struct device_node *node = chg->dev->of_node; int rc, byte_len; int rc, byte_len, tmp; if (!node) { pr_err("device tree node missing\n"); Loading Loading @@ -653,6 +653,12 @@ static int smb5_parse_dt(struct smb5 *chip) if (chg->chg_param.hvdcp3_max_icl_ua <= 0) chg->chg_param.hvdcp3_max_icl_ua = MICRO_3PA; chg->wls_icl_ua = DCIN_ICL_MAX_UA; rc = of_property_read_u32(node, "qcom,wls-current-max-ua", &tmp); if (!rc && tmp < DCIN_ICL_MAX_UA) chg->wls_icl_ua = tmp; return 0; } Loading Loading @@ -1468,15 +1474,14 @@ static int smb5_dc_set_prop(struct power_supply *psy, static int smb5_dc_prop_is_writeable(struct power_supply *psy, enum power_supply_property psp) { int rc; switch (psp) { case POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION: return 1; default: rc = 0; break; } return rc; return 0; } static const struct power_supply_desc dc_psy_desc = { Loading Loading @@ -2458,8 +2463,8 @@ static int smb5_init_dc_peripheral(struct smb_charger *chg) if (chg->chg_param.smb_version == PMI632_SUBTYPE) return 0; /* set DC icl_max 1A */ rc = smblib_set_charge_param(chg, &chg->param.dc_icl, 1000000); /* Set DCIN ICL to 100 mA */ rc = smblib_set_charge_param(chg, &chg->param.dc_icl, DCIN_ICL_MIN_UA); if (rc < 0) { dev_err(chg->dev, "Couldn't set dc_icl rc=%d\n", rc); return rc; Loading Loading @@ -3096,7 +3101,8 @@ static struct smb_irq_info smb5_irqs[] = { }, [DCIN_UV_IRQ] = { .name = "dcin-uv", .handler = default_irq_handler, .handler = dcin_uv_irq_handler, .wake = true, }, [DCIN_OV_IRQ] = { .name = "dcin-ov", Loading drivers/power/supply/qcom/smb5-lib.c +192 −3 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ #include <linux/iio/consumer.h> #include <linux/pmic-voter.h> #include <linux/of_batterydata.h> #include <linux/ktime.h> #include "smb5-lib.h" #include "smb5-reg.h" #include "schgm-flash.h" Loading Loading @@ -2982,12 +2983,15 @@ int smblib_get_prop_dc_voltage_now(struct smb_charger *chg, rc = power_supply_get_property(chg->wls_psy, POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION, val); if (rc < 0) if (rc < 0) { dev_err(chg->dev, "Couldn't get POWER_SUPPLY_PROP_VOLTAGE_REGULATION, rc=%d\n", rc); return rc; } return rc; } /******************* * DC PSY SETTERS * *******************/ Loading @@ -2998,6 +3002,7 @@ int smblib_set_prop_dc_current_max(struct smb_charger *chg, return smblib_set_charge_param(chg, &chg->param.dc_icl, val->intval); } #define DCIN_AICL_RERUN_DELAY_MS 5000 int smblib_set_prop_voltage_wls_output(struct smb_charger *chg, const union power_supply_propval *val) { Loading @@ -3016,7 +3021,19 @@ int smblib_set_prop_voltage_wls_output(struct smb_charger *chg, dev_err(chg->dev, "Couldn't set POWER_SUPPLY_PROP_VOLTAGE_REGULATION, rc=%d\n", rc); smblib_dbg(chg, PR_WLS, "Set WLS output voltage %d\n", val->intval); smblib_dbg(chg, PR_WLS, "%d\n", val->intval); /* * When WLS VOUT goes down, the power-constrained adaptor may be able * to supply more current, so allow it to do so. */ if ((val->intval > 0) && (val->intval < chg->last_wls_vout)) { /* Rerun AICL once after 10 s */ alarm_start_relative(&chg->dcin_aicl_alarm, ms_to_ktime(DCIN_AICL_RERUN_DELAY_MS)); } chg->last_wls_vout = val->intval; return rc; } Loading Loading @@ -5914,6 +5931,151 @@ irqreturn_t typec_attach_detach_irq_handler(int irq, void *data) return IRQ_HANDLED; } static void dcin_aicl(struct smb_charger *chg) { int rc, icl, icl_save; int input_present; /* * Hold awake votable to prevent pm_relax being called prior to * completion of this work. */ vote(chg->awake_votable, DCIN_AICL_VOTER, true, 0); increment: mutex_lock(&chg->dcin_aicl_lock); rc = smblib_get_charge_param(chg, &chg->param.dc_icl, &icl); if (rc < 0) goto unlock; if (icl == chg->wls_icl_ua) { /* Upper limit reached; do nothing */ smblib_dbg(chg, PR_WLS, "hit max ICL: stop\n"); goto unlock; } icl = min(chg->wls_icl_ua, icl + DCIN_ICL_STEP_UA); icl_save = icl; rc = smblib_set_charge_param(chg, &chg->param.dc_icl, icl); if (rc < 0) goto unlock; mutex_unlock(&chg->dcin_aicl_lock); smblib_dbg(chg, PR_WLS, "icl: %d mA\n", (icl / 1000)); /* Check to see if DC is still present before and after sleep */ rc = smblib_is_input_present(chg, &input_present); if (!(input_present & INPUT_PRESENT_DC) || rc < 0) goto unvote; /* * Wait awhile to check for any DCIN_UVs (the UV handler reduces the * ICL). If the adaptor collapses, the ICL read after waking up will be * lesser, indicating that the AICL process is complete. */ msleep(500); rc = smblib_is_input_present(chg, &input_present); if (!(input_present & INPUT_PRESENT_DC) || rc < 0) goto unvote; mutex_lock(&chg->dcin_aicl_lock); rc = smblib_get_charge_param(chg, &chg->param.dc_icl, &icl); if (rc < 0) goto unlock; if (icl < icl_save) { smblib_dbg(chg, PR_WLS, "done: icl: %d mA\n", (icl / 1000)); goto unlock; } mutex_unlock(&chg->dcin_aicl_lock); goto increment; unlock: mutex_unlock(&chg->dcin_aicl_lock); unvote: vote(chg->awake_votable, DCIN_AICL_VOTER, false, 0); } static void dcin_aicl_work(struct work_struct *work) { struct smb_charger *chg = container_of(work, struct smb_charger, dcin_aicl_work); dcin_aicl(chg); } static enum alarmtimer_restart dcin_aicl_alarm_cb(struct alarm *alarm, ktime_t now) { struct smb_charger *chg = container_of(alarm, struct smb_charger, dcin_aicl_alarm); smblib_dbg(chg, PR_WLS, "rerunning DCIN AICL\n"); pm_stay_awake(chg->dev); schedule_work(&chg->dcin_aicl_work); return ALARMTIMER_NORESTART; } static void dcin_icl_decrement(struct smb_charger *chg) { int rc, icl; ktime_t now = ktime_get(); rc = smblib_get_charge_param(chg, &chg->param.dc_icl, &icl); if (rc < 0) { smblib_err(chg, "reading DCIN ICL failed: %d\n", rc); return; } if (icl == DCIN_ICL_MIN_UA) { /* Cannot possibly decrease ICL any further - do nothing */ smblib_dbg(chg, PR_WLS, "hit min ICL: stop\n"); return; } /* Reduce ICL by 100 mA if 3 UVs happen in a row */ if (ktime_us_delta(now, chg->dcin_uv_last_time) > (200 * 1000)) { chg->dcin_uv_count = 0; } else if (chg->dcin_uv_count == 3) { icl -= DCIN_ICL_STEP_UA; smblib_dbg(chg, PR_WLS, "icl: %d mA\n", (icl / 1000)); rc = smblib_set_charge_param(chg, &chg->param.dc_icl, icl); if (rc < 0) { smblib_err(chg, "setting DCIN ICL failed: %d\n", rc); return; } chg->dcin_uv_count = 0; } chg->dcin_uv_last_time = now; } irqreturn_t dcin_uv_irq_handler(int irq, void *data) { struct smb_irq_data *irq_data = data; struct smb_charger *chg = irq_data->parent_data; mutex_lock(&chg->dcin_aicl_lock); chg->dcin_uv_count++; smblib_dbg(chg, (PR_WLS | PR_INTERRUPT), "DCIN UV count: %d\n", chg->dcin_uv_count); dcin_icl_decrement(chg); mutex_unlock(&chg->dcin_aicl_lock); return IRQ_HANDLED; } irqreturn_t dc_plugin_irq_handler(int irq, void *data) { struct smb_irq_data *irq_data = data; Loading @@ -5939,6 +6101,18 @@ irqreturn_t dc_plugin_irq_handler(int irq, void *data) vbus_present = input_present & INPUT_PRESENT_USB; if (dcin_present && !vbus_present) { cancel_work_sync(&chg->dcin_aicl_work); /* Reset DCIN ICL to 100 mA */ mutex_lock(&chg->dcin_aicl_lock); rc = smblib_set_charge_param(chg, &chg->param.dc_icl, DCIN_ICL_MIN_UA); mutex_unlock(&chg->dcin_aicl_lock); if (rc < 0) return IRQ_HANDLED; smblib_dbg(chg, (PR_WLS | PR_INTERRUPT), "reset: icl: 100 mA\n"); if (chg->sec_cp_present) { pval.intval = wireless_vout; rc = smblib_set_prop_voltage_wls_output(chg, &pval); Loading @@ -5963,6 +6137,8 @@ irqreturn_t dc_plugin_irq_handler(int irq, void *data) dev_err(chg->dev, "Couldn't set dc voltage to 5 V rc=%d\n", rc); } schedule_work(&chg->dcin_aicl_work); } else { if (chg->cp_reason == POWER_SUPPLY_CP_WIRELESS) { sec_charger = chg->sec_pl_present ? Loading @@ -5976,11 +6152,13 @@ irqreturn_t dc_plugin_irq_handler(int irq, void *data) } vote(chg->dc_suspend_votable, CHG_TERMINATION_VOTER, false, 0); chg->last_wls_vout = 0; } power_supply_changed(chg->dc_psy); smblib_dbg(chg, PR_WLS, "dcin_present= %d, usbin_present= %d, cp_reason = %d\n", smblib_dbg(chg, (PR_WLS | PR_INTERRUPT), "dcin_present= %d, usbin_present= %d, cp_reason = %d\n", dcin_present, vbus_present, chg->cp_reason); return IRQ_HANDLED; Loading Loading @@ -7167,9 +7345,11 @@ int smblib_init(struct smb_charger *chg) mutex_init(&chg->smb_lock); mutex_init(&chg->irq_status_lock); spin_lock_init(&chg->typec_pr_lock); mutex_init(&chg->dcin_aicl_lock); INIT_WORK(&chg->bms_update_work, bms_update_work); INIT_WORK(&chg->pl_update_work, pl_update_work); INIT_WORK(&chg->jeita_update_work, jeita_update_work); INIT_WORK(&chg->dcin_aicl_work, dcin_aicl_work); INIT_DELAYED_WORK(&chg->clear_hdc_work, clear_hdc_work); INIT_DELAYED_WORK(&chg->icl_change_work, smblib_icl_change_work); INIT_DELAYED_WORK(&chg->pl_enable_work, smblib_pl_enable_work); Loading Loading @@ -7213,6 +7393,14 @@ int smblib_init(struct smb_charger *chg) } } if (alarmtimer_get_rtcdev()) { alarm_init(&chg->dcin_aicl_alarm, ALARM_REALTIME, dcin_aicl_alarm_cb); } else { smblib_err(chg, "Failed to initialize dcin aicl alarm\n"); return -ENODEV; } chg->fake_capacity = -EINVAL; chg->fake_input_current_limited = -EINVAL; chg->fake_batt_status = -EINVAL; Loading Loading @@ -7318,6 +7506,7 @@ int smblib_deinit(struct smb_charger *chg) cancel_work_sync(&chg->bms_update_work); cancel_work_sync(&chg->jeita_update_work); cancel_work_sync(&chg->pl_update_work); cancel_work_sync(&chg->dcin_aicl_work); cancel_delayed_work_sync(&chg->clear_hdc_work); cancel_delayed_work_sync(&chg->icl_change_work); cancel_delayed_work_sync(&chg->pl_enable_work); Loading drivers/power/supply/qcom/smb5-lib.h +12 −1 Original line number Diff line number Diff line Loading @@ -83,6 +83,7 @@ enum print_reason { #define DETACH_DETECT_VOTER "DETACH_DETECT_VOTER" #define CC_MODE_VOTER "CC_MODE_VOTER" #define MAIN_FCC_VOTER "MAIN_FCC_VOTER" #define DCIN_AICL_VOTER "DCIN_AICL_VOTER" #define BOOST_BACK_STORM_COUNT 3 #define WEAK_CHG_STORM_COUNT 8 Loading @@ -101,6 +102,9 @@ enum print_reason { #define TYPEC_DEFAULT_CURRENT_UA 900000 #define TYPEC_MEDIUM_CURRENT_UA 1500000 #define TYPEC_HIGH_CURRENT_UA 3000000 #define DCIN_ICL_MIN_UA 100000 #define DCIN_ICL_MAX_UA 1500000 #define DCIN_ICL_STEP_UA 100000 #define ROLE_REVERSAL_DELAY_MS 2000 Loading Loading @@ -391,6 +395,7 @@ struct smb_charger { struct mutex dr_lock; struct mutex irq_status_lock; spinlock_t typec_pr_lock; struct mutex dcin_aicl_lock; /* power supplies */ struct power_supply *batt_psy; Loading Loading @@ -443,6 +448,7 @@ struct smb_charger { struct work_struct jeita_update_work; struct work_struct moisture_protection_work; struct work_struct chg_termination_work; struct work_struct dcin_aicl_work; struct delayed_work ps_change_timeout_work; struct delayed_work clear_hdc_work; struct delayed_work icl_change_work; Loading @@ -460,6 +466,7 @@ struct smb_charger { struct alarm lpd_recheck_timer; struct alarm moisture_protection_alarm; struct alarm chg_termination_alarm; struct alarm dcin_aicl_alarm; struct charger_param chg_param; /* secondary charger config */ Loading Loading @@ -553,6 +560,7 @@ struct smb_charger { int init_thermal_ua; u32 comp_clamp_level; bool hvdcp3_standalone_config; int wls_icl_ua; /* workaround flag */ u32 wa_flags; Loading Loading @@ -583,7 +591,9 @@ struct smb_charger { u32 irq_status; /* wireless */ int wireless_vout; int dcin_uv_count; ktime_t dcin_uv_last_time; int last_wls_vout; }; int smblib_read(struct smb_charger *chg, u16 addr, u8 *val); Loading Loading @@ -633,6 +643,7 @@ irqreturn_t usb_source_change_irq_handler(int irq, void *data); irqreturn_t icl_change_irq_handler(int irq, void *data); irqreturn_t typec_state_change_irq_handler(int irq, void *data); irqreturn_t typec_attach_detach_irq_handler(int irq, void *data); irqreturn_t dcin_uv_irq_handler(int irq, void *data); irqreturn_t dc_plugin_irq_handler(int irq, void *data); irqreturn_t high_duty_cycle_irq_handler(int irq, void *data); irqreturn_t switcher_power_ok_irq_handler(int irq, void *data); Loading Loading
Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb5.txt +7 −0 Original line number Diff line number Diff line Loading @@ -350,6 +350,13 @@ Charger specific properties: Value type: bool Definition: Boolean flag which when present disables FCC restriction. - qcom,wls-current-max-ua Usage: optional Value type: <u32> Definition: Upper limit of charging current supplied by the wireless charger. If left unspecified, the HW min value of 1.5 A is applied by default. ============================================= Second Level Nodes - SMB5 Charger Peripherals ============================================= Loading
drivers/power/supply/qcom/qpnp-smb5.c +15 −9 Original line number Diff line number Diff line Loading @@ -160,7 +160,7 @@ static struct smb_params smb5_pm8150b_params = { .name = "DC input current limit", .reg = DCDC_CFG_REF_MAX_PSNS_REG, .min_u = 0, .max_u = 1500000, .max_u = DCIN_ICL_MAX_UA, .step_u = 50000, }, .jeita_cc_comp_hot = { Loading Loading @@ -396,7 +396,7 @@ static int smb5_parse_dt(struct smb5 *chip) { struct smb_charger *chg = &chip->chg; struct device_node *node = chg->dev->of_node; int rc, byte_len; int rc, byte_len, tmp; if (!node) { pr_err("device tree node missing\n"); Loading Loading @@ -653,6 +653,12 @@ static int smb5_parse_dt(struct smb5 *chip) if (chg->chg_param.hvdcp3_max_icl_ua <= 0) chg->chg_param.hvdcp3_max_icl_ua = MICRO_3PA; chg->wls_icl_ua = DCIN_ICL_MAX_UA; rc = of_property_read_u32(node, "qcom,wls-current-max-ua", &tmp); if (!rc && tmp < DCIN_ICL_MAX_UA) chg->wls_icl_ua = tmp; return 0; } Loading Loading @@ -1468,15 +1474,14 @@ static int smb5_dc_set_prop(struct power_supply *psy, static int smb5_dc_prop_is_writeable(struct power_supply *psy, enum power_supply_property psp) { int rc; switch (psp) { case POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION: return 1; default: rc = 0; break; } return rc; return 0; } static const struct power_supply_desc dc_psy_desc = { Loading Loading @@ -2458,8 +2463,8 @@ static int smb5_init_dc_peripheral(struct smb_charger *chg) if (chg->chg_param.smb_version == PMI632_SUBTYPE) return 0; /* set DC icl_max 1A */ rc = smblib_set_charge_param(chg, &chg->param.dc_icl, 1000000); /* Set DCIN ICL to 100 mA */ rc = smblib_set_charge_param(chg, &chg->param.dc_icl, DCIN_ICL_MIN_UA); if (rc < 0) { dev_err(chg->dev, "Couldn't set dc_icl rc=%d\n", rc); return rc; Loading Loading @@ -3096,7 +3101,8 @@ static struct smb_irq_info smb5_irqs[] = { }, [DCIN_UV_IRQ] = { .name = "dcin-uv", .handler = default_irq_handler, .handler = dcin_uv_irq_handler, .wake = true, }, [DCIN_OV_IRQ] = { .name = "dcin-ov", Loading
drivers/power/supply/qcom/smb5-lib.c +192 −3 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ #include <linux/iio/consumer.h> #include <linux/pmic-voter.h> #include <linux/of_batterydata.h> #include <linux/ktime.h> #include "smb5-lib.h" #include "smb5-reg.h" #include "schgm-flash.h" Loading Loading @@ -2982,12 +2983,15 @@ int smblib_get_prop_dc_voltage_now(struct smb_charger *chg, rc = power_supply_get_property(chg->wls_psy, POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION, val); if (rc < 0) if (rc < 0) { dev_err(chg->dev, "Couldn't get POWER_SUPPLY_PROP_VOLTAGE_REGULATION, rc=%d\n", rc); return rc; } return rc; } /******************* * DC PSY SETTERS * *******************/ Loading @@ -2998,6 +3002,7 @@ int smblib_set_prop_dc_current_max(struct smb_charger *chg, return smblib_set_charge_param(chg, &chg->param.dc_icl, val->intval); } #define DCIN_AICL_RERUN_DELAY_MS 5000 int smblib_set_prop_voltage_wls_output(struct smb_charger *chg, const union power_supply_propval *val) { Loading @@ -3016,7 +3021,19 @@ int smblib_set_prop_voltage_wls_output(struct smb_charger *chg, dev_err(chg->dev, "Couldn't set POWER_SUPPLY_PROP_VOLTAGE_REGULATION, rc=%d\n", rc); smblib_dbg(chg, PR_WLS, "Set WLS output voltage %d\n", val->intval); smblib_dbg(chg, PR_WLS, "%d\n", val->intval); /* * When WLS VOUT goes down, the power-constrained adaptor may be able * to supply more current, so allow it to do so. */ if ((val->intval > 0) && (val->intval < chg->last_wls_vout)) { /* Rerun AICL once after 10 s */ alarm_start_relative(&chg->dcin_aicl_alarm, ms_to_ktime(DCIN_AICL_RERUN_DELAY_MS)); } chg->last_wls_vout = val->intval; return rc; } Loading Loading @@ -5914,6 +5931,151 @@ irqreturn_t typec_attach_detach_irq_handler(int irq, void *data) return IRQ_HANDLED; } static void dcin_aicl(struct smb_charger *chg) { int rc, icl, icl_save; int input_present; /* * Hold awake votable to prevent pm_relax being called prior to * completion of this work. */ vote(chg->awake_votable, DCIN_AICL_VOTER, true, 0); increment: mutex_lock(&chg->dcin_aicl_lock); rc = smblib_get_charge_param(chg, &chg->param.dc_icl, &icl); if (rc < 0) goto unlock; if (icl == chg->wls_icl_ua) { /* Upper limit reached; do nothing */ smblib_dbg(chg, PR_WLS, "hit max ICL: stop\n"); goto unlock; } icl = min(chg->wls_icl_ua, icl + DCIN_ICL_STEP_UA); icl_save = icl; rc = smblib_set_charge_param(chg, &chg->param.dc_icl, icl); if (rc < 0) goto unlock; mutex_unlock(&chg->dcin_aicl_lock); smblib_dbg(chg, PR_WLS, "icl: %d mA\n", (icl / 1000)); /* Check to see if DC is still present before and after sleep */ rc = smblib_is_input_present(chg, &input_present); if (!(input_present & INPUT_PRESENT_DC) || rc < 0) goto unvote; /* * Wait awhile to check for any DCIN_UVs (the UV handler reduces the * ICL). If the adaptor collapses, the ICL read after waking up will be * lesser, indicating that the AICL process is complete. */ msleep(500); rc = smblib_is_input_present(chg, &input_present); if (!(input_present & INPUT_PRESENT_DC) || rc < 0) goto unvote; mutex_lock(&chg->dcin_aicl_lock); rc = smblib_get_charge_param(chg, &chg->param.dc_icl, &icl); if (rc < 0) goto unlock; if (icl < icl_save) { smblib_dbg(chg, PR_WLS, "done: icl: %d mA\n", (icl / 1000)); goto unlock; } mutex_unlock(&chg->dcin_aicl_lock); goto increment; unlock: mutex_unlock(&chg->dcin_aicl_lock); unvote: vote(chg->awake_votable, DCIN_AICL_VOTER, false, 0); } static void dcin_aicl_work(struct work_struct *work) { struct smb_charger *chg = container_of(work, struct smb_charger, dcin_aicl_work); dcin_aicl(chg); } static enum alarmtimer_restart dcin_aicl_alarm_cb(struct alarm *alarm, ktime_t now) { struct smb_charger *chg = container_of(alarm, struct smb_charger, dcin_aicl_alarm); smblib_dbg(chg, PR_WLS, "rerunning DCIN AICL\n"); pm_stay_awake(chg->dev); schedule_work(&chg->dcin_aicl_work); return ALARMTIMER_NORESTART; } static void dcin_icl_decrement(struct smb_charger *chg) { int rc, icl; ktime_t now = ktime_get(); rc = smblib_get_charge_param(chg, &chg->param.dc_icl, &icl); if (rc < 0) { smblib_err(chg, "reading DCIN ICL failed: %d\n", rc); return; } if (icl == DCIN_ICL_MIN_UA) { /* Cannot possibly decrease ICL any further - do nothing */ smblib_dbg(chg, PR_WLS, "hit min ICL: stop\n"); return; } /* Reduce ICL by 100 mA if 3 UVs happen in a row */ if (ktime_us_delta(now, chg->dcin_uv_last_time) > (200 * 1000)) { chg->dcin_uv_count = 0; } else if (chg->dcin_uv_count == 3) { icl -= DCIN_ICL_STEP_UA; smblib_dbg(chg, PR_WLS, "icl: %d mA\n", (icl / 1000)); rc = smblib_set_charge_param(chg, &chg->param.dc_icl, icl); if (rc < 0) { smblib_err(chg, "setting DCIN ICL failed: %d\n", rc); return; } chg->dcin_uv_count = 0; } chg->dcin_uv_last_time = now; } irqreturn_t dcin_uv_irq_handler(int irq, void *data) { struct smb_irq_data *irq_data = data; struct smb_charger *chg = irq_data->parent_data; mutex_lock(&chg->dcin_aicl_lock); chg->dcin_uv_count++; smblib_dbg(chg, (PR_WLS | PR_INTERRUPT), "DCIN UV count: %d\n", chg->dcin_uv_count); dcin_icl_decrement(chg); mutex_unlock(&chg->dcin_aicl_lock); return IRQ_HANDLED; } irqreturn_t dc_plugin_irq_handler(int irq, void *data) { struct smb_irq_data *irq_data = data; Loading @@ -5939,6 +6101,18 @@ irqreturn_t dc_plugin_irq_handler(int irq, void *data) vbus_present = input_present & INPUT_PRESENT_USB; if (dcin_present && !vbus_present) { cancel_work_sync(&chg->dcin_aicl_work); /* Reset DCIN ICL to 100 mA */ mutex_lock(&chg->dcin_aicl_lock); rc = smblib_set_charge_param(chg, &chg->param.dc_icl, DCIN_ICL_MIN_UA); mutex_unlock(&chg->dcin_aicl_lock); if (rc < 0) return IRQ_HANDLED; smblib_dbg(chg, (PR_WLS | PR_INTERRUPT), "reset: icl: 100 mA\n"); if (chg->sec_cp_present) { pval.intval = wireless_vout; rc = smblib_set_prop_voltage_wls_output(chg, &pval); Loading @@ -5963,6 +6137,8 @@ irqreturn_t dc_plugin_irq_handler(int irq, void *data) dev_err(chg->dev, "Couldn't set dc voltage to 5 V rc=%d\n", rc); } schedule_work(&chg->dcin_aicl_work); } else { if (chg->cp_reason == POWER_SUPPLY_CP_WIRELESS) { sec_charger = chg->sec_pl_present ? Loading @@ -5976,11 +6152,13 @@ irqreturn_t dc_plugin_irq_handler(int irq, void *data) } vote(chg->dc_suspend_votable, CHG_TERMINATION_VOTER, false, 0); chg->last_wls_vout = 0; } power_supply_changed(chg->dc_psy); smblib_dbg(chg, PR_WLS, "dcin_present= %d, usbin_present= %d, cp_reason = %d\n", smblib_dbg(chg, (PR_WLS | PR_INTERRUPT), "dcin_present= %d, usbin_present= %d, cp_reason = %d\n", dcin_present, vbus_present, chg->cp_reason); return IRQ_HANDLED; Loading Loading @@ -7167,9 +7345,11 @@ int smblib_init(struct smb_charger *chg) mutex_init(&chg->smb_lock); mutex_init(&chg->irq_status_lock); spin_lock_init(&chg->typec_pr_lock); mutex_init(&chg->dcin_aicl_lock); INIT_WORK(&chg->bms_update_work, bms_update_work); INIT_WORK(&chg->pl_update_work, pl_update_work); INIT_WORK(&chg->jeita_update_work, jeita_update_work); INIT_WORK(&chg->dcin_aicl_work, dcin_aicl_work); INIT_DELAYED_WORK(&chg->clear_hdc_work, clear_hdc_work); INIT_DELAYED_WORK(&chg->icl_change_work, smblib_icl_change_work); INIT_DELAYED_WORK(&chg->pl_enable_work, smblib_pl_enable_work); Loading Loading @@ -7213,6 +7393,14 @@ int smblib_init(struct smb_charger *chg) } } if (alarmtimer_get_rtcdev()) { alarm_init(&chg->dcin_aicl_alarm, ALARM_REALTIME, dcin_aicl_alarm_cb); } else { smblib_err(chg, "Failed to initialize dcin aicl alarm\n"); return -ENODEV; } chg->fake_capacity = -EINVAL; chg->fake_input_current_limited = -EINVAL; chg->fake_batt_status = -EINVAL; Loading Loading @@ -7318,6 +7506,7 @@ int smblib_deinit(struct smb_charger *chg) cancel_work_sync(&chg->bms_update_work); cancel_work_sync(&chg->jeita_update_work); cancel_work_sync(&chg->pl_update_work); cancel_work_sync(&chg->dcin_aicl_work); cancel_delayed_work_sync(&chg->clear_hdc_work); cancel_delayed_work_sync(&chg->icl_change_work); cancel_delayed_work_sync(&chg->pl_enable_work); Loading
drivers/power/supply/qcom/smb5-lib.h +12 −1 Original line number Diff line number Diff line Loading @@ -83,6 +83,7 @@ enum print_reason { #define DETACH_DETECT_VOTER "DETACH_DETECT_VOTER" #define CC_MODE_VOTER "CC_MODE_VOTER" #define MAIN_FCC_VOTER "MAIN_FCC_VOTER" #define DCIN_AICL_VOTER "DCIN_AICL_VOTER" #define BOOST_BACK_STORM_COUNT 3 #define WEAK_CHG_STORM_COUNT 8 Loading @@ -101,6 +102,9 @@ enum print_reason { #define TYPEC_DEFAULT_CURRENT_UA 900000 #define TYPEC_MEDIUM_CURRENT_UA 1500000 #define TYPEC_HIGH_CURRENT_UA 3000000 #define DCIN_ICL_MIN_UA 100000 #define DCIN_ICL_MAX_UA 1500000 #define DCIN_ICL_STEP_UA 100000 #define ROLE_REVERSAL_DELAY_MS 2000 Loading Loading @@ -391,6 +395,7 @@ struct smb_charger { struct mutex dr_lock; struct mutex irq_status_lock; spinlock_t typec_pr_lock; struct mutex dcin_aicl_lock; /* power supplies */ struct power_supply *batt_psy; Loading Loading @@ -443,6 +448,7 @@ struct smb_charger { struct work_struct jeita_update_work; struct work_struct moisture_protection_work; struct work_struct chg_termination_work; struct work_struct dcin_aicl_work; struct delayed_work ps_change_timeout_work; struct delayed_work clear_hdc_work; struct delayed_work icl_change_work; Loading @@ -460,6 +466,7 @@ struct smb_charger { struct alarm lpd_recheck_timer; struct alarm moisture_protection_alarm; struct alarm chg_termination_alarm; struct alarm dcin_aicl_alarm; struct charger_param chg_param; /* secondary charger config */ Loading Loading @@ -553,6 +560,7 @@ struct smb_charger { int init_thermal_ua; u32 comp_clamp_level; bool hvdcp3_standalone_config; int wls_icl_ua; /* workaround flag */ u32 wa_flags; Loading Loading @@ -583,7 +591,9 @@ struct smb_charger { u32 irq_status; /* wireless */ int wireless_vout; int dcin_uv_count; ktime_t dcin_uv_last_time; int last_wls_vout; }; int smblib_read(struct smb_charger *chg, u16 addr, u8 *val); Loading Loading @@ -633,6 +643,7 @@ irqreturn_t usb_source_change_irq_handler(int irq, void *data); irqreturn_t icl_change_irq_handler(int irq, void *data); irqreturn_t typec_state_change_irq_handler(int irq, void *data); irqreturn_t typec_attach_detach_irq_handler(int irq, void *data); irqreturn_t dcin_uv_irq_handler(int irq, void *data); irqreturn_t dc_plugin_irq_handler(int irq, void *data); irqreturn_t high_duty_cycle_irq_handler(int irq, void *data); irqreturn_t switcher_power_ok_irq_handler(int irq, void *data); Loading