Loading Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb2.txt +4 −0 Original line number Diff line number Diff line Loading @@ -178,6 +178,10 @@ Charger specific properties: Definition: WD bark-timeout in seconds. The possible values are 16, 32, 64, 128. If not defined it defaults to 64. - qcom,sw-jeita-enable Usage: optional Value type: bool Definition: Boolean flag which when present enables sw compensation for jeita ============================================= Second Level Nodes - SMB2 Charger Peripherals Loading drivers/power/supply/qcom/qpnp-smb2.c +3 −0 Original line number Diff line number Diff line Loading @@ -210,6 +210,9 @@ static int smb2_parse_dt(struct smb2 *chip) chg->step_chg_enabled = of_property_read_bool(node, "qcom,step-charging-enable"); chg->sw_jeita_enabled = of_property_read_bool(node, "qcom,sw-jeita-enable"); rc = of_property_read_u32(node, "qcom,wd-bark-time-secs", &chip->dt.wd_bark_time); if (rc < 0 || chip->dt.wd_bark_time < MIN_WD_BARK_TIME) Loading drivers/power/supply/qcom/smb-lib.c +3 −2 Original line number Diff line number Diff line Loading @@ -4070,7 +4070,7 @@ irqreturn_t smblib_handle_wdog_bark(int irq, void *data) if (rc < 0) smblib_err(chg, "Couldn't pet the dog rc=%d\n", rc); if (chg->step_chg_enabled) if (chg->step_chg_enabled || chg->sw_jeita_enabled) power_supply_changed(chg->batt_psy); return IRQ_HANDLED; Loading Loading @@ -4708,7 +4708,8 @@ int smblib_init(struct smb_charger *chg) return rc; } rc = qcom_step_chg_init(chg->step_chg_enabled); rc = qcom_step_chg_init(chg->step_chg_enabled, chg->sw_jeita_enabled); if (rc < 0) { smblib_err(chg, "Couldn't init qcom_step_chg_init rc=%d\n", rc); Loading drivers/power/supply/qcom/smb-lib.h +1 −0 Original line number Diff line number Diff line Loading @@ -306,6 +306,7 @@ struct smb_charger { int dcp_icl_ua; int fake_capacity; bool step_chg_enabled; bool sw_jeita_enabled; bool is_hdc; bool chg_done; bool micro_usb_mode; Loading drivers/power/supply/qcom/step-chg-jeita.c +270 −51 Original line number Diff line number Diff line Loading @@ -20,7 +20,7 @@ #define MAX_STEP_CHG_ENTRIES 8 #define STEP_CHG_VOTER "STEP_CHG_VOTER" #define STATUS_CHANGE_VOTER "STATUS_CHANGE_VOTER" #define JEITA_VOTER "JEITA_VOTER" #define is_between(left, right, value) \ (((left) >= (right) && (left) >= (value) \ Loading @@ -28,23 +28,44 @@ || ((left) <= (right) && (left) <= (value) \ && (value) <= (right))) struct step_chg_data { u32 vbatt_soc_low; u32 vbatt_soc_high; u32 fcc_ua; struct range_data { u32 low_threshold; u32 high_threshold; u32 value; }; struct step_chg_cfg { u32 psy_prop; char *prop_name; struct step_chg_data cfg[MAX_STEP_CHG_ENTRIES]; int hysteresis; struct range_data fcc_cfg[MAX_STEP_CHG_ENTRIES]; }; struct jeita_fcc_cfg { u32 psy_prop; char *prop_name; int hysteresis; struct range_data fcc_cfg[MAX_STEP_CHG_ENTRIES]; }; struct jeita_fv_cfg { u32 psy_prop; char *prop_name; int hysteresis; struct range_data fv_cfg[MAX_STEP_CHG_ENTRIES]; }; struct step_chg_info { ktime_t last_update_time; ktime_t step_last_update_time; ktime_t jeita_last_update_time; bool step_chg_enable; bool sw_jeita_enable; int jeita_fcc_index; int jeita_fv_index; int step_index; struct votable *fcc_votable; struct votable *fv_votable; struct wakeup_source *step_chg_ws; struct power_supply *batt_psy; struct delayed_work status_change_work; Loading @@ -53,26 +74,30 @@ struct step_chg_info { static struct step_chg_info *the_chip; #define STEP_CHG_HYSTERISIS_DELAY_US 5000000 /* 5 secs */ /* * Step Charging Configuration * Update the table based on the battery profile * Supports VBATT and SOC based source * range data must be in increasing ranges and shouldn't overlap */ static struct step_chg_cfg step_chg_config = { .psy_prop = POWER_SUPPLY_PROP_VOLTAGE_NOW, .prop_name = "VBATT", .cfg = { .hysteresis = 100000, /* 100mV */ .fcc_cfg = { /* VBAT_LOW VBAT_HIGH FCC */ {3600000, 4000000, 3000000}, {4000000, 4200000, 2800000}, {4200000, 4400000, 2000000}, {4001000, 4200000, 2800000}, {4201000, 4400000, 2000000}, }, /* * SOC STEP-CHG configuration example. * * .psy_prop = POWER_SUPPLY_PROP_CAPACITY, * .prop_name = "SOC", * .cfg = { * .fcc_cfg = { * //SOC_LOW SOC_HIGH FCC * {20, 70, 3000000}, * {70, 90, 2750000}, Loading @@ -81,6 +106,40 @@ static struct step_chg_cfg step_chg_config = { */ }; /* * Jeita Charging Configuration * Update the table based on the battery profile * Please ensure that the TEMP ranges are programmed in the hw so that * an interrupt is issued and a consequent psy changed will cause us to * react immediately. * range data must be in increasing ranges and shouldn't overlap. * Gaps are okay */ static struct jeita_fcc_cfg jeita_fcc_config = { .psy_prop = POWER_SUPPLY_PROP_TEMP, .prop_name = "BATT_TEMP", .hysteresis = 10, /* 1degC hysteresis */ .fcc_cfg = { /* TEMP_LOW TEMP_HIGH FCC */ {0, 100, 600000}, {101, 200, 2000000}, {201, 450, 3000000}, {451, 550, 600000}, }, }; static struct jeita_fv_cfg jeita_fv_config = { .psy_prop = POWER_SUPPLY_PROP_TEMP, .prop_name = "BATT_TEMP", .hysteresis = 10, /* 1degC hysteresis */ .fv_cfg = { /* TEMP_LOW TEMP_HIGH FCC */ {0, 100, 4200000}, {101, 450, 4400000}, {451, 550, 4200000}, }, }; static bool is_batt_available(struct step_chg_info *chip) { if (!chip->batt_psy) Loading @@ -92,22 +151,67 @@ static bool is_batt_available(struct step_chg_info *chip) return true; } static int get_fcc(int threshold) static int get_val(struct range_data *range, int hysteresis, int current_index, int threshold, int *new_index, int *val) { int i; *new_index = -EINVAL; /* first find the matching index without hysteresis */ for (i = 0; i < MAX_STEP_CHG_ENTRIES; i++) if (is_between(step_chg_config.cfg[i].vbatt_soc_low, step_chg_config.cfg[i].vbatt_soc_high, threshold)) return step_chg_config.cfg[i].fcc_ua; if (is_between(range[i].low_threshold, range[i].high_threshold, threshold)) { *new_index = i; *val = range[i].value; } /* if nothing was found, return -ENODATA */ if (*new_index == -EINVAL) return -ENODATA; /* * If we don't have a current_index return this * newfound value. There is no hysterisis from out of range * to in range transition */ if (current_index == -EINVAL) return 0; /* * Check for hysteresis if it in the neighbourhood * of our current index. */ if (*new_index == current_index + 1) { if (threshold < range[*new_index].low_threshold + hysteresis) { /* * Stay in the current index, threshold is not higher * by hysteresis amount */ *new_index = current_index; *val = range[current_index].value; } } else if (*new_index == current_index - 1) { if (threshold > range[*new_index].high_threshold - hysteresis) { /* * stay in the current index, threshold is not lower * by hysteresis amount */ *new_index = current_index; *val = range[current_index].value; } } return 0; } static int handle_step_chg_config(struct step_chg_info *chip) { union power_supply_propval pval = {0, }; int rc = 0, fcc_ua = 0; u64 elapsed_us; elapsed_us = ktime_us_delta(ktime_get(), chip->step_last_update_time); if (elapsed_us < STEP_CHG_HYSTERISIS_DELAY_US) goto reschedule; rc = power_supply_get_property(chip->batt_psy, POWER_SUPPLY_PROP_STEP_CHARGING_ENABLED, &pval); Loading @@ -119,7 +223,7 @@ static int handle_step_chg_config(struct step_chg_info *chip) if (!chip->step_chg_enable) { if (chip->fcc_votable) vote(chip->fcc_votable, STEP_CHG_VOTER, false, 0); return 0; goto update_time; } rc = power_supply_get_property(chip->batt_psy, Loading @@ -130,48 +234,144 @@ static int handle_step_chg_config(struct step_chg_info *chip) return rc; } chip->fcc_votable = find_votable("FCC"); if (!chip->fcc_votable) return -EINVAL; fcc_ua = get_fcc(pval.intval); if (fcc_ua < 0) { rc = get_val(step_chg_config.fcc_cfg, step_chg_config.hysteresis, chip->step_index, pval.intval, &chip->step_index, &fcc_ua); if (rc < 0) { /* remove the vote if no step-based fcc is found */ if (chip->fcc_votable) vote(chip->fcc_votable, STEP_CHG_VOTER, false, 0); return 0; goto update_time; } if (!chip->fcc_votable) chip->fcc_votable = find_votable("FCC"); if (!chip->fcc_votable) return -EINVAL; vote(chip->fcc_votable, STEP_CHG_VOTER, true, fcc_ua); pr_debug("%s = %d Step-FCC = %duA\n", step_chg_config.prop_name, pval.intval, fcc_ua); update_time: chip->step_last_update_time = ktime_get(); return 0; reschedule: /* reschedule 1000uS after the remaining time */ return (STEP_CHG_HYSTERISIS_DELAY_US - elapsed_us + 1000); } static int handle_jeita(struct step_chg_info *chip) { union power_supply_propval pval = {0, }; int rc = 0, fcc_ua = 0, fv_uv = 0; u64 elapsed_us; if (!chip->sw_jeita_enable) { if (chip->fcc_votable) vote(chip->fcc_votable, JEITA_VOTER, false, 0); if (chip->fv_votable) vote(chip->fv_votable, JEITA_VOTER, false, 0); return 0; } elapsed_us = ktime_us_delta(ktime_get(), chip->jeita_last_update_time); if (elapsed_us < STEP_CHG_HYSTERISIS_DELAY_US) goto reschedule; rc = power_supply_get_property(chip->batt_psy, jeita_fcc_config.psy_prop, &pval); if (rc < 0) { pr_err("Couldn't read %s property rc=%d\n", step_chg_config.prop_name, rc); return rc; } rc = get_val(jeita_fcc_config.fcc_cfg, jeita_fcc_config.hysteresis, chip->jeita_fcc_index, pval.intval, &chip->jeita_fcc_index, &fcc_ua); if (rc < 0) { /* remove the vote if no step-based fcc is found */ if (chip->fcc_votable) vote(chip->fcc_votable, JEITA_VOTER, false, 0); goto update_time; } if (!chip->fcc_votable) chip->fcc_votable = find_votable("FCC"); if (!chip->fcc_votable) /* changing FCC is a must */ return -EINVAL; vote(chip->fcc_votable, JEITA_VOTER, true, fcc_ua); rc = get_val(jeita_fv_config.fv_cfg, jeita_fv_config.hysteresis, chip->jeita_fv_index, pval.intval, &chip->jeita_fv_index, &fv_uv); if (rc < 0) { /* remove the vote if no step-based fcc is found */ if (chip->fv_votable) vote(chip->fv_votable, JEITA_VOTER, false, 0); goto update_time; } chip->fv_votable = find_votable("FV"); if (!chip->fv_votable) goto update_time; vote(chip->fv_votable, JEITA_VOTER, true, fv_uv); pr_debug("%s = %d FCC = %duA FV = %duV\n", step_chg_config.prop_name, pval.intval, fcc_ua, fv_uv); update_time: chip->jeita_last_update_time = ktime_get(); return 0; reschedule: /* reschedule 1000uS after the remaining time */ return (STEP_CHG_HYSTERISIS_DELAY_US - elapsed_us + 1000); } #define STEP_CHG_HYSTERISIS_DELAY_US 5000000 /* 5 secs */ static void status_change_work(struct work_struct *work) { struct step_chg_info *chip = container_of(work, struct step_chg_info, status_change_work.work); int rc = 0; u64 elapsed_us; elapsed_us = ktime_us_delta(ktime_get(), chip->last_update_time); if (elapsed_us < STEP_CHG_HYSTERISIS_DELAY_US) goto release_ws; int reschedule_us; int reschedule_jeita_work_us = 0; int reschedule_step_work_us = 0; if (!is_batt_available(chip)) goto release_ws; return; /* skip elapsed_us debounce for handling battery temperature */ rc = handle_jeita(chip); if (rc > 0) reschedule_jeita_work_us = rc; else if (rc < 0) pr_err("Couldn't handle sw jeita rc = %d\n", rc); rc = handle_step_chg_config(chip); if (rc > 0) reschedule_step_work_us = rc; if (rc < 0) goto release_ws; pr_err("Couldn't handle step rc = %d\n", rc); chip->last_update_time = ktime_get(); release_ws: reschedule_us = min(reschedule_jeita_work_us, reschedule_step_work_us); if (reschedule_us == 0) __pm_relax(chip->step_chg_ws); else schedule_delayed_work(&chip->status_change_work, usecs_to_jiffies(reschedule_us)); } static int step_chg_notifier_call(struct notifier_block *nb, Loading Loading @@ -205,7 +405,7 @@ static int step_chg_register_notifier(struct step_chg_info *chip) return 0; } int qcom_step_chg_init(bool step_chg_enable) int qcom_step_chg_init(bool step_chg_enable, bool sw_jeita_enable) { int rc; struct step_chg_info *chip; Loading @@ -226,6 +426,11 @@ int qcom_step_chg_init(bool step_chg_enable) } chip->step_chg_enable = step_chg_enable; chip->sw_jeita_enable = sw_jeita_enable; chip->step_index = -EINVAL; chip->jeita_fcc_index = -EINVAL; chip->jeita_fv_index = -EINVAL; if (step_chg_enable && (!step_chg_config.psy_prop || !step_chg_config.prop_name)) { Loading @@ -234,6 +439,20 @@ int qcom_step_chg_init(bool step_chg_enable) return -ENODATA; } if (sw_jeita_enable && (!jeita_fcc_config.psy_prop || !jeita_fcc_config.prop_name)) { /* fail if step-chg configuration is invalid */ pr_err("Jeita TEMP configuration not defined - fail\n"); return -ENODATA; } if (sw_jeita_enable && (!jeita_fv_config.psy_prop || !jeita_fv_config.prop_name)) { /* fail if step-chg configuration is invalid */ pr_err("Jeita TEMP configuration not defined - fail\n"); return -ENODATA; } INIT_DELAYED_WORK(&chip->status_change_work, status_change_work); rc = step_chg_register_notifier(chip); Loading Loading
Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb2.txt +4 −0 Original line number Diff line number Diff line Loading @@ -178,6 +178,10 @@ Charger specific properties: Definition: WD bark-timeout in seconds. The possible values are 16, 32, 64, 128. If not defined it defaults to 64. - qcom,sw-jeita-enable Usage: optional Value type: bool Definition: Boolean flag which when present enables sw compensation for jeita ============================================= Second Level Nodes - SMB2 Charger Peripherals Loading
drivers/power/supply/qcom/qpnp-smb2.c +3 −0 Original line number Diff line number Diff line Loading @@ -210,6 +210,9 @@ static int smb2_parse_dt(struct smb2 *chip) chg->step_chg_enabled = of_property_read_bool(node, "qcom,step-charging-enable"); chg->sw_jeita_enabled = of_property_read_bool(node, "qcom,sw-jeita-enable"); rc = of_property_read_u32(node, "qcom,wd-bark-time-secs", &chip->dt.wd_bark_time); if (rc < 0 || chip->dt.wd_bark_time < MIN_WD_BARK_TIME) Loading
drivers/power/supply/qcom/smb-lib.c +3 −2 Original line number Diff line number Diff line Loading @@ -4070,7 +4070,7 @@ irqreturn_t smblib_handle_wdog_bark(int irq, void *data) if (rc < 0) smblib_err(chg, "Couldn't pet the dog rc=%d\n", rc); if (chg->step_chg_enabled) if (chg->step_chg_enabled || chg->sw_jeita_enabled) power_supply_changed(chg->batt_psy); return IRQ_HANDLED; Loading Loading @@ -4708,7 +4708,8 @@ int smblib_init(struct smb_charger *chg) return rc; } rc = qcom_step_chg_init(chg->step_chg_enabled); rc = qcom_step_chg_init(chg->step_chg_enabled, chg->sw_jeita_enabled); if (rc < 0) { smblib_err(chg, "Couldn't init qcom_step_chg_init rc=%d\n", rc); Loading
drivers/power/supply/qcom/smb-lib.h +1 −0 Original line number Diff line number Diff line Loading @@ -306,6 +306,7 @@ struct smb_charger { int dcp_icl_ua; int fake_capacity; bool step_chg_enabled; bool sw_jeita_enabled; bool is_hdc; bool chg_done; bool micro_usb_mode; Loading
drivers/power/supply/qcom/step-chg-jeita.c +270 −51 Original line number Diff line number Diff line Loading @@ -20,7 +20,7 @@ #define MAX_STEP_CHG_ENTRIES 8 #define STEP_CHG_VOTER "STEP_CHG_VOTER" #define STATUS_CHANGE_VOTER "STATUS_CHANGE_VOTER" #define JEITA_VOTER "JEITA_VOTER" #define is_between(left, right, value) \ (((left) >= (right) && (left) >= (value) \ Loading @@ -28,23 +28,44 @@ || ((left) <= (right) && (left) <= (value) \ && (value) <= (right))) struct step_chg_data { u32 vbatt_soc_low; u32 vbatt_soc_high; u32 fcc_ua; struct range_data { u32 low_threshold; u32 high_threshold; u32 value; }; struct step_chg_cfg { u32 psy_prop; char *prop_name; struct step_chg_data cfg[MAX_STEP_CHG_ENTRIES]; int hysteresis; struct range_data fcc_cfg[MAX_STEP_CHG_ENTRIES]; }; struct jeita_fcc_cfg { u32 psy_prop; char *prop_name; int hysteresis; struct range_data fcc_cfg[MAX_STEP_CHG_ENTRIES]; }; struct jeita_fv_cfg { u32 psy_prop; char *prop_name; int hysteresis; struct range_data fv_cfg[MAX_STEP_CHG_ENTRIES]; }; struct step_chg_info { ktime_t last_update_time; ktime_t step_last_update_time; ktime_t jeita_last_update_time; bool step_chg_enable; bool sw_jeita_enable; int jeita_fcc_index; int jeita_fv_index; int step_index; struct votable *fcc_votable; struct votable *fv_votable; struct wakeup_source *step_chg_ws; struct power_supply *batt_psy; struct delayed_work status_change_work; Loading @@ -53,26 +74,30 @@ struct step_chg_info { static struct step_chg_info *the_chip; #define STEP_CHG_HYSTERISIS_DELAY_US 5000000 /* 5 secs */ /* * Step Charging Configuration * Update the table based on the battery profile * Supports VBATT and SOC based source * range data must be in increasing ranges and shouldn't overlap */ static struct step_chg_cfg step_chg_config = { .psy_prop = POWER_SUPPLY_PROP_VOLTAGE_NOW, .prop_name = "VBATT", .cfg = { .hysteresis = 100000, /* 100mV */ .fcc_cfg = { /* VBAT_LOW VBAT_HIGH FCC */ {3600000, 4000000, 3000000}, {4000000, 4200000, 2800000}, {4200000, 4400000, 2000000}, {4001000, 4200000, 2800000}, {4201000, 4400000, 2000000}, }, /* * SOC STEP-CHG configuration example. * * .psy_prop = POWER_SUPPLY_PROP_CAPACITY, * .prop_name = "SOC", * .cfg = { * .fcc_cfg = { * //SOC_LOW SOC_HIGH FCC * {20, 70, 3000000}, * {70, 90, 2750000}, Loading @@ -81,6 +106,40 @@ static struct step_chg_cfg step_chg_config = { */ }; /* * Jeita Charging Configuration * Update the table based on the battery profile * Please ensure that the TEMP ranges are programmed in the hw so that * an interrupt is issued and a consequent psy changed will cause us to * react immediately. * range data must be in increasing ranges and shouldn't overlap. * Gaps are okay */ static struct jeita_fcc_cfg jeita_fcc_config = { .psy_prop = POWER_SUPPLY_PROP_TEMP, .prop_name = "BATT_TEMP", .hysteresis = 10, /* 1degC hysteresis */ .fcc_cfg = { /* TEMP_LOW TEMP_HIGH FCC */ {0, 100, 600000}, {101, 200, 2000000}, {201, 450, 3000000}, {451, 550, 600000}, }, }; static struct jeita_fv_cfg jeita_fv_config = { .psy_prop = POWER_SUPPLY_PROP_TEMP, .prop_name = "BATT_TEMP", .hysteresis = 10, /* 1degC hysteresis */ .fv_cfg = { /* TEMP_LOW TEMP_HIGH FCC */ {0, 100, 4200000}, {101, 450, 4400000}, {451, 550, 4200000}, }, }; static bool is_batt_available(struct step_chg_info *chip) { if (!chip->batt_psy) Loading @@ -92,22 +151,67 @@ static bool is_batt_available(struct step_chg_info *chip) return true; } static int get_fcc(int threshold) static int get_val(struct range_data *range, int hysteresis, int current_index, int threshold, int *new_index, int *val) { int i; *new_index = -EINVAL; /* first find the matching index without hysteresis */ for (i = 0; i < MAX_STEP_CHG_ENTRIES; i++) if (is_between(step_chg_config.cfg[i].vbatt_soc_low, step_chg_config.cfg[i].vbatt_soc_high, threshold)) return step_chg_config.cfg[i].fcc_ua; if (is_between(range[i].low_threshold, range[i].high_threshold, threshold)) { *new_index = i; *val = range[i].value; } /* if nothing was found, return -ENODATA */ if (*new_index == -EINVAL) return -ENODATA; /* * If we don't have a current_index return this * newfound value. There is no hysterisis from out of range * to in range transition */ if (current_index == -EINVAL) return 0; /* * Check for hysteresis if it in the neighbourhood * of our current index. */ if (*new_index == current_index + 1) { if (threshold < range[*new_index].low_threshold + hysteresis) { /* * Stay in the current index, threshold is not higher * by hysteresis amount */ *new_index = current_index; *val = range[current_index].value; } } else if (*new_index == current_index - 1) { if (threshold > range[*new_index].high_threshold - hysteresis) { /* * stay in the current index, threshold is not lower * by hysteresis amount */ *new_index = current_index; *val = range[current_index].value; } } return 0; } static int handle_step_chg_config(struct step_chg_info *chip) { union power_supply_propval pval = {0, }; int rc = 0, fcc_ua = 0; u64 elapsed_us; elapsed_us = ktime_us_delta(ktime_get(), chip->step_last_update_time); if (elapsed_us < STEP_CHG_HYSTERISIS_DELAY_US) goto reschedule; rc = power_supply_get_property(chip->batt_psy, POWER_SUPPLY_PROP_STEP_CHARGING_ENABLED, &pval); Loading @@ -119,7 +223,7 @@ static int handle_step_chg_config(struct step_chg_info *chip) if (!chip->step_chg_enable) { if (chip->fcc_votable) vote(chip->fcc_votable, STEP_CHG_VOTER, false, 0); return 0; goto update_time; } rc = power_supply_get_property(chip->batt_psy, Loading @@ -130,48 +234,144 @@ static int handle_step_chg_config(struct step_chg_info *chip) return rc; } chip->fcc_votable = find_votable("FCC"); if (!chip->fcc_votable) return -EINVAL; fcc_ua = get_fcc(pval.intval); if (fcc_ua < 0) { rc = get_val(step_chg_config.fcc_cfg, step_chg_config.hysteresis, chip->step_index, pval.intval, &chip->step_index, &fcc_ua); if (rc < 0) { /* remove the vote if no step-based fcc is found */ if (chip->fcc_votable) vote(chip->fcc_votable, STEP_CHG_VOTER, false, 0); return 0; goto update_time; } if (!chip->fcc_votable) chip->fcc_votable = find_votable("FCC"); if (!chip->fcc_votable) return -EINVAL; vote(chip->fcc_votable, STEP_CHG_VOTER, true, fcc_ua); pr_debug("%s = %d Step-FCC = %duA\n", step_chg_config.prop_name, pval.intval, fcc_ua); update_time: chip->step_last_update_time = ktime_get(); return 0; reschedule: /* reschedule 1000uS after the remaining time */ return (STEP_CHG_HYSTERISIS_DELAY_US - elapsed_us + 1000); } static int handle_jeita(struct step_chg_info *chip) { union power_supply_propval pval = {0, }; int rc = 0, fcc_ua = 0, fv_uv = 0; u64 elapsed_us; if (!chip->sw_jeita_enable) { if (chip->fcc_votable) vote(chip->fcc_votable, JEITA_VOTER, false, 0); if (chip->fv_votable) vote(chip->fv_votable, JEITA_VOTER, false, 0); return 0; } elapsed_us = ktime_us_delta(ktime_get(), chip->jeita_last_update_time); if (elapsed_us < STEP_CHG_HYSTERISIS_DELAY_US) goto reschedule; rc = power_supply_get_property(chip->batt_psy, jeita_fcc_config.psy_prop, &pval); if (rc < 0) { pr_err("Couldn't read %s property rc=%d\n", step_chg_config.prop_name, rc); return rc; } rc = get_val(jeita_fcc_config.fcc_cfg, jeita_fcc_config.hysteresis, chip->jeita_fcc_index, pval.intval, &chip->jeita_fcc_index, &fcc_ua); if (rc < 0) { /* remove the vote if no step-based fcc is found */ if (chip->fcc_votable) vote(chip->fcc_votable, JEITA_VOTER, false, 0); goto update_time; } if (!chip->fcc_votable) chip->fcc_votable = find_votable("FCC"); if (!chip->fcc_votable) /* changing FCC is a must */ return -EINVAL; vote(chip->fcc_votable, JEITA_VOTER, true, fcc_ua); rc = get_val(jeita_fv_config.fv_cfg, jeita_fv_config.hysteresis, chip->jeita_fv_index, pval.intval, &chip->jeita_fv_index, &fv_uv); if (rc < 0) { /* remove the vote if no step-based fcc is found */ if (chip->fv_votable) vote(chip->fv_votable, JEITA_VOTER, false, 0); goto update_time; } chip->fv_votable = find_votable("FV"); if (!chip->fv_votable) goto update_time; vote(chip->fv_votable, JEITA_VOTER, true, fv_uv); pr_debug("%s = %d FCC = %duA FV = %duV\n", step_chg_config.prop_name, pval.intval, fcc_ua, fv_uv); update_time: chip->jeita_last_update_time = ktime_get(); return 0; reschedule: /* reschedule 1000uS after the remaining time */ return (STEP_CHG_HYSTERISIS_DELAY_US - elapsed_us + 1000); } #define STEP_CHG_HYSTERISIS_DELAY_US 5000000 /* 5 secs */ static void status_change_work(struct work_struct *work) { struct step_chg_info *chip = container_of(work, struct step_chg_info, status_change_work.work); int rc = 0; u64 elapsed_us; elapsed_us = ktime_us_delta(ktime_get(), chip->last_update_time); if (elapsed_us < STEP_CHG_HYSTERISIS_DELAY_US) goto release_ws; int reschedule_us; int reschedule_jeita_work_us = 0; int reschedule_step_work_us = 0; if (!is_batt_available(chip)) goto release_ws; return; /* skip elapsed_us debounce for handling battery temperature */ rc = handle_jeita(chip); if (rc > 0) reschedule_jeita_work_us = rc; else if (rc < 0) pr_err("Couldn't handle sw jeita rc = %d\n", rc); rc = handle_step_chg_config(chip); if (rc > 0) reschedule_step_work_us = rc; if (rc < 0) goto release_ws; pr_err("Couldn't handle step rc = %d\n", rc); chip->last_update_time = ktime_get(); release_ws: reschedule_us = min(reschedule_jeita_work_us, reschedule_step_work_us); if (reschedule_us == 0) __pm_relax(chip->step_chg_ws); else schedule_delayed_work(&chip->status_change_work, usecs_to_jiffies(reschedule_us)); } static int step_chg_notifier_call(struct notifier_block *nb, Loading Loading @@ -205,7 +405,7 @@ static int step_chg_register_notifier(struct step_chg_info *chip) return 0; } int qcom_step_chg_init(bool step_chg_enable) int qcom_step_chg_init(bool step_chg_enable, bool sw_jeita_enable) { int rc; struct step_chg_info *chip; Loading @@ -226,6 +426,11 @@ int qcom_step_chg_init(bool step_chg_enable) } chip->step_chg_enable = step_chg_enable; chip->sw_jeita_enable = sw_jeita_enable; chip->step_index = -EINVAL; chip->jeita_fcc_index = -EINVAL; chip->jeita_fv_index = -EINVAL; if (step_chg_enable && (!step_chg_config.psy_prop || !step_chg_config.prop_name)) { Loading @@ -234,6 +439,20 @@ int qcom_step_chg_init(bool step_chg_enable) return -ENODATA; } if (sw_jeita_enable && (!jeita_fcc_config.psy_prop || !jeita_fcc_config.prop_name)) { /* fail if step-chg configuration is invalid */ pr_err("Jeita TEMP configuration not defined - fail\n"); return -ENODATA; } if (sw_jeita_enable && (!jeita_fv_config.psy_prop || !jeita_fv_config.prop_name)) { /* fail if step-chg configuration is invalid */ pr_err("Jeita TEMP configuration not defined - fail\n"); return -ENODATA; } INIT_DELAYED_WORK(&chip->status_change_work, status_change_work); rc = step_chg_register_notifier(chip); Loading