Loading Documentation/devicetree/bindings/power/qpnp-smbcharger.txt +4 −0 Original line number Diff line number Diff line Loading @@ -300,6 +300,10 @@ Optional Properties: - qcom,max-pulse-allowed The maximum number of pulses allowed in HVDCP3 mode. It can be used to restrict VBUS to a value. - qcom,use-parallel-aicl A boolean property to indicate that AICL is run on the parallel slave to detect adapter's ICL capacity. Ideally to be used if the required FCC is greater then 3A and if the parallel slave supports AICL. Example: qcom,qpnp-smbcharger { Loading drivers/power/pmic-voter.c +1 −1 Original line number Diff line number Diff line Loading @@ -18,7 +18,7 @@ #include "pmic-voter.h" #define NUM_MAX_CLIENTS 8 #define NUM_MAX_CLIENTS 12 struct client_vote { int state; Loading drivers/power/power_supply_sysfs.c +1 −0 Original line number Diff line number Diff line Loading @@ -248,6 +248,7 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(typec_mode), POWER_SUPPLY_ATTR(allow_hvdcp3), POWER_SUPPLY_ATTR(max_pulse_allowed), POWER_SUPPLY_ATTR(enable_aicl), POWER_SUPPLY_ATTR(soc_reporting_ready), POWER_SUPPLY_ATTR(ignore_false_negative_isense), POWER_SUPPLY_ATTR(enable_jeita_detection), Loading drivers/power/qpnp-smbcharger.c +306 −63 Original line number Diff line number Diff line Loading @@ -63,6 +63,9 @@ struct parallel_usb_cfg { int initial_aicl_ma; ktime_t last_disabled; bool enabled_once; int parallel_aicl_ma; bool use_parallel_aicl; bool parallel_en_in_progress; }; struct ilim_entry { Loading Loading @@ -263,6 +266,7 @@ struct smbchg_chip { struct completion src_det_raised; struct completion usbin_uv_lowered; struct completion usbin_uv_raised; struct completion hvdcp_det_done; int pulse_cnt; struct led_classdev led_cdev; bool skip_usb_notification; Loading Loading @@ -347,6 +351,7 @@ enum icl_voters { SW_AICL_ICL_VOTER, CHG_SUSPEND_WORKAROUND_ICL_VOTER, SHUTDOWN_WORKAROUND_ICL_VOTER, PARALLEL_ICL_VOTER, NUM_ICL_VOTER, }; Loading Loading @@ -2067,6 +2072,106 @@ static int smbchg_get_aicl_level_ma(struct smbchg_chip *chip) return chip->tables.usb_ilim_ma_table[reg]; } static int smbchg_run_parallel_aicl(struct smbchg_chip *chip) { int rc, aicl_ma, fcc_ma, icl_ma; union power_supply_propval pval = {0, }; struct power_supply *parallel_psy = get_parallel_psy(chip); if (!parallel_psy) { pr_err("parallel charger not found\n"); return 0; } rc = power_supply_set_present(parallel_psy, true); if (rc) { pr_err("Unable to set_present for parallel_psy rc=%d\n", rc); return rc; } rc = power_supply_set_voltage_limit(parallel_psy, chip->vfloat_mv); if (rc) { pr_err("Unable to set vfloat for parallel_psy rc=%d\n", rc); return rc; } /* Enable slave AICL */ pval.intval = 1; rc = parallel_psy->set_property(parallel_psy, POWER_SUPPLY_PROP_ENABLE_AICL, &pval); if (rc) { pr_err("Unable to enable AICL on parallel_psy rc=%d\n", rc); return rc; } /* Set max allowable FCC to slave */ fcc_ma = get_effective_result_locked(chip->fcc_votable); pval.intval = fcc_ma * 1000; rc = parallel_psy->set_property(parallel_psy, POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &pval); if (rc) { pr_err("Unable to set FCC for parallel_psy rc=%d\n", rc); goto restore_icl; } /* Get max allowable ICL */ icl_ma = get_effective_result_locked(chip->usb_icl_votable); /* * Force main charger to draw 100mA as the minimal input current * but don't suspend it to keep the slave charger online. * Set effective ICL value to slave charger to use it detecting * adapter's real capability. The 100mA draw in main charger path * will be added on slave's ICL status. */ vote(chip->usb_icl_votable, PARALLEL_ICL_VOTER, true, CURRENT_100_MA); rc = power_supply_set_current_limit(parallel_psy, icl_ma * 1000); if (rc) { pr_err("Unable to set ICL for parallel_psy rc=%d\n", rc); goto restore_icl; } rc = parallel_psy->get_property(parallel_psy, POWER_SUPPLY_PROP_INPUT_CURRENT_MAX, &pval); if (rc) { pr_err("Unable to read AICL from parallel_psy rc=%d\n", rc); goto restore_icl; } aicl_ma = pval.intval / 1000; if (aicl_ma == 0) { pr_err("Parallel_psy ICL status is 0mA\n"); goto restore_icl; } chip->parallel.parallel_aicl_ma = min(icl_ma, CURRENT_100_MA + aicl_ma); pr_smb(PR_STATUS, "parallel AICL = %d mA\n", chip->parallel.parallel_aicl_ma); restore_icl: /* Disable slave AICL */ pval.intval = 0; rc = parallel_psy->set_property(parallel_psy, POWER_SUPPLY_PROP_ENABLE_AICL, &pval); if (rc) pr_err("Unable to disable AICL on parallel_psy rc=%d\n", rc); /* Suspend slave and set back last ICL value for main charger */ pval.intval = 1; rc = parallel_psy->set_property(parallel_psy, POWER_SUPPLY_PROP_INPUT_SUSPEND, &pval); if (rc) pr_err("Unable to suspend-input to parallel_psy rc=%d\n", rc); vote(chip->usb_icl_votable, PARALLEL_ICL_VOTER, false, 0); return rc; } static void smbchg_parallel_usb_disable(struct smbchg_chip *chip) { struct power_supply *parallel_psy = get_parallel_psy(chip); Loading @@ -2078,6 +2183,7 @@ static void smbchg_parallel_usb_disable(struct smbchg_chip *chip) taper_irq_en(chip, false); chip->parallel.initial_aicl_ma = 0; chip->parallel.current_max_ma = 0; chip->parallel.parallel_aicl_ma = 0; power_supply_set_current_limit(parallel_psy, SUSPEND_CURRENT_MA * 1000); power_supply_set_present(parallel_psy, false); Loading Loading @@ -2353,6 +2459,19 @@ static bool smbchg_is_parallel_usb_ok(struct smbchg_chip *chip, if (parallel_cl_ma <= SUSPEND_CURRENT_MA) parallel_cl_ma = 0; if (chip->parallel.use_parallel_aicl) { rc = smbchg_run_parallel_aicl(chip); if (rc) { pr_err("Failed to run parallel AICL rc=%d\n", rc); return false; } total_current_ma = chip->parallel.parallel_aicl_ma; pr_smb(PR_STATUS, "use parallel AICL result: %dmA\n", total_current_ma); *ret_total_current_ma = total_current_ma; return true; } /* * Set the parallel charge path's input current limit (ICL) * to the total current / 2 Loading @@ -2374,6 +2493,7 @@ static bool smbchg_is_parallel_usb_ok(struct smbchg_chip *chip, return true; } #define HVDCP_DETECTION_DONE_MS 5000 #define PARALLEL_CHARGER_EN_DELAY_MS 500 static void smbchg_parallel_usb_en_work(struct work_struct *work) { Loading @@ -2382,6 +2502,37 @@ static void smbchg_parallel_usb_en_work(struct work_struct *work) parallel_en_work.work); int previous_aicl_ma, total_current_ma, aicl_ma; bool in_progress; int rc, tries = 3; pr_smb(PR_MISC, "started\n"); chip->parallel.parallel_en_in_progress = true; /* * If use-parallel-aicl is enabled, we run parallel AICL to detect * the total input current from the adapter which could be split and * allotted to main and slave chargers. Running parallel AICL would * cost ~2 seconds which is the same delay for hvdcp detection. This * would cause the concurrence of parallel charging checking and * hvdcp detection. * The concurrence would result a mess on the parallel charger settings * because both logics manipulate ICL setting as well as the parallel * enabling/disabling. * Wait here before parallel charging logic running, until hvdcp/ * hvdcp3 detection logic has finished/confirmed the charger type * detection, to provide a stable ICL value for parallel charging * splitting logic. */ if (chip->parallel.use_parallel_aicl) { while (tries--) { rc = wait_for_completion_interruptible_timeout( &chip->hvdcp_det_done, msecs_to_jiffies(HVDCP_DETECTION_DONE_MS)); if (rc >= 0) { pr_smb(PR_STATUS, "hvdcp detection done\n"); break; } pr_smb(PR_STATUS, "wait hvdcp_det_done interrupted\n"); } } /* do a check to see if the aicl is stable */ previous_aicl_ma = smbchg_get_aicl_level_ma(chip); Loading @@ -2408,10 +2559,14 @@ static void smbchg_parallel_usb_en_work(struct work_struct *work) } } mutex_unlock(&chip->parallel.lock); pr_smb(PR_STATUS, "Parallel check complete!\n"); chip->parallel.parallel_en_in_progress = false; smbchg_relax(chip, PM_PARALLEL_CHECK); return; recheck: chip->parallel.parallel_en_in_progress = false; schedule_delayed_work(&chip->parallel_en_work, 0); } Loading @@ -2422,6 +2577,11 @@ static void smbchg_parallel_usb_check_ok(struct smbchg_chip *chip) if (!parallel_psy || !chip->parallel_charger_detected) return; if (chip->parallel.parallel_en_in_progress) { pr_smb(PR_MISC, "parallel logic run in progress, ignore\n"); return; } smbchg_stay_awake(chip, PM_PARALLEL_CHECK); schedule_delayed_work(&chip->parallel_en_work, 0); } Loading Loading @@ -2825,14 +2985,17 @@ static int set_usb_current_limit_vote_cb(struct device *dev, } } /* skip the aicl rerun if hvdcp icl voter is active */ if (effective_id == HVDCP_ICL_VOTER) /* skip the aicl rerun if hvdcp icl voter or parallel voter is active */ if (effective_id == HVDCP_ICL_VOTER || effective_id == PARALLEL_ICL_VOTER) return 0; aicl_ma = smbchg_get_aicl_level_ma(chip); if (icl_ma > aicl_ma) smbchg_rerun_aicl(chip); smbchg_parallel_usb_check_ok(chip); return 0; } Loading Loading @@ -3719,65 +3882,6 @@ static void check_battery_type(struct smbchg_chip *chip) } } static void smbchg_external_power_changed(struct power_supply *psy) { struct smbchg_chip *chip = container_of(psy, struct smbchg_chip, batt_psy); union power_supply_propval prop = {0,}; int rc, current_limit = 0, soc; enum power_supply_type usb_supply_type; char *usb_type_name = "null"; if (chip->bms_psy_name) chip->bms_psy = power_supply_get_by_name((char *)chip->bms_psy_name); smbchg_aicl_deglitch_wa_check(chip); if (chip->bms_psy) { check_battery_type(chip); soc = get_prop_batt_capacity(chip); if (chip->previous_soc != soc) { chip->previous_soc = soc; smbchg_soc_changed(chip); } rc = smbchg_config_chg_battery_type(chip); if (rc) pr_smb(PR_MISC, "Couldn't update charger configuration rc=%d\n", rc); } rc = chip->usb_psy->get_property(chip->usb_psy, POWER_SUPPLY_PROP_CHARGING_ENABLED, &prop); if (rc == 0) vote(chip->usb_suspend_votable, POWER_SUPPLY_EN_VOTER, !prop.intval, 0); rc = chip->usb_psy->get_property(chip->usb_psy, POWER_SUPPLY_PROP_CURRENT_MAX, &prop); if (rc == 0) current_limit = prop.intval / 1000; read_usb_type(chip, &usb_type_name, &usb_supply_type); if (usb_supply_type != POWER_SUPPLY_TYPE_USB) goto skip_current_for_non_sdp; pr_smb(PR_MISC, "usb type = %s current_limit = %d\n", usb_type_name, current_limit); rc = vote(chip->usb_icl_votable, PSY_ICL_VOTER, true, current_limit); if (rc < 0) pr_err("Couldn't update USB PSY ICL vote rc=%d\n", rc); skip_current_for_non_sdp: smbchg_vfloat_adjust_check(chip); power_supply_changed(&chip->batt_psy); } static int smbchg_otg_regulator_enable(struct regulator_dev *rdev) { int rc = 0; Loading Loading @@ -4605,6 +4709,13 @@ static void smbchg_hvdcp_det_work(struct work_struct *work) hvdcp_det_work.work); int rc; if (chip->parallel.use_parallel_aicl) { if (!chip->hvdcp3_supported || !is_hvdcp_present(chip)) { complete_all(&chip->hvdcp_det_done); pr_smb(PR_MISC, "hvdcp_det_done complete\n"); } } if (is_hvdcp_present(chip)) { if (!chip->hvdcp3_supported && (chip->wa_flags & SMBCHG_HVDCP_9V_EN_WA)) { Loading Loading @@ -4738,6 +4849,12 @@ static void handle_usb_removal(struct smbchg_chip *chip) /* cancel/wait for hvdcp pending work if any */ cancel_delayed_work_sync(&chip->hvdcp_det_work); smbchg_change_usb_supply_type(chip, POWER_SUPPLY_TYPE_UNKNOWN); if (chip->parallel.use_parallel_aicl) { complete_all(&chip->hvdcp_det_done); pr_smb(PR_MISC, "complete hvdcp_det_done\n"); } if (!chip->skip_usb_notification) { pr_smb(PR_MISC, "setting usb psy present = %d\n", chip->usb_present); Loading Loading @@ -4839,6 +4956,10 @@ static void handle_usb_insertion(struct smbchg_chip *chip) smbchg_stay_awake(chip, PM_DETECT_HVDCP); schedule_delayed_work(&chip->hvdcp_det_work, msecs_to_jiffies(HVDCP_NOTIFY_MS)); if (chip->parallel.use_parallel_aicl) { reinit_completion(&chip->hvdcp_det_done); pr_smb(PR_MISC, "init hvdcp_det_done\n"); } } smbchg_detect_parallel_charger(chip); Loading Loading @@ -5173,9 +5294,14 @@ static void smbchg_handle_hvdcp3_disable(struct smbchg_chip *chip) } else if (is_usb_present(chip)) { read_usb_type(chip, &usb_type_name, &usb_supply_type); smbchg_change_usb_supply_type(chip, usb_supply_type); if (usb_supply_type == POWER_SUPPLY_TYPE_USB_DCP) if (usb_supply_type == POWER_SUPPLY_TYPE_USB_DCP) { schedule_delayed_work(&chip->hvdcp_det_work, msecs_to_jiffies(HVDCP_NOTIFY_MS)); if (chip->parallel.use_parallel_aicl) { reinit_completion(&chip->hvdcp_det_done); pr_smb(PR_MISC, "init hvdcp_det_done\n"); } } } else { smbchg_change_usb_supply_type(chip, POWER_SUPPLY_TYPE_UNKNOWN); } Loading Loading @@ -5397,6 +5523,11 @@ out: smbchg_handle_hvdcp3_disable(chip); if (chip->parallel.use_parallel_aicl) { pr_smb(PR_MISC, "complete hvdcp_det_done\n"); complete_all(&chip->hvdcp_det_done); } return rc; } Loading Loading @@ -5601,6 +5732,11 @@ static int smbchg_unprepare_for_pulsing_lite(struct smbchg_chip *chip) } smbchg_handle_hvdcp3_disable(chip); if (chip->parallel.use_parallel_aicl) { pr_smb(PR_MISC, "complete hvdcp_det_done\n"); complete_all(&chip->hvdcp_det_done); } return rc; } Loading Loading @@ -5650,6 +5786,11 @@ static int smbchg_hvdcp3_confirmed(struct smbchg_chip *chip) smbchg_change_usb_supply_type(chip, POWER_SUPPLY_TYPE_USB_HVDCP_3); if (chip->parallel.use_parallel_aicl) { complete_all(&chip->hvdcp_det_done); pr_smb(PR_MISC, "hvdcp_det_done complete\n"); } return rc; } Loading Loading @@ -5788,6 +5929,105 @@ static int smbchg_get_iusb(struct smbchg_chip *chip) return iusb_ua; } static void smbchg_external_power_changed(struct power_supply *psy) { struct smbchg_chip *chip = container_of(psy, struct smbchg_chip, batt_psy); union power_supply_propval prop = {0,}; int rc, current_limit = 0, soc; enum power_supply_type usb_supply_type; char *usb_type_name = "null"; if (chip->bms_psy_name) chip->bms_psy = power_supply_get_by_name((char *)chip->bms_psy_name); smbchg_aicl_deglitch_wa_check(chip); if (chip->bms_psy) { check_battery_type(chip); soc = get_prop_batt_capacity(chip); if (chip->previous_soc != soc) { chip->previous_soc = soc; smbchg_soc_changed(chip); } rc = smbchg_config_chg_battery_type(chip); if (rc) pr_smb(PR_MISC, "Couldn't update charger configuration rc=%d\n", rc); } rc = chip->usb_psy->get_property(chip->usb_psy, POWER_SUPPLY_PROP_CHARGING_ENABLED, &prop); if (rc == 0) vote(chip->usb_suspend_votable, POWER_SUPPLY_EN_VOTER, !prop.intval, 0); rc = chip->usb_psy->get_property(chip->usb_psy, POWER_SUPPLY_PROP_CURRENT_MAX, &prop); if (rc == 0) current_limit = prop.intval / 1000; rc = chip->usb_psy->get_property(chip->usb_psy, POWER_SUPPLY_PROP_TYPE, &prop); read_usb_type(chip, &usb_type_name, &usb_supply_type); if (!rc && usb_supply_type == POWER_SUPPLY_TYPE_USB && prop.intval != POWER_SUPPLY_TYPE_USB && is_usb_present(chip)) { /* incorrect type detected */ pr_smb(PR_MISC, "Incorrect charger type detetced - rerun APSD\n"); chip->hvdcp_3_det_ignore_uv = true; pr_smb(PR_MISC, "setting usb psy dp=f dm=f\n"); power_supply_set_dp_dm(chip->usb_psy, POWER_SUPPLY_DP_DM_DPF_DMF); rc = rerun_apsd(chip); if (rc) pr_err("APSD re-run failed\n"); chip->hvdcp_3_det_ignore_uv = false; if (!is_src_detect_high(chip)) { pr_smb(PR_MISC, "Charger removed - force removal\n"); update_usb_status(chip, is_usb_present(chip), true); return; } read_usb_type(chip, &usb_type_name, &usb_supply_type); if (usb_supply_type == POWER_SUPPLY_TYPE_USB_DCP) { schedule_delayed_work(&chip->hvdcp_det_work, msecs_to_jiffies(HVDCP_NOTIFY_MS)); if (chip->parallel.use_parallel_aicl) { reinit_completion(&chip->hvdcp_det_done); pr_smb(PR_MISC, "init hvdcp_det_done\n"); } smbchg_change_usb_supply_type(chip, usb_supply_type); } read_usb_type(chip, &usb_type_name, &usb_supply_type); if (usb_supply_type == POWER_SUPPLY_TYPE_USB_DCP) schedule_delayed_work(&chip->hvdcp_det_work, msecs_to_jiffies(HVDCP_NOTIFY_MS)); } if (usb_supply_type != POWER_SUPPLY_TYPE_USB) goto skip_current_for_non_sdp; pr_smb(PR_MISC, "usb type = %s current_limit = %d\n", usb_type_name, current_limit); rc = vote(chip->usb_icl_votable, PSY_ICL_VOTER, true, current_limit); if (rc < 0) pr_err("Couldn't update USB PSY ICL vote rc=%d\n", rc); skip_current_for_non_sdp: smbchg_vfloat_adjust_check(chip); power_supply_changed(&chip->batt_psy); } static enum power_supply_property smbchg_battery_properties[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_PRESENT, Loading Loading @@ -6636,7 +6876,7 @@ static irqreturn_t aicl_done_handler(int irq, void *_chip) increment_aicl_count(chip); if (usb_present) if (usb_present && !chip->parallel.use_parallel_aicl) smbchg_parallel_usb_check_ok(chip); if (chip->aicl_complete) Loading Loading @@ -7457,6 +7697,8 @@ static int smb_parse_dt(struct smbchg_chip *chip) OF_PROP_READ(chip, chip->max_pulse_allowed, "max-pulse-allowed", rc, 1); chip->parallel.use_parallel_aicl = of_property_read_bool(node, "qcom,use-parallel-aicl"); /* * use the dt values if they exist, otherwise do not touch the params */ Loading Loading @@ -8187,6 +8429,7 @@ static int smbchg_probe(struct spmi_device *spmi) init_completion(&chip->src_det_raised); init_completion(&chip->usbin_uv_lowered); init_completion(&chip->usbin_uv_raised); init_completion(&chip->hvdcp_det_done); chip->vadc_dev = vadc_dev; chip->vchg_vadc_dev = vchg_vadc_dev; chip->spmi = spmi; Loading drivers/power/smb138x-charger.c +500 −0 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
Documentation/devicetree/bindings/power/qpnp-smbcharger.txt +4 −0 Original line number Diff line number Diff line Loading @@ -300,6 +300,10 @@ Optional Properties: - qcom,max-pulse-allowed The maximum number of pulses allowed in HVDCP3 mode. It can be used to restrict VBUS to a value. - qcom,use-parallel-aicl A boolean property to indicate that AICL is run on the parallel slave to detect adapter's ICL capacity. Ideally to be used if the required FCC is greater then 3A and if the parallel slave supports AICL. Example: qcom,qpnp-smbcharger { Loading
drivers/power/pmic-voter.c +1 −1 Original line number Diff line number Diff line Loading @@ -18,7 +18,7 @@ #include "pmic-voter.h" #define NUM_MAX_CLIENTS 8 #define NUM_MAX_CLIENTS 12 struct client_vote { int state; Loading
drivers/power/power_supply_sysfs.c +1 −0 Original line number Diff line number Diff line Loading @@ -248,6 +248,7 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(typec_mode), POWER_SUPPLY_ATTR(allow_hvdcp3), POWER_SUPPLY_ATTR(max_pulse_allowed), POWER_SUPPLY_ATTR(enable_aicl), POWER_SUPPLY_ATTR(soc_reporting_ready), POWER_SUPPLY_ATTR(ignore_false_negative_isense), POWER_SUPPLY_ATTR(enable_jeita_detection), Loading
drivers/power/qpnp-smbcharger.c +306 −63 Original line number Diff line number Diff line Loading @@ -63,6 +63,9 @@ struct parallel_usb_cfg { int initial_aicl_ma; ktime_t last_disabled; bool enabled_once; int parallel_aicl_ma; bool use_parallel_aicl; bool parallel_en_in_progress; }; struct ilim_entry { Loading Loading @@ -263,6 +266,7 @@ struct smbchg_chip { struct completion src_det_raised; struct completion usbin_uv_lowered; struct completion usbin_uv_raised; struct completion hvdcp_det_done; int pulse_cnt; struct led_classdev led_cdev; bool skip_usb_notification; Loading Loading @@ -347,6 +351,7 @@ enum icl_voters { SW_AICL_ICL_VOTER, CHG_SUSPEND_WORKAROUND_ICL_VOTER, SHUTDOWN_WORKAROUND_ICL_VOTER, PARALLEL_ICL_VOTER, NUM_ICL_VOTER, }; Loading Loading @@ -2067,6 +2072,106 @@ static int smbchg_get_aicl_level_ma(struct smbchg_chip *chip) return chip->tables.usb_ilim_ma_table[reg]; } static int smbchg_run_parallel_aicl(struct smbchg_chip *chip) { int rc, aicl_ma, fcc_ma, icl_ma; union power_supply_propval pval = {0, }; struct power_supply *parallel_psy = get_parallel_psy(chip); if (!parallel_psy) { pr_err("parallel charger not found\n"); return 0; } rc = power_supply_set_present(parallel_psy, true); if (rc) { pr_err("Unable to set_present for parallel_psy rc=%d\n", rc); return rc; } rc = power_supply_set_voltage_limit(parallel_psy, chip->vfloat_mv); if (rc) { pr_err("Unable to set vfloat for parallel_psy rc=%d\n", rc); return rc; } /* Enable slave AICL */ pval.intval = 1; rc = parallel_psy->set_property(parallel_psy, POWER_SUPPLY_PROP_ENABLE_AICL, &pval); if (rc) { pr_err("Unable to enable AICL on parallel_psy rc=%d\n", rc); return rc; } /* Set max allowable FCC to slave */ fcc_ma = get_effective_result_locked(chip->fcc_votable); pval.intval = fcc_ma * 1000; rc = parallel_psy->set_property(parallel_psy, POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &pval); if (rc) { pr_err("Unable to set FCC for parallel_psy rc=%d\n", rc); goto restore_icl; } /* Get max allowable ICL */ icl_ma = get_effective_result_locked(chip->usb_icl_votable); /* * Force main charger to draw 100mA as the minimal input current * but don't suspend it to keep the slave charger online. * Set effective ICL value to slave charger to use it detecting * adapter's real capability. The 100mA draw in main charger path * will be added on slave's ICL status. */ vote(chip->usb_icl_votable, PARALLEL_ICL_VOTER, true, CURRENT_100_MA); rc = power_supply_set_current_limit(parallel_psy, icl_ma * 1000); if (rc) { pr_err("Unable to set ICL for parallel_psy rc=%d\n", rc); goto restore_icl; } rc = parallel_psy->get_property(parallel_psy, POWER_SUPPLY_PROP_INPUT_CURRENT_MAX, &pval); if (rc) { pr_err("Unable to read AICL from parallel_psy rc=%d\n", rc); goto restore_icl; } aicl_ma = pval.intval / 1000; if (aicl_ma == 0) { pr_err("Parallel_psy ICL status is 0mA\n"); goto restore_icl; } chip->parallel.parallel_aicl_ma = min(icl_ma, CURRENT_100_MA + aicl_ma); pr_smb(PR_STATUS, "parallel AICL = %d mA\n", chip->parallel.parallel_aicl_ma); restore_icl: /* Disable slave AICL */ pval.intval = 0; rc = parallel_psy->set_property(parallel_psy, POWER_SUPPLY_PROP_ENABLE_AICL, &pval); if (rc) pr_err("Unable to disable AICL on parallel_psy rc=%d\n", rc); /* Suspend slave and set back last ICL value for main charger */ pval.intval = 1; rc = parallel_psy->set_property(parallel_psy, POWER_SUPPLY_PROP_INPUT_SUSPEND, &pval); if (rc) pr_err("Unable to suspend-input to parallel_psy rc=%d\n", rc); vote(chip->usb_icl_votable, PARALLEL_ICL_VOTER, false, 0); return rc; } static void smbchg_parallel_usb_disable(struct smbchg_chip *chip) { struct power_supply *parallel_psy = get_parallel_psy(chip); Loading @@ -2078,6 +2183,7 @@ static void smbchg_parallel_usb_disable(struct smbchg_chip *chip) taper_irq_en(chip, false); chip->parallel.initial_aicl_ma = 0; chip->parallel.current_max_ma = 0; chip->parallel.parallel_aicl_ma = 0; power_supply_set_current_limit(parallel_psy, SUSPEND_CURRENT_MA * 1000); power_supply_set_present(parallel_psy, false); Loading Loading @@ -2353,6 +2459,19 @@ static bool smbchg_is_parallel_usb_ok(struct smbchg_chip *chip, if (parallel_cl_ma <= SUSPEND_CURRENT_MA) parallel_cl_ma = 0; if (chip->parallel.use_parallel_aicl) { rc = smbchg_run_parallel_aicl(chip); if (rc) { pr_err("Failed to run parallel AICL rc=%d\n", rc); return false; } total_current_ma = chip->parallel.parallel_aicl_ma; pr_smb(PR_STATUS, "use parallel AICL result: %dmA\n", total_current_ma); *ret_total_current_ma = total_current_ma; return true; } /* * Set the parallel charge path's input current limit (ICL) * to the total current / 2 Loading @@ -2374,6 +2493,7 @@ static bool smbchg_is_parallel_usb_ok(struct smbchg_chip *chip, return true; } #define HVDCP_DETECTION_DONE_MS 5000 #define PARALLEL_CHARGER_EN_DELAY_MS 500 static void smbchg_parallel_usb_en_work(struct work_struct *work) { Loading @@ -2382,6 +2502,37 @@ static void smbchg_parallel_usb_en_work(struct work_struct *work) parallel_en_work.work); int previous_aicl_ma, total_current_ma, aicl_ma; bool in_progress; int rc, tries = 3; pr_smb(PR_MISC, "started\n"); chip->parallel.parallel_en_in_progress = true; /* * If use-parallel-aicl is enabled, we run parallel AICL to detect * the total input current from the adapter which could be split and * allotted to main and slave chargers. Running parallel AICL would * cost ~2 seconds which is the same delay for hvdcp detection. This * would cause the concurrence of parallel charging checking and * hvdcp detection. * The concurrence would result a mess on the parallel charger settings * because both logics manipulate ICL setting as well as the parallel * enabling/disabling. * Wait here before parallel charging logic running, until hvdcp/ * hvdcp3 detection logic has finished/confirmed the charger type * detection, to provide a stable ICL value for parallel charging * splitting logic. */ if (chip->parallel.use_parallel_aicl) { while (tries--) { rc = wait_for_completion_interruptible_timeout( &chip->hvdcp_det_done, msecs_to_jiffies(HVDCP_DETECTION_DONE_MS)); if (rc >= 0) { pr_smb(PR_STATUS, "hvdcp detection done\n"); break; } pr_smb(PR_STATUS, "wait hvdcp_det_done interrupted\n"); } } /* do a check to see if the aicl is stable */ previous_aicl_ma = smbchg_get_aicl_level_ma(chip); Loading @@ -2408,10 +2559,14 @@ static void smbchg_parallel_usb_en_work(struct work_struct *work) } } mutex_unlock(&chip->parallel.lock); pr_smb(PR_STATUS, "Parallel check complete!\n"); chip->parallel.parallel_en_in_progress = false; smbchg_relax(chip, PM_PARALLEL_CHECK); return; recheck: chip->parallel.parallel_en_in_progress = false; schedule_delayed_work(&chip->parallel_en_work, 0); } Loading @@ -2422,6 +2577,11 @@ static void smbchg_parallel_usb_check_ok(struct smbchg_chip *chip) if (!parallel_psy || !chip->parallel_charger_detected) return; if (chip->parallel.parallel_en_in_progress) { pr_smb(PR_MISC, "parallel logic run in progress, ignore\n"); return; } smbchg_stay_awake(chip, PM_PARALLEL_CHECK); schedule_delayed_work(&chip->parallel_en_work, 0); } Loading Loading @@ -2825,14 +2985,17 @@ static int set_usb_current_limit_vote_cb(struct device *dev, } } /* skip the aicl rerun if hvdcp icl voter is active */ if (effective_id == HVDCP_ICL_VOTER) /* skip the aicl rerun if hvdcp icl voter or parallel voter is active */ if (effective_id == HVDCP_ICL_VOTER || effective_id == PARALLEL_ICL_VOTER) return 0; aicl_ma = smbchg_get_aicl_level_ma(chip); if (icl_ma > aicl_ma) smbchg_rerun_aicl(chip); smbchg_parallel_usb_check_ok(chip); return 0; } Loading Loading @@ -3719,65 +3882,6 @@ static void check_battery_type(struct smbchg_chip *chip) } } static void smbchg_external_power_changed(struct power_supply *psy) { struct smbchg_chip *chip = container_of(psy, struct smbchg_chip, batt_psy); union power_supply_propval prop = {0,}; int rc, current_limit = 0, soc; enum power_supply_type usb_supply_type; char *usb_type_name = "null"; if (chip->bms_psy_name) chip->bms_psy = power_supply_get_by_name((char *)chip->bms_psy_name); smbchg_aicl_deglitch_wa_check(chip); if (chip->bms_psy) { check_battery_type(chip); soc = get_prop_batt_capacity(chip); if (chip->previous_soc != soc) { chip->previous_soc = soc; smbchg_soc_changed(chip); } rc = smbchg_config_chg_battery_type(chip); if (rc) pr_smb(PR_MISC, "Couldn't update charger configuration rc=%d\n", rc); } rc = chip->usb_psy->get_property(chip->usb_psy, POWER_SUPPLY_PROP_CHARGING_ENABLED, &prop); if (rc == 0) vote(chip->usb_suspend_votable, POWER_SUPPLY_EN_VOTER, !prop.intval, 0); rc = chip->usb_psy->get_property(chip->usb_psy, POWER_SUPPLY_PROP_CURRENT_MAX, &prop); if (rc == 0) current_limit = prop.intval / 1000; read_usb_type(chip, &usb_type_name, &usb_supply_type); if (usb_supply_type != POWER_SUPPLY_TYPE_USB) goto skip_current_for_non_sdp; pr_smb(PR_MISC, "usb type = %s current_limit = %d\n", usb_type_name, current_limit); rc = vote(chip->usb_icl_votable, PSY_ICL_VOTER, true, current_limit); if (rc < 0) pr_err("Couldn't update USB PSY ICL vote rc=%d\n", rc); skip_current_for_non_sdp: smbchg_vfloat_adjust_check(chip); power_supply_changed(&chip->batt_psy); } static int smbchg_otg_regulator_enable(struct regulator_dev *rdev) { int rc = 0; Loading Loading @@ -4605,6 +4709,13 @@ static void smbchg_hvdcp_det_work(struct work_struct *work) hvdcp_det_work.work); int rc; if (chip->parallel.use_parallel_aicl) { if (!chip->hvdcp3_supported || !is_hvdcp_present(chip)) { complete_all(&chip->hvdcp_det_done); pr_smb(PR_MISC, "hvdcp_det_done complete\n"); } } if (is_hvdcp_present(chip)) { if (!chip->hvdcp3_supported && (chip->wa_flags & SMBCHG_HVDCP_9V_EN_WA)) { Loading Loading @@ -4738,6 +4849,12 @@ static void handle_usb_removal(struct smbchg_chip *chip) /* cancel/wait for hvdcp pending work if any */ cancel_delayed_work_sync(&chip->hvdcp_det_work); smbchg_change_usb_supply_type(chip, POWER_SUPPLY_TYPE_UNKNOWN); if (chip->parallel.use_parallel_aicl) { complete_all(&chip->hvdcp_det_done); pr_smb(PR_MISC, "complete hvdcp_det_done\n"); } if (!chip->skip_usb_notification) { pr_smb(PR_MISC, "setting usb psy present = %d\n", chip->usb_present); Loading Loading @@ -4839,6 +4956,10 @@ static void handle_usb_insertion(struct smbchg_chip *chip) smbchg_stay_awake(chip, PM_DETECT_HVDCP); schedule_delayed_work(&chip->hvdcp_det_work, msecs_to_jiffies(HVDCP_NOTIFY_MS)); if (chip->parallel.use_parallel_aicl) { reinit_completion(&chip->hvdcp_det_done); pr_smb(PR_MISC, "init hvdcp_det_done\n"); } } smbchg_detect_parallel_charger(chip); Loading Loading @@ -5173,9 +5294,14 @@ static void smbchg_handle_hvdcp3_disable(struct smbchg_chip *chip) } else if (is_usb_present(chip)) { read_usb_type(chip, &usb_type_name, &usb_supply_type); smbchg_change_usb_supply_type(chip, usb_supply_type); if (usb_supply_type == POWER_SUPPLY_TYPE_USB_DCP) if (usb_supply_type == POWER_SUPPLY_TYPE_USB_DCP) { schedule_delayed_work(&chip->hvdcp_det_work, msecs_to_jiffies(HVDCP_NOTIFY_MS)); if (chip->parallel.use_parallel_aicl) { reinit_completion(&chip->hvdcp_det_done); pr_smb(PR_MISC, "init hvdcp_det_done\n"); } } } else { smbchg_change_usb_supply_type(chip, POWER_SUPPLY_TYPE_UNKNOWN); } Loading Loading @@ -5397,6 +5523,11 @@ out: smbchg_handle_hvdcp3_disable(chip); if (chip->parallel.use_parallel_aicl) { pr_smb(PR_MISC, "complete hvdcp_det_done\n"); complete_all(&chip->hvdcp_det_done); } return rc; } Loading Loading @@ -5601,6 +5732,11 @@ static int smbchg_unprepare_for_pulsing_lite(struct smbchg_chip *chip) } smbchg_handle_hvdcp3_disable(chip); if (chip->parallel.use_parallel_aicl) { pr_smb(PR_MISC, "complete hvdcp_det_done\n"); complete_all(&chip->hvdcp_det_done); } return rc; } Loading Loading @@ -5650,6 +5786,11 @@ static int smbchg_hvdcp3_confirmed(struct smbchg_chip *chip) smbchg_change_usb_supply_type(chip, POWER_SUPPLY_TYPE_USB_HVDCP_3); if (chip->parallel.use_parallel_aicl) { complete_all(&chip->hvdcp_det_done); pr_smb(PR_MISC, "hvdcp_det_done complete\n"); } return rc; } Loading Loading @@ -5788,6 +5929,105 @@ static int smbchg_get_iusb(struct smbchg_chip *chip) return iusb_ua; } static void smbchg_external_power_changed(struct power_supply *psy) { struct smbchg_chip *chip = container_of(psy, struct smbchg_chip, batt_psy); union power_supply_propval prop = {0,}; int rc, current_limit = 0, soc; enum power_supply_type usb_supply_type; char *usb_type_name = "null"; if (chip->bms_psy_name) chip->bms_psy = power_supply_get_by_name((char *)chip->bms_psy_name); smbchg_aicl_deglitch_wa_check(chip); if (chip->bms_psy) { check_battery_type(chip); soc = get_prop_batt_capacity(chip); if (chip->previous_soc != soc) { chip->previous_soc = soc; smbchg_soc_changed(chip); } rc = smbchg_config_chg_battery_type(chip); if (rc) pr_smb(PR_MISC, "Couldn't update charger configuration rc=%d\n", rc); } rc = chip->usb_psy->get_property(chip->usb_psy, POWER_SUPPLY_PROP_CHARGING_ENABLED, &prop); if (rc == 0) vote(chip->usb_suspend_votable, POWER_SUPPLY_EN_VOTER, !prop.intval, 0); rc = chip->usb_psy->get_property(chip->usb_psy, POWER_SUPPLY_PROP_CURRENT_MAX, &prop); if (rc == 0) current_limit = prop.intval / 1000; rc = chip->usb_psy->get_property(chip->usb_psy, POWER_SUPPLY_PROP_TYPE, &prop); read_usb_type(chip, &usb_type_name, &usb_supply_type); if (!rc && usb_supply_type == POWER_SUPPLY_TYPE_USB && prop.intval != POWER_SUPPLY_TYPE_USB && is_usb_present(chip)) { /* incorrect type detected */ pr_smb(PR_MISC, "Incorrect charger type detetced - rerun APSD\n"); chip->hvdcp_3_det_ignore_uv = true; pr_smb(PR_MISC, "setting usb psy dp=f dm=f\n"); power_supply_set_dp_dm(chip->usb_psy, POWER_SUPPLY_DP_DM_DPF_DMF); rc = rerun_apsd(chip); if (rc) pr_err("APSD re-run failed\n"); chip->hvdcp_3_det_ignore_uv = false; if (!is_src_detect_high(chip)) { pr_smb(PR_MISC, "Charger removed - force removal\n"); update_usb_status(chip, is_usb_present(chip), true); return; } read_usb_type(chip, &usb_type_name, &usb_supply_type); if (usb_supply_type == POWER_SUPPLY_TYPE_USB_DCP) { schedule_delayed_work(&chip->hvdcp_det_work, msecs_to_jiffies(HVDCP_NOTIFY_MS)); if (chip->parallel.use_parallel_aicl) { reinit_completion(&chip->hvdcp_det_done); pr_smb(PR_MISC, "init hvdcp_det_done\n"); } smbchg_change_usb_supply_type(chip, usb_supply_type); } read_usb_type(chip, &usb_type_name, &usb_supply_type); if (usb_supply_type == POWER_SUPPLY_TYPE_USB_DCP) schedule_delayed_work(&chip->hvdcp_det_work, msecs_to_jiffies(HVDCP_NOTIFY_MS)); } if (usb_supply_type != POWER_SUPPLY_TYPE_USB) goto skip_current_for_non_sdp; pr_smb(PR_MISC, "usb type = %s current_limit = %d\n", usb_type_name, current_limit); rc = vote(chip->usb_icl_votable, PSY_ICL_VOTER, true, current_limit); if (rc < 0) pr_err("Couldn't update USB PSY ICL vote rc=%d\n", rc); skip_current_for_non_sdp: smbchg_vfloat_adjust_check(chip); power_supply_changed(&chip->batt_psy); } static enum power_supply_property smbchg_battery_properties[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_PRESENT, Loading Loading @@ -6636,7 +6876,7 @@ static irqreturn_t aicl_done_handler(int irq, void *_chip) increment_aicl_count(chip); if (usb_present) if (usb_present && !chip->parallel.use_parallel_aicl) smbchg_parallel_usb_check_ok(chip); if (chip->aicl_complete) Loading Loading @@ -7457,6 +7697,8 @@ static int smb_parse_dt(struct smbchg_chip *chip) OF_PROP_READ(chip, chip->max_pulse_allowed, "max-pulse-allowed", rc, 1); chip->parallel.use_parallel_aicl = of_property_read_bool(node, "qcom,use-parallel-aicl"); /* * use the dt values if they exist, otherwise do not touch the params */ Loading Loading @@ -8187,6 +8429,7 @@ static int smbchg_probe(struct spmi_device *spmi) init_completion(&chip->src_det_raised); init_completion(&chip->usbin_uv_lowered); init_completion(&chip->usbin_uv_raised); init_completion(&chip->hvdcp_det_done); chip->vadc_dev = vadc_dev; chip->vchg_vadc_dev = vchg_vadc_dev; chip->spmi = spmi; Loading
drivers/power/smb138x-charger.c +500 −0 File changed.Preview size limit exceeded, changes collapsed. Show changes