Loading Documentation/devicetree/bindings/power/qpnp-fg-gen3.txt +6 −0 Original line number Diff line number Diff line Loading @@ -385,6 +385,12 @@ First Level Node - FG Gen3 device property "qcom,slope-limit-temp-threshold" to make dynamic slope limit adjustment functional. - qcom,fg-use-sw-esr Usage: optional Value type: <empty> Definition: A boolean property when defined uses software based ESR during charging. ========================================================== Second Level Nodes - Peripherals managed by FG Gen3 driver ========================================================== Loading arch/arm/boot/dts/qcom/msm8909w-pm660-mtp.dtsi +4 −0 Original line number Diff line number Diff line Loading @@ -314,4 +314,8 @@ qcom,fg-rsense-sel = <1>; /* External rsense */ qcom,fg-cutoff-voltage = <3400>; qcom,fg-recharge-voltage = <4100>; qcom,fg-use-sw-esr; qcom,fg-esr-pulse-thresh-ma = <40>; qcom,fg-esr-meas-curr-ma = <60>; }; drivers/power/fg-core.h +18 −1 Original line number Diff line number Diff line /* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. /* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading @@ -24,8 +24,10 @@ #include <linux/module.h> #include <linux/mutex.h> #include <linux/spmi.h> #include <linux/alarmtimer.h> #include <linux/power_supply.h> #include <linux/slab.h> #include <linux/spinlock.h> #include <linux/string_helpers.h> #include <linux/types.h> #include <linux/uaccess.h> Loading @@ -50,6 +52,7 @@ #define SRAM_WRITE "fg_sram_write" #define PROFILE_LOAD "fg_profile_load" #define DELTA_SOC "fg_delta_soc" #define FG_ESR_VOTER "fg_esr_voter" /* Delta BSOC irq votable reasons */ #define DELTA_BSOC_IRQ_VOTER "fg_delta_bsoc_irq" Loading Loading @@ -94,6 +97,11 @@ enum fg_debug_flag { FG_TTF = BIT(8), /* Show time to full */ }; enum awake_reasons { FG_SW_ESR_WAKE = BIT(0), FG_STATUS_NOTIFY_WAKE = BIT(1), }; /* SRAM access */ enum sram_access_flags { FG_IMA_DEFAULT = 0, Loading Loading @@ -233,6 +241,7 @@ struct fg_dt_props { bool force_load_profile; bool hold_soc_while_full; bool auto_recharge_soc; bool use_esr_sw; int cutoff_volt_mv; int empty_volt_mv; int vbatt_low_thr_mv; Loading Loading @@ -375,15 +384,19 @@ struct fg_chip { struct fg_cyc_ctr_data cyc_ctr; struct notifier_block nb; struct fg_cap_learning cl; struct alarm esr_sw_timer; struct mutex bus_lock; struct mutex sram_rw_lock; struct mutex batt_avg_lock; struct mutex charge_full_lock; spinlock_t awake_lock; u32 batt_soc_base; u32 batt_info_base; u32 mem_if_base; u32 rradc_base; u32 wa_flags; u32 esr_wakeup_ms; u32 awake_status; int batt_id_ohms; int ki_coeff_full_soc; int charge_status; Loading @@ -410,11 +423,13 @@ struct fg_chip { bool esr_flt_cold_temp_en; bool slope_limit_en; bool use_ima_single_mode; bool usb_present; struct completion soc_update; struct completion soc_ready; struct delayed_work profile_load_work; struct work_struct status_change_work; struct work_struct cycle_count_work; struct work_struct esr_sw_work; struct delayed_work batt_avg_work; struct delayed_work sram_dump_work; struct fg_circ_buf ibatt_circ_buf; Loading Loading @@ -477,4 +492,6 @@ extern void fg_circ_buf_add(struct fg_circ_buf *, int); extern void fg_circ_buf_clr(struct fg_circ_buf *); extern int fg_circ_buf_avg(struct fg_circ_buf *, int *); extern int fg_lerp(const struct fg_pt *, size_t, s32, s32 *); void fg_stay_awake(struct fg_chip *chip, int awake_reason); void fg_relax(struct fg_chip *chip, int awake_reason); #endif drivers/power/fg-util.c +25 −1 Original line number Diff line number Diff line /* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. /* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading Loading @@ -935,3 +935,27 @@ err_remove_fs: debugfs_remove_recursive(chip->dfs_root); return -ENOMEM; } void fg_stay_awake(struct fg_chip *chip, int awake_reason) { spin_lock(&chip->awake_lock); if (!chip->awake_status) pm_stay_awake(chip->dev); chip->awake_status |= awake_reason; spin_unlock(&chip->awake_lock); } void fg_relax(struct fg_chip *chip, int awake_reason) { spin_lock(&chip->awake_lock); chip->awake_status &= ~awake_reason; if (!chip->awake_status) pm_relax(chip->dev); spin_unlock(&chip->awake_lock); } drivers/power/qpnp-fg-gen3.c +193 −4 Original line number Diff line number Diff line /* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. /* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading @@ -16,6 +16,8 @@ #include <linux/of.h> #include <linux/of_irq.h> #include <linux/spmi.h> #include <linux/spinlock.h> #include <linux/alarmtimer.h> #include <linux/of_batterydata.h> #include <linux/iio/consumer.h> #include <linux/qpnp/qpnp-revid.h> Loading Loading @@ -74,6 +76,8 @@ #define ESR_TIMER_CHG_MAX_OFFSET 0 #define ESR_TIMER_CHG_INIT_WORD 18 #define ESR_TIMER_CHG_INIT_OFFSET 2 #define ESR_EXTRACTION_ENABLE_WORD 19 #define ESR_EXTRACTION_ENABLE_OFFSET 0 #define PROFILE_LOAD_WORD 24 #define PROFILE_LOAD_OFFSET 0 #define ESR_RSLOW_DISCHG_WORD 34 Loading Loading @@ -1175,6 +1179,18 @@ static bool batt_psy_initialized(struct fg_chip *chip) return true; } static bool usb_psy_initialized(struct fg_chip *chip) { if (chip->usb_psy) return true; chip->usb_psy = power_supply_get_by_name("usb"); if (!chip->usb_psy) return false; return true; } static bool is_parallel_charger_available(struct fg_chip *chip) { if (!chip->parallel_psy) Loading Loading @@ -2149,6 +2165,142 @@ static void fg_batt_avg_update(struct fg_chip *chip) msecs_to_jiffies(2000)); } #define ESR_SW_FCC_UA 100000 /* 100mA */ #define ESR_EXTRACTION_ENABLE_MASK BIT(0) static void fg_esr_sw_work(struct work_struct *work) { struct fg_chip *chip = container_of(work, struct fg_chip, esr_sw_work); union power_supply_propval pval = {0, }; int rc, esr_uohms = 0; vote(chip->awake_votable, FG_ESR_VOTER, true, 0); /* * Enable ESR extraction just before we reduce the FCC * to make sure that FG extracts the ESR. Disable ESR * extraction after FCC reduction is complete to prevent * any further HW pulses. */ rc = fg_sram_masked_write(chip, ESR_EXTRACTION_ENABLE_WORD, ESR_EXTRACTION_ENABLE_OFFSET, ESR_EXTRACTION_ENABLE_MASK, 0x1, FG_IMA_DEFAULT); if (rc < 0) { pr_err("Failed to enable ESR extraction rc=%d\n", rc); goto done; } /* delay for 1 FG cycle to complete */ msleep(1500); /* for FCC to 100mA */ pval.intval = ESR_SW_FCC_UA; rc = power_supply_set_property(chip->batt_psy, POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, &pval); if (rc < 0) { pr_err("Failed to set FCC to 100mA rc=%d\n", rc); goto done; } /* delay for ESR readings */ msleep(3000); /* FCC to 0 (removes vote) */ pval.intval = 0; rc = power_supply_set_property(chip->batt_psy, POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, &pval); if (rc < 0) { pr_err("Failed to remove FCC vote rc=%d\n", rc); goto done; } fg_get_sram_prop(chip, FG_SRAM_ESR, &esr_uohms); fg_dbg(chip, FG_STATUS, "SW ESR done ESR=%d\n", esr_uohms); /* restart the alarm timer */ alarm_start_relative(&chip->esr_sw_timer, ms_to_ktime(chip->esr_wakeup_ms)); done: rc = fg_sram_masked_write(chip, ESR_EXTRACTION_ENABLE_WORD, ESR_EXTRACTION_ENABLE_OFFSET, ESR_EXTRACTION_ENABLE_MASK, 0x0, FG_IMA_DEFAULT); if (rc < 0) pr_err("Failed to disable ESR extraction rc=%d\n", rc); vote(chip->awake_votable, FG_ESR_VOTER, false, 0); fg_relax(chip, FG_SW_ESR_WAKE); } static enum alarmtimer_restart fg_esr_sw_timer(struct alarm *alarm, ktime_t now) { struct fg_chip *chip = container_of(alarm, struct fg_chip, esr_sw_timer); if (!chip->usb_present) return ALARMTIMER_NORESTART; fg_stay_awake(chip, FG_SW_ESR_WAKE); schedule_work(&chip->esr_sw_work); return ALARMTIMER_NORESTART; } static int fg_config_esr_sw(struct fg_chip *chip) { int rc; union power_supply_propval prop = {0, }; if (!chip->dt.use_esr_sw) return 0; if (!usb_psy_initialized(chip)) return 0; rc = power_supply_get_property(chip->usb_psy, POWER_SUPPLY_PROP_PRESENT, &prop); if (rc < 0) { pr_err("Error in reading usb-status rc = %d\n", rc); return rc; } if (chip->usb_present != prop.intval) { chip->usb_present = prop.intval; fg_dbg(chip, FG_STATUS, "USB status changed=%d\n", chip->usb_present); /* cancel any pending work */ alarm_cancel(&chip->esr_sw_timer); cancel_work_sync(&chip->esr_sw_work); if (chip->usb_present) { /* disable ESR extraction across the charging cycle */ rc = fg_sram_masked_write(chip, ESR_EXTRACTION_ENABLE_WORD, ESR_EXTRACTION_ENABLE_OFFSET, ESR_EXTRACTION_ENABLE_MASK, 0x0, FG_IMA_DEFAULT); if (rc < 0) return rc; /* wake up early for the first ESR on insertion */ alarm_start_relative(&chip->esr_sw_timer, ms_to_ktime(chip->esr_wakeup_ms / 2)); } else { /* enable ESR extraction on removal */ rc = fg_sram_masked_write(chip, ESR_EXTRACTION_ENABLE_WORD, ESR_EXTRACTION_ENABLE_OFFSET, ESR_EXTRACTION_ENABLE_MASK, 0x1, FG_IMA_DEFAULT); if (rc < 0) return rc; } } return 0; } static void status_change_work(struct work_struct *work) { struct fg_chip *chip = container_of(work, Loading @@ -2166,6 +2318,10 @@ static void status_change_work(struct work_struct *work) goto out; } rc = fg_config_esr_sw(chip); if (rc < 0) pr_err("Failed to config SW ESR rc=%d\n", rc); rc = power_supply_get_property(chip->batt_psy, POWER_SUPPLY_PROP_STATUS, &prop); if (rc < 0) { Loading Loading @@ -2234,7 +2390,7 @@ static void status_change_work(struct work_struct *work) out: fg_dbg(chip, FG_STATUS, "charge_status:%d charge_type:%d charge_done:%d\n", chip->charge_status, chip->charge_type, chip->charge_done); pm_relax(chip->dev); fg_relax(chip, FG_STATUS_NOTIFY_WAKE); } static void restore_cycle_counter(struct fg_chip *chip) Loading Loading @@ -2629,7 +2785,7 @@ out: chip->soc_reporting_ready = true; vote(chip->awake_votable, PROFILE_LOAD, false, 0); if (!work_pending(&chip->status_change_work)) { pm_stay_awake(chip->dev); fg_stay_awake(chip, FG_STATUS_NOTIFY_WAKE); schedule_work(&chip->status_change_work); } } Loading Loading @@ -3197,7 +3353,7 @@ static int fg_notifier_cb(struct notifier_block *nb, * We cannot vote for awake votable here as that takes * a mutex lock and this is executed in an atomic context. */ pm_stay_awake(chip->dev); fg_stay_awake(chip, FG_STATUS_NOTIFY_WAKE); schedule_work(&chip->status_change_work); } Loading Loading @@ -3500,6 +3656,18 @@ static int fg_hw_init(struct fg_chip *chip) } } if (chip->dt.use_esr_sw) { /* Enable ESR extraction explicitly */ rc = fg_sram_masked_write(chip, ESR_EXTRACTION_ENABLE_WORD, ESR_EXTRACTION_ENABLE_OFFSET, ESR_EXTRACTION_ENABLE_MASK, 0x1, FG_IMA_DEFAULT); if (rc < 0) { pr_err("Error in enabling ESR extraction rc=%d\n", rc); return rc; } } return 0; } Loading Loading @@ -4374,6 +4542,8 @@ static int fg_parse_dt(struct fg_chip *chip) chip->dt.esr_meas_curr_ma = temp; } chip->dt.use_esr_sw = of_property_read_bool(node, "qcom,fg-use-sw-esr"); return 0; } Loading Loading @@ -4482,11 +4652,13 @@ static int fg_gen3_probe(struct spmi_device *spmi) mutex_init(&chip->cl.lock); mutex_init(&chip->batt_avg_lock); mutex_init(&chip->charge_full_lock); spin_lock_init(&chip->awake_lock); init_completion(&chip->soc_update); init_completion(&chip->soc_ready); INIT_DELAYED_WORK(&chip->profile_load_work, profile_load_work); INIT_WORK(&chip->status_change_work, status_change_work); INIT_WORK(&chip->cycle_count_work, cycle_count_work); INIT_WORK(&chip->esr_sw_work, fg_esr_sw_work); INIT_DELAYED_WORK(&chip->batt_avg_work, batt_avg_work); INIT_DELAYED_WORK(&chip->sram_dump_work, sram_dump_work); dev_set_drvdata(&spmi->dev, chip); Loading @@ -4505,6 +4677,23 @@ static int fg_gen3_probe(struct spmi_device *spmi) goto exit; } if (chip->dt.use_esr_sw) { if (alarmtimer_get_rtcdev()) { alarm_init(&chip->esr_sw_timer, ALARM_BOOTTIME, fg_esr_sw_timer); } else { pr_err("Failed to get esw_sw alarm-timer\n"); /* RTC always registers, hence defer until it passes */ rc = -EPROBE_DEFER; goto exit; } if (chip->dt.esr_timer_charging[TIMER_MAX] != -EINVAL) chip->esr_wakeup_ms = chip->dt.esr_timer_charging[TIMER_MAX] * 1460; else chip->esr_wakeup_ms = 140000; /* 140 seconds */ } chip->fg_psy.name = "bms"; chip->fg_psy.type = POWER_SUPPLY_TYPE_BMS; chip->fg_psy.properties = fg_psy_props; Loading Loading
Documentation/devicetree/bindings/power/qpnp-fg-gen3.txt +6 −0 Original line number Diff line number Diff line Loading @@ -385,6 +385,12 @@ First Level Node - FG Gen3 device property "qcom,slope-limit-temp-threshold" to make dynamic slope limit adjustment functional. - qcom,fg-use-sw-esr Usage: optional Value type: <empty> Definition: A boolean property when defined uses software based ESR during charging. ========================================================== Second Level Nodes - Peripherals managed by FG Gen3 driver ========================================================== Loading
arch/arm/boot/dts/qcom/msm8909w-pm660-mtp.dtsi +4 −0 Original line number Diff line number Diff line Loading @@ -314,4 +314,8 @@ qcom,fg-rsense-sel = <1>; /* External rsense */ qcom,fg-cutoff-voltage = <3400>; qcom,fg-recharge-voltage = <4100>; qcom,fg-use-sw-esr; qcom,fg-esr-pulse-thresh-ma = <40>; qcom,fg-esr-meas-curr-ma = <60>; };
drivers/power/fg-core.h +18 −1 Original line number Diff line number Diff line /* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. /* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading @@ -24,8 +24,10 @@ #include <linux/module.h> #include <linux/mutex.h> #include <linux/spmi.h> #include <linux/alarmtimer.h> #include <linux/power_supply.h> #include <linux/slab.h> #include <linux/spinlock.h> #include <linux/string_helpers.h> #include <linux/types.h> #include <linux/uaccess.h> Loading @@ -50,6 +52,7 @@ #define SRAM_WRITE "fg_sram_write" #define PROFILE_LOAD "fg_profile_load" #define DELTA_SOC "fg_delta_soc" #define FG_ESR_VOTER "fg_esr_voter" /* Delta BSOC irq votable reasons */ #define DELTA_BSOC_IRQ_VOTER "fg_delta_bsoc_irq" Loading Loading @@ -94,6 +97,11 @@ enum fg_debug_flag { FG_TTF = BIT(8), /* Show time to full */ }; enum awake_reasons { FG_SW_ESR_WAKE = BIT(0), FG_STATUS_NOTIFY_WAKE = BIT(1), }; /* SRAM access */ enum sram_access_flags { FG_IMA_DEFAULT = 0, Loading Loading @@ -233,6 +241,7 @@ struct fg_dt_props { bool force_load_profile; bool hold_soc_while_full; bool auto_recharge_soc; bool use_esr_sw; int cutoff_volt_mv; int empty_volt_mv; int vbatt_low_thr_mv; Loading Loading @@ -375,15 +384,19 @@ struct fg_chip { struct fg_cyc_ctr_data cyc_ctr; struct notifier_block nb; struct fg_cap_learning cl; struct alarm esr_sw_timer; struct mutex bus_lock; struct mutex sram_rw_lock; struct mutex batt_avg_lock; struct mutex charge_full_lock; spinlock_t awake_lock; u32 batt_soc_base; u32 batt_info_base; u32 mem_if_base; u32 rradc_base; u32 wa_flags; u32 esr_wakeup_ms; u32 awake_status; int batt_id_ohms; int ki_coeff_full_soc; int charge_status; Loading @@ -410,11 +423,13 @@ struct fg_chip { bool esr_flt_cold_temp_en; bool slope_limit_en; bool use_ima_single_mode; bool usb_present; struct completion soc_update; struct completion soc_ready; struct delayed_work profile_load_work; struct work_struct status_change_work; struct work_struct cycle_count_work; struct work_struct esr_sw_work; struct delayed_work batt_avg_work; struct delayed_work sram_dump_work; struct fg_circ_buf ibatt_circ_buf; Loading Loading @@ -477,4 +492,6 @@ extern void fg_circ_buf_add(struct fg_circ_buf *, int); extern void fg_circ_buf_clr(struct fg_circ_buf *); extern int fg_circ_buf_avg(struct fg_circ_buf *, int *); extern int fg_lerp(const struct fg_pt *, size_t, s32, s32 *); void fg_stay_awake(struct fg_chip *chip, int awake_reason); void fg_relax(struct fg_chip *chip, int awake_reason); #endif
drivers/power/fg-util.c +25 −1 Original line number Diff line number Diff line /* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. /* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading Loading @@ -935,3 +935,27 @@ err_remove_fs: debugfs_remove_recursive(chip->dfs_root); return -ENOMEM; } void fg_stay_awake(struct fg_chip *chip, int awake_reason) { spin_lock(&chip->awake_lock); if (!chip->awake_status) pm_stay_awake(chip->dev); chip->awake_status |= awake_reason; spin_unlock(&chip->awake_lock); } void fg_relax(struct fg_chip *chip, int awake_reason) { spin_lock(&chip->awake_lock); chip->awake_status &= ~awake_reason; if (!chip->awake_status) pm_relax(chip->dev); spin_unlock(&chip->awake_lock); }
drivers/power/qpnp-fg-gen3.c +193 −4 Original line number Diff line number Diff line /* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. /* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading @@ -16,6 +16,8 @@ #include <linux/of.h> #include <linux/of_irq.h> #include <linux/spmi.h> #include <linux/spinlock.h> #include <linux/alarmtimer.h> #include <linux/of_batterydata.h> #include <linux/iio/consumer.h> #include <linux/qpnp/qpnp-revid.h> Loading Loading @@ -74,6 +76,8 @@ #define ESR_TIMER_CHG_MAX_OFFSET 0 #define ESR_TIMER_CHG_INIT_WORD 18 #define ESR_TIMER_CHG_INIT_OFFSET 2 #define ESR_EXTRACTION_ENABLE_WORD 19 #define ESR_EXTRACTION_ENABLE_OFFSET 0 #define PROFILE_LOAD_WORD 24 #define PROFILE_LOAD_OFFSET 0 #define ESR_RSLOW_DISCHG_WORD 34 Loading Loading @@ -1175,6 +1179,18 @@ static bool batt_psy_initialized(struct fg_chip *chip) return true; } static bool usb_psy_initialized(struct fg_chip *chip) { if (chip->usb_psy) return true; chip->usb_psy = power_supply_get_by_name("usb"); if (!chip->usb_psy) return false; return true; } static bool is_parallel_charger_available(struct fg_chip *chip) { if (!chip->parallel_psy) Loading Loading @@ -2149,6 +2165,142 @@ static void fg_batt_avg_update(struct fg_chip *chip) msecs_to_jiffies(2000)); } #define ESR_SW_FCC_UA 100000 /* 100mA */ #define ESR_EXTRACTION_ENABLE_MASK BIT(0) static void fg_esr_sw_work(struct work_struct *work) { struct fg_chip *chip = container_of(work, struct fg_chip, esr_sw_work); union power_supply_propval pval = {0, }; int rc, esr_uohms = 0; vote(chip->awake_votable, FG_ESR_VOTER, true, 0); /* * Enable ESR extraction just before we reduce the FCC * to make sure that FG extracts the ESR. Disable ESR * extraction after FCC reduction is complete to prevent * any further HW pulses. */ rc = fg_sram_masked_write(chip, ESR_EXTRACTION_ENABLE_WORD, ESR_EXTRACTION_ENABLE_OFFSET, ESR_EXTRACTION_ENABLE_MASK, 0x1, FG_IMA_DEFAULT); if (rc < 0) { pr_err("Failed to enable ESR extraction rc=%d\n", rc); goto done; } /* delay for 1 FG cycle to complete */ msleep(1500); /* for FCC to 100mA */ pval.intval = ESR_SW_FCC_UA; rc = power_supply_set_property(chip->batt_psy, POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, &pval); if (rc < 0) { pr_err("Failed to set FCC to 100mA rc=%d\n", rc); goto done; } /* delay for ESR readings */ msleep(3000); /* FCC to 0 (removes vote) */ pval.intval = 0; rc = power_supply_set_property(chip->batt_psy, POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, &pval); if (rc < 0) { pr_err("Failed to remove FCC vote rc=%d\n", rc); goto done; } fg_get_sram_prop(chip, FG_SRAM_ESR, &esr_uohms); fg_dbg(chip, FG_STATUS, "SW ESR done ESR=%d\n", esr_uohms); /* restart the alarm timer */ alarm_start_relative(&chip->esr_sw_timer, ms_to_ktime(chip->esr_wakeup_ms)); done: rc = fg_sram_masked_write(chip, ESR_EXTRACTION_ENABLE_WORD, ESR_EXTRACTION_ENABLE_OFFSET, ESR_EXTRACTION_ENABLE_MASK, 0x0, FG_IMA_DEFAULT); if (rc < 0) pr_err("Failed to disable ESR extraction rc=%d\n", rc); vote(chip->awake_votable, FG_ESR_VOTER, false, 0); fg_relax(chip, FG_SW_ESR_WAKE); } static enum alarmtimer_restart fg_esr_sw_timer(struct alarm *alarm, ktime_t now) { struct fg_chip *chip = container_of(alarm, struct fg_chip, esr_sw_timer); if (!chip->usb_present) return ALARMTIMER_NORESTART; fg_stay_awake(chip, FG_SW_ESR_WAKE); schedule_work(&chip->esr_sw_work); return ALARMTIMER_NORESTART; } static int fg_config_esr_sw(struct fg_chip *chip) { int rc; union power_supply_propval prop = {0, }; if (!chip->dt.use_esr_sw) return 0; if (!usb_psy_initialized(chip)) return 0; rc = power_supply_get_property(chip->usb_psy, POWER_SUPPLY_PROP_PRESENT, &prop); if (rc < 0) { pr_err("Error in reading usb-status rc = %d\n", rc); return rc; } if (chip->usb_present != prop.intval) { chip->usb_present = prop.intval; fg_dbg(chip, FG_STATUS, "USB status changed=%d\n", chip->usb_present); /* cancel any pending work */ alarm_cancel(&chip->esr_sw_timer); cancel_work_sync(&chip->esr_sw_work); if (chip->usb_present) { /* disable ESR extraction across the charging cycle */ rc = fg_sram_masked_write(chip, ESR_EXTRACTION_ENABLE_WORD, ESR_EXTRACTION_ENABLE_OFFSET, ESR_EXTRACTION_ENABLE_MASK, 0x0, FG_IMA_DEFAULT); if (rc < 0) return rc; /* wake up early for the first ESR on insertion */ alarm_start_relative(&chip->esr_sw_timer, ms_to_ktime(chip->esr_wakeup_ms / 2)); } else { /* enable ESR extraction on removal */ rc = fg_sram_masked_write(chip, ESR_EXTRACTION_ENABLE_WORD, ESR_EXTRACTION_ENABLE_OFFSET, ESR_EXTRACTION_ENABLE_MASK, 0x1, FG_IMA_DEFAULT); if (rc < 0) return rc; } } return 0; } static void status_change_work(struct work_struct *work) { struct fg_chip *chip = container_of(work, Loading @@ -2166,6 +2318,10 @@ static void status_change_work(struct work_struct *work) goto out; } rc = fg_config_esr_sw(chip); if (rc < 0) pr_err("Failed to config SW ESR rc=%d\n", rc); rc = power_supply_get_property(chip->batt_psy, POWER_SUPPLY_PROP_STATUS, &prop); if (rc < 0) { Loading Loading @@ -2234,7 +2390,7 @@ static void status_change_work(struct work_struct *work) out: fg_dbg(chip, FG_STATUS, "charge_status:%d charge_type:%d charge_done:%d\n", chip->charge_status, chip->charge_type, chip->charge_done); pm_relax(chip->dev); fg_relax(chip, FG_STATUS_NOTIFY_WAKE); } static void restore_cycle_counter(struct fg_chip *chip) Loading Loading @@ -2629,7 +2785,7 @@ out: chip->soc_reporting_ready = true; vote(chip->awake_votable, PROFILE_LOAD, false, 0); if (!work_pending(&chip->status_change_work)) { pm_stay_awake(chip->dev); fg_stay_awake(chip, FG_STATUS_NOTIFY_WAKE); schedule_work(&chip->status_change_work); } } Loading Loading @@ -3197,7 +3353,7 @@ static int fg_notifier_cb(struct notifier_block *nb, * We cannot vote for awake votable here as that takes * a mutex lock and this is executed in an atomic context. */ pm_stay_awake(chip->dev); fg_stay_awake(chip, FG_STATUS_NOTIFY_WAKE); schedule_work(&chip->status_change_work); } Loading Loading @@ -3500,6 +3656,18 @@ static int fg_hw_init(struct fg_chip *chip) } } if (chip->dt.use_esr_sw) { /* Enable ESR extraction explicitly */ rc = fg_sram_masked_write(chip, ESR_EXTRACTION_ENABLE_WORD, ESR_EXTRACTION_ENABLE_OFFSET, ESR_EXTRACTION_ENABLE_MASK, 0x1, FG_IMA_DEFAULT); if (rc < 0) { pr_err("Error in enabling ESR extraction rc=%d\n", rc); return rc; } } return 0; } Loading Loading @@ -4374,6 +4542,8 @@ static int fg_parse_dt(struct fg_chip *chip) chip->dt.esr_meas_curr_ma = temp; } chip->dt.use_esr_sw = of_property_read_bool(node, "qcom,fg-use-sw-esr"); return 0; } Loading Loading @@ -4482,11 +4652,13 @@ static int fg_gen3_probe(struct spmi_device *spmi) mutex_init(&chip->cl.lock); mutex_init(&chip->batt_avg_lock); mutex_init(&chip->charge_full_lock); spin_lock_init(&chip->awake_lock); init_completion(&chip->soc_update); init_completion(&chip->soc_ready); INIT_DELAYED_WORK(&chip->profile_load_work, profile_load_work); INIT_WORK(&chip->status_change_work, status_change_work); INIT_WORK(&chip->cycle_count_work, cycle_count_work); INIT_WORK(&chip->esr_sw_work, fg_esr_sw_work); INIT_DELAYED_WORK(&chip->batt_avg_work, batt_avg_work); INIT_DELAYED_WORK(&chip->sram_dump_work, sram_dump_work); dev_set_drvdata(&spmi->dev, chip); Loading @@ -4505,6 +4677,23 @@ static int fg_gen3_probe(struct spmi_device *spmi) goto exit; } if (chip->dt.use_esr_sw) { if (alarmtimer_get_rtcdev()) { alarm_init(&chip->esr_sw_timer, ALARM_BOOTTIME, fg_esr_sw_timer); } else { pr_err("Failed to get esw_sw alarm-timer\n"); /* RTC always registers, hence defer until it passes */ rc = -EPROBE_DEFER; goto exit; } if (chip->dt.esr_timer_charging[TIMER_MAX] != -EINVAL) chip->esr_wakeup_ms = chip->dt.esr_timer_charging[TIMER_MAX] * 1460; else chip->esr_wakeup_ms = 140000; /* 140 seconds */ } chip->fg_psy.name = "bms"; chip->fg_psy.type = POWER_SUPPLY_TYPE_BMS; chip->fg_psy.properties = fg_psy_props; Loading