Loading drivers/power/supply/power_supply_sysfs.c +1 −0 Original line number Diff line number Diff line Loading @@ -382,6 +382,7 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(esr_nominal), POWER_SUPPLY_ATTR(soh), POWER_SUPPLY_ATTR(force_recharge), POWER_SUPPLY_ATTR(fcc_stepper_enable), /* Local extensions of type int64_t */ POWER_SUPPLY_ATTR(charge_counter_ext), /* Properties of type `const char *' */ Loading drivers/power/supply/qcom/battery.c +190 −44 Original line number Diff line number Diff line Loading @@ -44,10 +44,12 @@ #define PL_INDIRECT_VOTER "PL_INDIRECT_VOTER" #define USBIN_I_VOTER "USBIN_I_VOTER" #define PL_FCC_LOW_VOTER "PL_FCC_LOW_VOTER" #define ICL_LIMIT_VOTER "ICL_LIMIT_VOTER" struct pl_data { int pl_mode; int pl_batfet_mode; int pl_min_icl_ua; int slave_pct; int slave_fcc_ua; int restricted_current; Loading @@ -71,10 +73,13 @@ struct pl_data { int charge_type; int total_settled_ua; int pl_settled_ua; int pl_fcc_max; u32 wa_flags; struct class qcom_batt_class; struct wakeup_source *pl_ws; struct notifier_block nb; bool pl_disable; int taper_entry_fv; }; struct pl_data *the_chip; Loading @@ -85,6 +90,7 @@ enum print_reason { enum { AICL_RERUN_WA_BIT = BIT(0), FORCE_INOV_DISABLE_BIT = BIT(1), }; static int debug_mask; Loading @@ -98,6 +104,8 @@ module_param_named(debug_mask, debug_mask, int, 0600); pr_debug(fmt, ##__VA_ARGS__); \ } while (0) #define IS_USBIN(mode) ((mode == POWER_SUPPLY_PL_USBIN_USBIN) \ || (mode == POWER_SUPPLY_PL_USBIN_USBIN_EXT)) enum { VER = 0, SLAVE_PCT, Loading @@ -108,19 +116,19 @@ enum { /******* * ICL * ********/ static void split_settled(struct pl_data *chip) static int get_settled_split(struct pl_data *chip, int *main_icl_ua, int *slave_icl_ua, int *total_settled_icl_ua) { int slave_icl_pct, total_current_ua; int slave_ua = 0, main_settled_ua = 0; union power_supply_propval pval = {0, }; int rc, total_settled_ua = 0; if ((chip->pl_mode != POWER_SUPPLY_PL_USBIN_USBIN) && (chip->pl_mode != POWER_SUPPLY_PL_USBIN_USBIN_EXT)) return; if (!IS_USBIN(chip->pl_mode)) return -EINVAL; if (!chip->main_psy) return; return -EINVAL; if (!get_effective_result_locked(chip->pl_disable_votable)) { /* read the aicl settled value */ Loading @@ -128,11 +136,10 @@ static void split_settled(struct pl_data *chip) POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED, &pval); if (rc < 0) { pr_err("Couldn't get aicl settled value rc=%d\n", rc); return; return rc; } main_settled_ua = pval.intval; /* slave gets 10 percent points less for ICL */ slave_icl_pct = max(0, chip->slave_pct - 10); slave_icl_pct = max(0, chip->slave_pct); slave_ua = ((main_settled_ua + chip->pl_settled_ua) * slave_icl_pct) / 100; total_settled_ua = main_settled_ua + chip->pl_settled_ua; Loading @@ -144,18 +151,63 @@ static void split_settled(struct pl_data *chip) chip->usb_psy = power_supply_get_by_name("usb"); if (!chip->usb_psy) { pr_err("Couldn't get usbpsy while splitting settled\n"); return; return -ENOENT; } /* no client is voting, so get the total current from charger */ rc = power_supply_get_property(chip->usb_psy, POWER_SUPPLY_PROP_HW_CURRENT_MAX, &pval); if (rc < 0) { pr_err("Couldn't get max current rc=%d\n", rc); return; return rc; } total_current_ua = pval.intval; } *main_icl_ua = total_current_ua - slave_ua; *slave_icl_ua = slave_ua; *total_settled_icl_ua = total_settled_ua; pl_dbg(chip, PR_PARALLEL, "Split total_current_ua=%d total_settled_ua=%d main_settled_ua=%d slave_ua=%d\n", total_current_ua, total_settled_ua, main_settled_ua, slave_ua); return 0; } static int validate_parallel_icl(struct pl_data *chip, bool *disable) { int rc = 0; int main_ua = 0, slave_ua = 0, total_settled_ua = 0; if (!IS_USBIN(chip->pl_mode) || get_effective_result_locked(chip->pl_disable_votable)) return 0; rc = get_settled_split(chip, &main_ua, &slave_ua, &total_settled_ua); if (rc < 0) { pr_err("Couldn't get split current rc=%d\n", rc); return rc; } if (slave_ua < chip->pl_min_icl_ua) *disable = true; else *disable = false; return 0; } static void split_settled(struct pl_data *chip) { union power_supply_propval pval = {0, }; int rc, main_ua, slave_ua, total_settled_ua; rc = get_settled_split(chip, &main_ua, &slave_ua, &total_settled_ua); if (rc < 0) { pr_err("Couldn't get split current rc=%d\n", rc); return; } /* * If there is an increase in slave share * (Also handles parallel enable case) Loading @@ -165,7 +217,7 @@ static void split_settled(struct pl_data *chip) * Set slave ICL then main ICL. */ if (slave_ua > chip->pl_settled_ua) { pval.intval = total_current_ua - slave_ua; pval.intval = main_ua; /* Set ICL on main charger */ rc = power_supply_set_property(chip->main_psy, POWER_SUPPLY_PROP_CURRENT_MAX, &pval); Loading Loading @@ -193,7 +245,7 @@ static void split_settled(struct pl_data *chip) return; } pval.intval = total_current_ua - slave_ua; pval.intval = main_ua; /* Set ICL on main charger */ rc = power_supply_set_property(chip->main_psy, POWER_SUPPLY_PROP_CURRENT_MAX, &pval); Loading @@ -207,9 +259,6 @@ static void split_settled(struct pl_data *chip) chip->total_settled_ua = total_settled_ua; chip->pl_settled_ua = slave_ua; pl_dbg(chip, PR_PARALLEL, "Split total_current_ua=%d main_settled_ua=%d slave_ua=%d\n", total_current_ua, main_settled_ua, slave_ua); } static ssize_t version_show(struct class *c, struct class_attribute *attr, Loading @@ -235,14 +284,21 @@ static ssize_t slave_pct_show(struct class *c, struct class_attribute *attr, static ssize_t slave_pct_store(struct class *c, struct class_attribute *attr, const char *ubuf, size_t count) { struct pl_data *chip = container_of(c, struct pl_data, qcom_batt_class); struct pl_data *chip = container_of(c, struct pl_data, qcom_batt_class); int rc; unsigned long val; bool disable = false; if (kstrtoul(ubuf, 10, &val)) return -EINVAL; chip->slave_pct = val; rc = validate_parallel_icl(chip, &disable); if (rc < 0) return rc; vote(chip->pl_disable_votable, ICL_LIMIT_VOTER, disable, 0); rerun_election(chip->fcc_votable); rerun_election(chip->fv_votable); split_settled(chip); Loading Loading @@ -374,6 +430,7 @@ static void get_fcc_split(struct pl_data *chip, int total_ua, effective_total_ua = max(0, total_ua + hw_cc_delta_ua); slave_limited_ua = min(effective_total_ua, bcl_ua); *slave_ua = (slave_limited_ua * chip->slave_pct) / 100; *slave_ua = min(*slave_ua, chip->pl_fcc_max); /* * In stacked BATFET configuration charger's current goes Loading @@ -399,6 +456,7 @@ static void pl_taper_work(struct work_struct *work) int eff_fcc_ua; int total_fcc_ua, master_fcc_ua, slave_fcc_ua = 0; chip->taper_entry_fv = get_effective_result(chip->fv_votable); chip->taper_work_running = true; while (true) { if (get_effective_result(chip->pl_disable_votable)) { Loading Loading @@ -444,9 +502,27 @@ static void pl_taper_work(struct work_struct *work) eff_fcc_ua); vote(chip->fcc_votable, TAPER_STEPPER_VOTER, true, eff_fcc_ua); } else { /* * Due to reduction of float voltage in JEITA condition * taper charging can be initiated at a lower FV. On * removal of JEITA condition, FV readjusts itself. * However, once taper charging is initiated, it doesn't * exits until parallel chaging is disabled due to which * FCC doesn't scale back to its original value, leading * to slow charging thereafter. * Check if FV increases in comparison to FV at which * taper charging was initiated, and if yes, exit taper * charging. */ if (get_effective_result(chip->fv_votable) > chip->taper_entry_fv) { pl_dbg(chip, PR_PARALLEL, "Float voltage increased. Exiting taper\n"); goto done; } else { pl_dbg(chip, PR_PARALLEL, "master is fast charging; waiting for next taper\n"); } } /* wait for the charger state to deglitch after FCC change */ msleep(PL_TAPER_WORK_DELAY_MS); } Loading @@ -473,11 +549,9 @@ static int pl_fcc_vote_callback(struct votable *votable, void *data, &slave_fcc_ua); if (slave_fcc_ua > MINIMUM_PARALLEL_FCC_UA) { chip->slave_fcc_ua = slave_fcc_ua; vote(chip->pl_disable_votable, PL_FCC_LOW_VOTER, false, 0); } else { chip->slave_fcc_ua = 0; vote(chip->pl_disable_votable, PL_FCC_LOW_VOTER, true, 0); } Loading Loading @@ -631,11 +705,9 @@ static int pl_disable_vote_callback(struct votable *votable, { struct pl_data *chip = data; union power_supply_propval pval = {0, }; int master_fcc_ua, total_fcc_ua, slave_fcc_ua; int rc; chip->total_settled_ua = 0; chip->pl_settled_ua = 0; int master_fcc_ua = 0, total_fcc_ua = 0, slave_fcc_ua = 0; int rc = 0; bool disable = false; if (!is_main_available(chip)) return -ENODEV; Loading @@ -647,6 +719,16 @@ static int pl_disable_vote_callback(struct votable *votable, cancel_delayed_work_sync(&chip->pl_awake_work); vote(chip->pl_awake_votable, PL_VOTER, true, 0); rc = validate_parallel_icl(chip, &disable); if (rc < 0) return rc; if (disable) { pr_info("Parallel ICL is less than min ICL(%d), skipping parallel enable\n", chip->pl_min_icl_ua); return 0; } /* enable parallel charging */ rc = power_supply_get_property(chip->pl_psy, POWER_SUPPLY_PROP_CHARGE_TYPE, &pval); Loading Loading @@ -729,8 +811,7 @@ static int pl_disable_vote_callback(struct votable *votable, pr_err("Couldn't change slave suspend state rc=%d\n", rc); if ((chip->pl_mode == POWER_SUPPLY_PL_USBIN_USBIN) || (chip->pl_mode == POWER_SUPPLY_PL_USBIN_USBIN_EXT)) if (IS_USBIN(chip->pl_mode)) split_settled(chip); /* * we could have been enabled while in taper mode, Loading @@ -757,8 +838,7 @@ static int pl_disable_vote_callback(struct votable *votable, (master_fcc_ua * 100) / total_fcc_ua, (slave_fcc_ua * 100) / total_fcc_ua); } else { if ((chip->pl_mode == POWER_SUPPLY_PL_USBIN_USBIN) || (chip->pl_mode == POWER_SUPPLY_PL_USBIN_USBIN_EXT)) if (IS_USBIN(chip->pl_mode)) split_settled(chip); /* pl_psy may be NULL while in the disable branch */ Loading @@ -781,11 +861,22 @@ static int pl_disable_vote_callback(struct votable *votable, return rc; } /* reset parallel FCC */ chip->slave_fcc_ua = 0; rerun_election(chip->fv_votable); cancel_delayed_work_sync(&chip->pl_awake_work); schedule_delayed_work(&chip->pl_awake_work, msecs_to_jiffies(5000)); chip->total_settled_ua = 0; chip->pl_settled_ua = 0; } /* notify parallel state change */ if (chip->pl_psy && (chip->pl_disable != pl_disable)) { power_supply_changed(chip->pl_psy); chip->pl_disable = (bool)pl_disable; } pl_dbg(chip, PR_PARALLEL, "parallel charging %s\n", Loading Loading @@ -856,8 +947,8 @@ static bool is_parallel_available(struct pl_data *chip) chip->pl_mode = pval.intval; /* Disable autonomous votage increments for USBIN-USBIN */ if ((chip->pl_mode == POWER_SUPPLY_PL_USBIN_USBIN) || (chip->pl_mode == POWER_SUPPLY_PL_USBIN_USBIN_EXT)) { if (IS_USBIN(chip->pl_mode) && (chip->wa_flags & FORCE_INOV_DISABLE_BIT)) { if (!chip->hvdcp_hw_inov_dis_votable) chip->hvdcp_hw_inov_dis_votable = find_votable("HVDCP_HW_INOV_DIS"); Loading @@ -878,6 +969,17 @@ static bool is_parallel_available(struct pl_data *chip) } chip->pl_batfet_mode = pval.intval; pval.intval = 0; power_supply_get_property(chip->pl_psy, POWER_SUPPLY_PROP_MIN_ICL, &pval); chip->pl_min_icl_ua = pval.intval; chip->pl_fcc_max = INT_MAX; rc = power_supply_get_property(chip->pl_psy, POWER_SUPPLY_PROP_PARALLEL_FCC_MAX, &pval); if (!rc) chip->pl_fcc_max = pval.intval; vote(chip->pl_disable_votable, PARALLEL_PSY_VOTER, false, 0); return true; Loading @@ -899,9 +1001,6 @@ static void handle_main_charge_type(struct pl_data *chip) if ((pval.intval != POWER_SUPPLY_CHARGE_TYPE_FAST) && (pval.intval != POWER_SUPPLY_CHARGE_TYPE_TAPER)) { vote(chip->pl_disable_votable, CHG_STATE_VOTER, true, 0); vote(chip->pl_disable_votable, TAPER_END_VOTER, false, 0); vote(chip->pl_disable_votable, PL_TAPER_EARLY_BAD_VOTER, false, 0); chip->charge_type = pval.intval; return; } Loading @@ -921,6 +1020,16 @@ static void handle_main_charge_type(struct pl_data *chip) /* handle fast/taper charge entry */ if (pval.intval == POWER_SUPPLY_CHARGE_TYPE_TAPER || pval.intval == POWER_SUPPLY_CHARGE_TYPE_FAST) { /* * Undo parallel charging termination if entered taper in * reduced float voltage condition due to jeita mitigation. */ if (pval.intval == POWER_SUPPLY_CHARGE_TYPE_FAST && (chip->taper_entry_fv < get_effective_result(chip->fv_votable))) { vote(chip->pl_disable_votable, TAPER_END_VOTER, false, 0); } pl_dbg(chip, PR_PARALLEL, "chg_state enabling parallel\n"); vote(chip->pl_disable_votable, CHG_STATE_VOTER, false, 0); chip->charge_type = pval.intval; Loading @@ -940,6 +1049,7 @@ static void handle_settled_icl_change(struct pl_data *chip) int main_settled_ua; int main_limited; int total_current_ua; bool disable = false; total_current_ua = get_effective_result_locked(chip->usb_icl_votable); Loading Loading @@ -973,13 +1083,8 @@ static void handle_settled_icl_change(struct pl_data *chip) else vote(chip->pl_enable_votable_indirect, USBIN_I_VOTER, true, 0); rerun_election(chip->fcc_votable); if (get_effective_result(chip->pl_disable_votable)) return; if (chip->pl_mode == POWER_SUPPLY_PL_USBIN_USBIN || chip->pl_mode == POWER_SUPPLY_PL_USBIN_USBIN_EXT) { if (IS_USBIN(chip->pl_mode)) { /* * call aicl split only when USBIN_USBIN and enabled * and if settled current has changed by more than 300mA Loading @@ -993,10 +1098,19 @@ static void handle_settled_icl_change(struct pl_data *chip) /* If ICL change is small skip splitting */ if (abs(new_total_settled_ua - chip->total_settled_ua) > MIN_ICL_CHANGE_DELTA_UA) > MIN_ICL_CHANGE_DELTA_UA) { rc = validate_parallel_icl(chip, &disable); if (rc < 0) return; vote(chip->pl_disable_votable, ICL_LIMIT_VOTER, disable, 0); if (!get_effective_result_locked( chip->pl_disable_votable)) split_settled(chip); } } } static void handle_parallel_in_taper(struct pl_data *chip) { Loading Loading @@ -1027,6 +1141,34 @@ static void handle_parallel_in_taper(struct pl_data *chip) } } static void handle_usb_change(struct pl_data *chip) { int rc; union power_supply_propval pval = {0, }; if (!chip->usb_psy) chip->usb_psy = power_supply_get_by_name("usb"); if (!chip->usb_psy) { pr_err("Couldn't get usbpsy\n"); return; } rc = power_supply_get_property(chip->usb_psy, POWER_SUPPLY_PROP_PRESENT, &pval); if (rc < 0) { pr_err("Couldn't get present from USB rc=%d\n", rc); return; } if (!pval.intval) { /* USB removed: remove all stale votes */ vote(chip->pl_disable_votable, TAPER_END_VOTER, false, 0); vote(chip->pl_disable_votable, PL_TAPER_EARLY_BAD_VOTER, false, 0); vote(chip->pl_disable_votable, ICL_LIMIT_VOTER, false, 0); } } static void status_change_work(struct work_struct *work) { struct pl_data *chip = container_of(work, Loading @@ -1051,6 +1193,7 @@ static void status_change_work(struct work_struct *work) is_parallel_available(chip); handle_usb_change(chip); handle_main_charge_type(chip); handle_settled_icl_change(chip); handle_parallel_in_taper(chip); Loading Loading @@ -1098,7 +1241,9 @@ static void pl_config_init(struct pl_data *chip, int smb_version) switch (smb_version) { case PMI8998_SUBTYPE: case PM660_SUBTYPE: chip->wa_flags = AICL_RERUN_WA_BIT; chip->wa_flags = AICL_RERUN_WA_BIT | FORCE_INOV_DISABLE_BIT; break; case PMI632_SUBTYPE: break; default: break; Loading Loading @@ -1199,6 +1344,7 @@ int qcom_batt_init(int smb_version) goto unreg_notifier; } chip->pl_disable = true; chip->qcom_batt_class.name = "qcom-battery", chip->qcom_batt_class.owner = THIS_MODULE, chip->qcom_batt_class.class_groups = batt_class_groups; Loading include/linux/power_supply.h +1 −0 Original line number Diff line number Diff line Loading @@ -308,6 +308,7 @@ enum power_supply_property { POWER_SUPPLY_PROP_ESR_NOMINAL, POWER_SUPPLY_PROP_SOH, POWER_SUPPLY_PROP_FORCE_RECHARGE, POWER_SUPPLY_PROP_FCC_STEPPER_ENABLE, /* Local extensions of type int64_t */ POWER_SUPPLY_PROP_CHARGE_COUNTER_EXT, /* Properties of type `const char *' */ Loading Loading
drivers/power/supply/power_supply_sysfs.c +1 −0 Original line number Diff line number Diff line Loading @@ -382,6 +382,7 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(esr_nominal), POWER_SUPPLY_ATTR(soh), POWER_SUPPLY_ATTR(force_recharge), POWER_SUPPLY_ATTR(fcc_stepper_enable), /* Local extensions of type int64_t */ POWER_SUPPLY_ATTR(charge_counter_ext), /* Properties of type `const char *' */ Loading
drivers/power/supply/qcom/battery.c +190 −44 Original line number Diff line number Diff line Loading @@ -44,10 +44,12 @@ #define PL_INDIRECT_VOTER "PL_INDIRECT_VOTER" #define USBIN_I_VOTER "USBIN_I_VOTER" #define PL_FCC_LOW_VOTER "PL_FCC_LOW_VOTER" #define ICL_LIMIT_VOTER "ICL_LIMIT_VOTER" struct pl_data { int pl_mode; int pl_batfet_mode; int pl_min_icl_ua; int slave_pct; int slave_fcc_ua; int restricted_current; Loading @@ -71,10 +73,13 @@ struct pl_data { int charge_type; int total_settled_ua; int pl_settled_ua; int pl_fcc_max; u32 wa_flags; struct class qcom_batt_class; struct wakeup_source *pl_ws; struct notifier_block nb; bool pl_disable; int taper_entry_fv; }; struct pl_data *the_chip; Loading @@ -85,6 +90,7 @@ enum print_reason { enum { AICL_RERUN_WA_BIT = BIT(0), FORCE_INOV_DISABLE_BIT = BIT(1), }; static int debug_mask; Loading @@ -98,6 +104,8 @@ module_param_named(debug_mask, debug_mask, int, 0600); pr_debug(fmt, ##__VA_ARGS__); \ } while (0) #define IS_USBIN(mode) ((mode == POWER_SUPPLY_PL_USBIN_USBIN) \ || (mode == POWER_SUPPLY_PL_USBIN_USBIN_EXT)) enum { VER = 0, SLAVE_PCT, Loading @@ -108,19 +116,19 @@ enum { /******* * ICL * ********/ static void split_settled(struct pl_data *chip) static int get_settled_split(struct pl_data *chip, int *main_icl_ua, int *slave_icl_ua, int *total_settled_icl_ua) { int slave_icl_pct, total_current_ua; int slave_ua = 0, main_settled_ua = 0; union power_supply_propval pval = {0, }; int rc, total_settled_ua = 0; if ((chip->pl_mode != POWER_SUPPLY_PL_USBIN_USBIN) && (chip->pl_mode != POWER_SUPPLY_PL_USBIN_USBIN_EXT)) return; if (!IS_USBIN(chip->pl_mode)) return -EINVAL; if (!chip->main_psy) return; return -EINVAL; if (!get_effective_result_locked(chip->pl_disable_votable)) { /* read the aicl settled value */ Loading @@ -128,11 +136,10 @@ static void split_settled(struct pl_data *chip) POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED, &pval); if (rc < 0) { pr_err("Couldn't get aicl settled value rc=%d\n", rc); return; return rc; } main_settled_ua = pval.intval; /* slave gets 10 percent points less for ICL */ slave_icl_pct = max(0, chip->slave_pct - 10); slave_icl_pct = max(0, chip->slave_pct); slave_ua = ((main_settled_ua + chip->pl_settled_ua) * slave_icl_pct) / 100; total_settled_ua = main_settled_ua + chip->pl_settled_ua; Loading @@ -144,18 +151,63 @@ static void split_settled(struct pl_data *chip) chip->usb_psy = power_supply_get_by_name("usb"); if (!chip->usb_psy) { pr_err("Couldn't get usbpsy while splitting settled\n"); return; return -ENOENT; } /* no client is voting, so get the total current from charger */ rc = power_supply_get_property(chip->usb_psy, POWER_SUPPLY_PROP_HW_CURRENT_MAX, &pval); if (rc < 0) { pr_err("Couldn't get max current rc=%d\n", rc); return; return rc; } total_current_ua = pval.intval; } *main_icl_ua = total_current_ua - slave_ua; *slave_icl_ua = slave_ua; *total_settled_icl_ua = total_settled_ua; pl_dbg(chip, PR_PARALLEL, "Split total_current_ua=%d total_settled_ua=%d main_settled_ua=%d slave_ua=%d\n", total_current_ua, total_settled_ua, main_settled_ua, slave_ua); return 0; } static int validate_parallel_icl(struct pl_data *chip, bool *disable) { int rc = 0; int main_ua = 0, slave_ua = 0, total_settled_ua = 0; if (!IS_USBIN(chip->pl_mode) || get_effective_result_locked(chip->pl_disable_votable)) return 0; rc = get_settled_split(chip, &main_ua, &slave_ua, &total_settled_ua); if (rc < 0) { pr_err("Couldn't get split current rc=%d\n", rc); return rc; } if (slave_ua < chip->pl_min_icl_ua) *disable = true; else *disable = false; return 0; } static void split_settled(struct pl_data *chip) { union power_supply_propval pval = {0, }; int rc, main_ua, slave_ua, total_settled_ua; rc = get_settled_split(chip, &main_ua, &slave_ua, &total_settled_ua); if (rc < 0) { pr_err("Couldn't get split current rc=%d\n", rc); return; } /* * If there is an increase in slave share * (Also handles parallel enable case) Loading @@ -165,7 +217,7 @@ static void split_settled(struct pl_data *chip) * Set slave ICL then main ICL. */ if (slave_ua > chip->pl_settled_ua) { pval.intval = total_current_ua - slave_ua; pval.intval = main_ua; /* Set ICL on main charger */ rc = power_supply_set_property(chip->main_psy, POWER_SUPPLY_PROP_CURRENT_MAX, &pval); Loading Loading @@ -193,7 +245,7 @@ static void split_settled(struct pl_data *chip) return; } pval.intval = total_current_ua - slave_ua; pval.intval = main_ua; /* Set ICL on main charger */ rc = power_supply_set_property(chip->main_psy, POWER_SUPPLY_PROP_CURRENT_MAX, &pval); Loading @@ -207,9 +259,6 @@ static void split_settled(struct pl_data *chip) chip->total_settled_ua = total_settled_ua; chip->pl_settled_ua = slave_ua; pl_dbg(chip, PR_PARALLEL, "Split total_current_ua=%d main_settled_ua=%d slave_ua=%d\n", total_current_ua, main_settled_ua, slave_ua); } static ssize_t version_show(struct class *c, struct class_attribute *attr, Loading @@ -235,14 +284,21 @@ static ssize_t slave_pct_show(struct class *c, struct class_attribute *attr, static ssize_t slave_pct_store(struct class *c, struct class_attribute *attr, const char *ubuf, size_t count) { struct pl_data *chip = container_of(c, struct pl_data, qcom_batt_class); struct pl_data *chip = container_of(c, struct pl_data, qcom_batt_class); int rc; unsigned long val; bool disable = false; if (kstrtoul(ubuf, 10, &val)) return -EINVAL; chip->slave_pct = val; rc = validate_parallel_icl(chip, &disable); if (rc < 0) return rc; vote(chip->pl_disable_votable, ICL_LIMIT_VOTER, disable, 0); rerun_election(chip->fcc_votable); rerun_election(chip->fv_votable); split_settled(chip); Loading Loading @@ -374,6 +430,7 @@ static void get_fcc_split(struct pl_data *chip, int total_ua, effective_total_ua = max(0, total_ua + hw_cc_delta_ua); slave_limited_ua = min(effective_total_ua, bcl_ua); *slave_ua = (slave_limited_ua * chip->slave_pct) / 100; *slave_ua = min(*slave_ua, chip->pl_fcc_max); /* * In stacked BATFET configuration charger's current goes Loading @@ -399,6 +456,7 @@ static void pl_taper_work(struct work_struct *work) int eff_fcc_ua; int total_fcc_ua, master_fcc_ua, slave_fcc_ua = 0; chip->taper_entry_fv = get_effective_result(chip->fv_votable); chip->taper_work_running = true; while (true) { if (get_effective_result(chip->pl_disable_votable)) { Loading Loading @@ -444,9 +502,27 @@ static void pl_taper_work(struct work_struct *work) eff_fcc_ua); vote(chip->fcc_votable, TAPER_STEPPER_VOTER, true, eff_fcc_ua); } else { /* * Due to reduction of float voltage in JEITA condition * taper charging can be initiated at a lower FV. On * removal of JEITA condition, FV readjusts itself. * However, once taper charging is initiated, it doesn't * exits until parallel chaging is disabled due to which * FCC doesn't scale back to its original value, leading * to slow charging thereafter. * Check if FV increases in comparison to FV at which * taper charging was initiated, and if yes, exit taper * charging. */ if (get_effective_result(chip->fv_votable) > chip->taper_entry_fv) { pl_dbg(chip, PR_PARALLEL, "Float voltage increased. Exiting taper\n"); goto done; } else { pl_dbg(chip, PR_PARALLEL, "master is fast charging; waiting for next taper\n"); } } /* wait for the charger state to deglitch after FCC change */ msleep(PL_TAPER_WORK_DELAY_MS); } Loading @@ -473,11 +549,9 @@ static int pl_fcc_vote_callback(struct votable *votable, void *data, &slave_fcc_ua); if (slave_fcc_ua > MINIMUM_PARALLEL_FCC_UA) { chip->slave_fcc_ua = slave_fcc_ua; vote(chip->pl_disable_votable, PL_FCC_LOW_VOTER, false, 0); } else { chip->slave_fcc_ua = 0; vote(chip->pl_disable_votable, PL_FCC_LOW_VOTER, true, 0); } Loading Loading @@ -631,11 +705,9 @@ static int pl_disable_vote_callback(struct votable *votable, { struct pl_data *chip = data; union power_supply_propval pval = {0, }; int master_fcc_ua, total_fcc_ua, slave_fcc_ua; int rc; chip->total_settled_ua = 0; chip->pl_settled_ua = 0; int master_fcc_ua = 0, total_fcc_ua = 0, slave_fcc_ua = 0; int rc = 0; bool disable = false; if (!is_main_available(chip)) return -ENODEV; Loading @@ -647,6 +719,16 @@ static int pl_disable_vote_callback(struct votable *votable, cancel_delayed_work_sync(&chip->pl_awake_work); vote(chip->pl_awake_votable, PL_VOTER, true, 0); rc = validate_parallel_icl(chip, &disable); if (rc < 0) return rc; if (disable) { pr_info("Parallel ICL is less than min ICL(%d), skipping parallel enable\n", chip->pl_min_icl_ua); return 0; } /* enable parallel charging */ rc = power_supply_get_property(chip->pl_psy, POWER_SUPPLY_PROP_CHARGE_TYPE, &pval); Loading Loading @@ -729,8 +811,7 @@ static int pl_disable_vote_callback(struct votable *votable, pr_err("Couldn't change slave suspend state rc=%d\n", rc); if ((chip->pl_mode == POWER_SUPPLY_PL_USBIN_USBIN) || (chip->pl_mode == POWER_SUPPLY_PL_USBIN_USBIN_EXT)) if (IS_USBIN(chip->pl_mode)) split_settled(chip); /* * we could have been enabled while in taper mode, Loading @@ -757,8 +838,7 @@ static int pl_disable_vote_callback(struct votable *votable, (master_fcc_ua * 100) / total_fcc_ua, (slave_fcc_ua * 100) / total_fcc_ua); } else { if ((chip->pl_mode == POWER_SUPPLY_PL_USBIN_USBIN) || (chip->pl_mode == POWER_SUPPLY_PL_USBIN_USBIN_EXT)) if (IS_USBIN(chip->pl_mode)) split_settled(chip); /* pl_psy may be NULL while in the disable branch */ Loading @@ -781,11 +861,22 @@ static int pl_disable_vote_callback(struct votable *votable, return rc; } /* reset parallel FCC */ chip->slave_fcc_ua = 0; rerun_election(chip->fv_votable); cancel_delayed_work_sync(&chip->pl_awake_work); schedule_delayed_work(&chip->pl_awake_work, msecs_to_jiffies(5000)); chip->total_settled_ua = 0; chip->pl_settled_ua = 0; } /* notify parallel state change */ if (chip->pl_psy && (chip->pl_disable != pl_disable)) { power_supply_changed(chip->pl_psy); chip->pl_disable = (bool)pl_disable; } pl_dbg(chip, PR_PARALLEL, "parallel charging %s\n", Loading Loading @@ -856,8 +947,8 @@ static bool is_parallel_available(struct pl_data *chip) chip->pl_mode = pval.intval; /* Disable autonomous votage increments for USBIN-USBIN */ if ((chip->pl_mode == POWER_SUPPLY_PL_USBIN_USBIN) || (chip->pl_mode == POWER_SUPPLY_PL_USBIN_USBIN_EXT)) { if (IS_USBIN(chip->pl_mode) && (chip->wa_flags & FORCE_INOV_DISABLE_BIT)) { if (!chip->hvdcp_hw_inov_dis_votable) chip->hvdcp_hw_inov_dis_votable = find_votable("HVDCP_HW_INOV_DIS"); Loading @@ -878,6 +969,17 @@ static bool is_parallel_available(struct pl_data *chip) } chip->pl_batfet_mode = pval.intval; pval.intval = 0; power_supply_get_property(chip->pl_psy, POWER_SUPPLY_PROP_MIN_ICL, &pval); chip->pl_min_icl_ua = pval.intval; chip->pl_fcc_max = INT_MAX; rc = power_supply_get_property(chip->pl_psy, POWER_SUPPLY_PROP_PARALLEL_FCC_MAX, &pval); if (!rc) chip->pl_fcc_max = pval.intval; vote(chip->pl_disable_votable, PARALLEL_PSY_VOTER, false, 0); return true; Loading @@ -899,9 +1001,6 @@ static void handle_main_charge_type(struct pl_data *chip) if ((pval.intval != POWER_SUPPLY_CHARGE_TYPE_FAST) && (pval.intval != POWER_SUPPLY_CHARGE_TYPE_TAPER)) { vote(chip->pl_disable_votable, CHG_STATE_VOTER, true, 0); vote(chip->pl_disable_votable, TAPER_END_VOTER, false, 0); vote(chip->pl_disable_votable, PL_TAPER_EARLY_BAD_VOTER, false, 0); chip->charge_type = pval.intval; return; } Loading @@ -921,6 +1020,16 @@ static void handle_main_charge_type(struct pl_data *chip) /* handle fast/taper charge entry */ if (pval.intval == POWER_SUPPLY_CHARGE_TYPE_TAPER || pval.intval == POWER_SUPPLY_CHARGE_TYPE_FAST) { /* * Undo parallel charging termination if entered taper in * reduced float voltage condition due to jeita mitigation. */ if (pval.intval == POWER_SUPPLY_CHARGE_TYPE_FAST && (chip->taper_entry_fv < get_effective_result(chip->fv_votable))) { vote(chip->pl_disable_votable, TAPER_END_VOTER, false, 0); } pl_dbg(chip, PR_PARALLEL, "chg_state enabling parallel\n"); vote(chip->pl_disable_votable, CHG_STATE_VOTER, false, 0); chip->charge_type = pval.intval; Loading @@ -940,6 +1049,7 @@ static void handle_settled_icl_change(struct pl_data *chip) int main_settled_ua; int main_limited; int total_current_ua; bool disable = false; total_current_ua = get_effective_result_locked(chip->usb_icl_votable); Loading Loading @@ -973,13 +1083,8 @@ static void handle_settled_icl_change(struct pl_data *chip) else vote(chip->pl_enable_votable_indirect, USBIN_I_VOTER, true, 0); rerun_election(chip->fcc_votable); if (get_effective_result(chip->pl_disable_votable)) return; if (chip->pl_mode == POWER_SUPPLY_PL_USBIN_USBIN || chip->pl_mode == POWER_SUPPLY_PL_USBIN_USBIN_EXT) { if (IS_USBIN(chip->pl_mode)) { /* * call aicl split only when USBIN_USBIN and enabled * and if settled current has changed by more than 300mA Loading @@ -993,10 +1098,19 @@ static void handle_settled_icl_change(struct pl_data *chip) /* If ICL change is small skip splitting */ if (abs(new_total_settled_ua - chip->total_settled_ua) > MIN_ICL_CHANGE_DELTA_UA) > MIN_ICL_CHANGE_DELTA_UA) { rc = validate_parallel_icl(chip, &disable); if (rc < 0) return; vote(chip->pl_disable_votable, ICL_LIMIT_VOTER, disable, 0); if (!get_effective_result_locked( chip->pl_disable_votable)) split_settled(chip); } } } static void handle_parallel_in_taper(struct pl_data *chip) { Loading Loading @@ -1027,6 +1141,34 @@ static void handle_parallel_in_taper(struct pl_data *chip) } } static void handle_usb_change(struct pl_data *chip) { int rc; union power_supply_propval pval = {0, }; if (!chip->usb_psy) chip->usb_psy = power_supply_get_by_name("usb"); if (!chip->usb_psy) { pr_err("Couldn't get usbpsy\n"); return; } rc = power_supply_get_property(chip->usb_psy, POWER_SUPPLY_PROP_PRESENT, &pval); if (rc < 0) { pr_err("Couldn't get present from USB rc=%d\n", rc); return; } if (!pval.intval) { /* USB removed: remove all stale votes */ vote(chip->pl_disable_votable, TAPER_END_VOTER, false, 0); vote(chip->pl_disable_votable, PL_TAPER_EARLY_BAD_VOTER, false, 0); vote(chip->pl_disable_votable, ICL_LIMIT_VOTER, false, 0); } } static void status_change_work(struct work_struct *work) { struct pl_data *chip = container_of(work, Loading @@ -1051,6 +1193,7 @@ static void status_change_work(struct work_struct *work) is_parallel_available(chip); handle_usb_change(chip); handle_main_charge_type(chip); handle_settled_icl_change(chip); handle_parallel_in_taper(chip); Loading Loading @@ -1098,7 +1241,9 @@ static void pl_config_init(struct pl_data *chip, int smb_version) switch (smb_version) { case PMI8998_SUBTYPE: case PM660_SUBTYPE: chip->wa_flags = AICL_RERUN_WA_BIT; chip->wa_flags = AICL_RERUN_WA_BIT | FORCE_INOV_DISABLE_BIT; break; case PMI632_SUBTYPE: break; default: break; Loading Loading @@ -1199,6 +1344,7 @@ int qcom_batt_init(int smb_version) goto unreg_notifier; } chip->pl_disable = true; chip->qcom_batt_class.name = "qcom-battery", chip->qcom_batt_class.owner = THIS_MODULE, chip->qcom_batt_class.class_groups = batt_class_groups; Loading
include/linux/power_supply.h +1 −0 Original line number Diff line number Diff line Loading @@ -308,6 +308,7 @@ enum power_supply_property { POWER_SUPPLY_PROP_ESR_NOMINAL, POWER_SUPPLY_PROP_SOH, POWER_SUPPLY_PROP_FORCE_RECHARGE, POWER_SUPPLY_PROP_FCC_STEPPER_ENABLE, /* Local extensions of type int64_t */ POWER_SUPPLY_PROP_CHARGE_COUNTER_EXT, /* Properties of type `const char *' */ Loading