Loading drivers/power/supply/qcom/qg-core.h +16 −0 Original line number Original line Diff line number Diff line Loading @@ -36,6 +36,10 @@ struct qg_dt { int s2_vbat_low_fifo_length; int s2_vbat_low_fifo_length; int s2_acc_length; int s2_acc_length; int s2_acc_intvl_ms; int s2_acc_intvl_ms; int sleep_s2_fifo_length; int sleep_s2_acc_length; int sleep_s2_acc_intvl_ms; int fast_chg_s2_fifo_length; int ocv_timer_expiry_min; int ocv_timer_expiry_min; int ocv_tol_threshold_uv; int ocv_tol_threshold_uv; int s3_entry_fifo_length; int s3_entry_fifo_length; Loading @@ -61,6 +65,8 @@ struct qg_dt { bool esr_discharge_enable; bool esr_discharge_enable; bool qg_ext_sense; bool qg_ext_sense; bool use_s7_ocv; bool use_s7_ocv; bool qg_sleep_config; bool qg_fast_chg_cfg; }; }; struct qg_esr_data { struct qg_esr_data { Loading @@ -86,6 +92,7 @@ struct qpnp_qg { struct work_struct udata_work; struct work_struct udata_work; struct work_struct scale_soc_work; struct work_struct scale_soc_work; struct work_struct qg_status_change_work; struct work_struct qg_status_change_work; struct delayed_work qg_sleep_exit_work; struct notifier_block nb; struct notifier_block nb; struct mutex bus_lock; struct mutex bus_lock; struct mutex data_lock; struct mutex data_lock; Loading Loading @@ -136,6 +143,8 @@ struct qpnp_qg { u32 charge_counter_uah; u32 charge_counter_uah; u32 esr_avg; u32 esr_avg; u32 esr_last; u32 esr_last; u32 s2_state; u32 s2_state_mask; ktime_t last_user_update_time; ktime_t last_user_update_time; ktime_t last_fifo_update_time; ktime_t last_fifo_update_time; unsigned long last_maint_soc_update_time; unsigned long last_maint_soc_update_time; Loading Loading @@ -182,6 +191,13 @@ enum ocv_type { PON_OCV_MAX, PON_OCV_MAX, }; }; enum s2_state { S2_FAST_CHARGING = BIT(0), S2_LOW_VBAT = BIT(1), S2_SLEEP = BIT(2), S2_DEFAULT = BIT(3), }; enum debug_mask { enum debug_mask { QG_DEBUG_PON = BIT(0), QG_DEBUG_PON = BIT(0), QG_DEBUG_PROFILE = BIT(1), QG_DEBUG_PROFILE = BIT(1), Loading drivers/power/supply/qcom/qg-defs.h +3 −1 Original line number Original line Diff line number Diff line /* SPDX-License-Identifier: GPL-2.0 */ /* SPDX-License-Identifier: GPL-2.0 */ /* /* * Copyright (c) 2018 The Linux Foundation. All rights reserved. * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. */ */ #ifndef __QG_DEFS_H__ #ifndef __QG_DEFS_H__ Loading Loading @@ -28,6 +28,8 @@ #define PROFILE_IRQ_DISABLE "NO_PROFILE_IRQ_DISABLE" #define PROFILE_IRQ_DISABLE "NO_PROFILE_IRQ_DISABLE" #define QG_INIT_STATE_IRQ_DISABLE "QG_INIT_STATE_IRQ_DISABLE" #define QG_INIT_STATE_IRQ_DISABLE "QG_INIT_STATE_IRQ_DISABLE" #define TTF_AWAKE_VOTER "TTF_AWAKE_VOTER" #define TTF_AWAKE_VOTER "TTF_AWAKE_VOTER" #define SLEEP_EXIT_DATA_VOTER "SLEEP_EXIT_DATA_VOTER" #define SLEEP_EXIT_VOTER "SLEEP_EXIT_VOTER" #define V_RAW_TO_UV(V_RAW) div_u64(194637ULL * (u64)V_RAW, 1000) #define V_RAW_TO_UV(V_RAW) div_u64(194637ULL * (u64)V_RAW, 1000) #define FIFO_V_RESET_VAL 0x8000 #define FIFO_V_RESET_VAL 0x8000 Loading drivers/power/supply/qcom/qpnp-qg.c +256 −44 Original line number Original line Diff line number Diff line Loading @@ -20,6 +20,7 @@ #include <linux/power_supply.h> #include <linux/power_supply.h> #include <linux/regmap.h> #include <linux/regmap.h> #include <linux/slab.h> #include <linux/slab.h> #include <linux/timekeeping.h> #include <linux/uaccess.h> #include <linux/uaccess.h> #include <linux/pmic-voter.h> #include <linux/pmic-voter.h> #include <linux/iio/consumer.h> #include <linux/iio/consumer.h> Loading Loading @@ -89,6 +90,8 @@ static struct attribute *qg_attrs[] = { }; }; ATTRIBUTE_GROUPS(qg); ATTRIBUTE_GROUPS(qg); static int qg_process_rt_fifo(struct qpnp_qg *chip); static bool is_battery_present(struct qpnp_qg *chip) static bool is_battery_present(struct qpnp_qg *chip) { { u8 reg = 0; u8 reg = 0; Loading Loading @@ -179,7 +182,7 @@ static int qg_update_fifo_length(struct qpnp_qg *chip, u8 length) pr_err("Failed to write S2 FIFO length, rc=%d\n", rc); pr_err("Failed to write S2 FIFO length, rc=%d\n", rc); /* update the S3 FIFO length, when S2 length is updated */ /* update the S3 FIFO length, when S2 length is updated */ if (length > 3) if (length > 3 && !chip->dt.qg_sleep_config) s3_entry_fifo_length = (chip->dt.s3_entry_fifo_length > 0) ? s3_entry_fifo_length = (chip->dt.s3_entry_fifo_length > 0) ? chip->dt.s3_entry_fifo_length : DEFAULT_S3_FIFO_LENGTH; chip->dt.s3_entry_fifo_length : DEFAULT_S3_FIFO_LENGTH; else /* Use S3 length as 1 for any S2 length <= 3 */ else /* Use S3 length as 1 for any S2 length <= 3 */ Loading Loading @@ -308,6 +311,111 @@ static int qg_store_soc_params(struct qpnp_qg *chip) return rc; return rc; } } static int qg_config_s2_state(struct qpnp_qg *chip, enum s2_state requested_state, bool state_enable, bool process_fifo) { int rc, acc_interval, acc_length; u8 fifo_length, reg = 0, state = S2_DEFAULT; if ((chip->s2_state_mask & requested_state) && (state_enable == true)) return 0; /* No change in state */ if (!(chip->s2_state_mask & requested_state) && (state_enable == false)) return 0; /* No change in state */ if (state_enable) chip->s2_state_mask |= requested_state; else chip->s2_state_mask &= ~requested_state; /* define the priority of the states */ if (chip->s2_state_mask & S2_FAST_CHARGING) state = S2_FAST_CHARGING; else if (chip->s2_state_mask & S2_LOW_VBAT) state = S2_LOW_VBAT; else if (chip->s2_state_mask & S2_SLEEP) state = S2_SLEEP; else state = S2_DEFAULT; if (state == chip->s2_state) return 0; switch (state) { case S2_FAST_CHARGING: fifo_length = chip->dt.fast_chg_s2_fifo_length; acc_interval = chip->dt.s2_acc_intvl_ms; acc_length = chip->dt.s2_acc_length; break; case S2_LOW_VBAT: fifo_length = chip->dt.s2_vbat_low_fifo_length; acc_interval = chip->dt.s2_acc_intvl_ms; acc_length = chip->dt.s2_acc_length; break; case S2_SLEEP: fifo_length = chip->dt.sleep_s2_fifo_length; acc_interval = chip->dt.sleep_s2_acc_intvl_ms; acc_length = chip->dt.sleep_s2_acc_length; break; case S2_DEFAULT: fifo_length = chip->dt.s2_fifo_length; acc_interval = chip->dt.s2_acc_intvl_ms; acc_length = chip->dt.s2_acc_length; break; default: pr_err("Invalid S2 state %d\n", state); return -EINVAL; } rc = qg_master_hold(chip, true); if (rc < 0) { pr_err("Failed to hold master, rc=%d\n", rc); return rc; } if (process_fifo) { rc = qg_process_rt_fifo(chip); if (rc < 0) { pr_err("Failed to process FIFO real-time, rc=%d\n", rc); goto done; } } rc = qg_update_fifo_length(chip, fifo_length); if (rc < 0) { pr_err("Failed to update S2 fifo-length, rc=%d\n", rc); goto done; } reg = acc_interval / 10; rc = qg_write(chip, chip->qg_base + QG_S2_NORMAL_MEAS_CTL3_REG, ®, 1); if (rc < 0) { pr_err("Failed to update S2 acc intrvl, rc=%d\n", rc); goto done; } reg = ilog2(acc_length) - 1; rc = qg_masked_write(chip, chip->qg_base + QG_S2_NORMAL_MEAS_CTL2_REG, NUM_OF_ACCUM_MASK, reg); if (rc < 0) { pr_err("Failed to update S2 ACC length, rc=%d\n", rc); goto done; } chip->s2_state = state; qg_dbg(chip, QG_DEBUG_STATUS, "S2 New state=%x fifo_length=%d interval=%d acc_length=%d\n", state, fifo_length, acc_interval, acc_length); done: qg_master_hold(chip, false); /* FIFO restarted */ chip->last_fifo_update_time = ktime_get_boottime(); return rc; } static int qg_process_fifo(struct qpnp_qg *chip, u32 fifo_length) static int qg_process_fifo(struct qpnp_qg *chip, u32 fifo_length) { { int rc = 0, i, j = 0, temp; int rc = 0, i, j = 0, temp; Loading Loading @@ -492,7 +600,7 @@ static int qg_process_rt_fifo(struct qpnp_qg *chip) static int process_rt_fifo_data(struct qpnp_qg *chip, bool update_smb) static int process_rt_fifo_data(struct qpnp_qg *chip, bool update_smb) { { int rc = 0; int rc = 0; ktime_t now = ktime_get(); ktime_t now = ktime_get_boottime(); s64 time_delta; s64 time_delta; /* /* Loading Loading @@ -537,7 +645,7 @@ static int process_rt_fifo_data(struct qpnp_qg *chip, bool update_smb) goto done; goto done; } } /* FIFOs restarted */ /* FIFOs restarted */ chip->last_fifo_update_time = ktime_get(); chip->last_fifo_update_time = ktime_get_boottime(); /* signal the read thread */ /* signal the read thread */ chip->data_ready = true; chip->data_ready = true; Loading @@ -559,7 +667,7 @@ static int process_rt_fifo_data(struct qpnp_qg *chip, bool update_smb) static int qg_vbat_low_wa(struct qpnp_qg *chip) static int qg_vbat_low_wa(struct qpnp_qg *chip) { { int rc, i, temp = 0; int rc, i, temp = 0; u32 vbat_low_uv = 0, fifo_length = 0; u32 vbat_low_uv = 0; if ((chip->wa_flags & QG_VBAT_LOW_WA) && chip->vbat_low) { if ((chip->wa_flags & QG_VBAT_LOW_WA) && chip->vbat_low) { rc = qg_get_battery_temp(chip, &temp); rc = qg_get_battery_temp(chip, &temp); Loading Loading @@ -589,37 +697,11 @@ static int qg_vbat_low_wa(struct qpnp_qg *chip) } } } } rc = get_fifo_length(chip, &fifo_length, false); rc = qg_config_s2_state(chip, S2_LOW_VBAT, if (rc < 0) { chip->vbat_low ? true : false, false); pr_err("Failed to get FIFO length, rc=%d\n", rc); return rc; } if (chip->vbat_low && fifo_length == chip->dt.s2_vbat_low_fifo_length) return 0; if (!chip->vbat_low && fifo_length == chip->dt.s2_fifo_length) return 0; rc = qg_master_hold(chip, true); if (rc < 0) { pr_err("Failed to hold master, rc=%d\n", rc); goto done; } fifo_length = chip->vbat_low ? chip->dt.s2_vbat_low_fifo_length : chip->dt.s2_fifo_length; rc = qg_update_fifo_length(chip, fifo_length); if (rc < 0) if (rc < 0) goto done; pr_err("Failed to configure for VBAT_LOW rc=%d\n", rc); qg_dbg(chip, QG_DEBUG_STATUS, "FIFO length updated to %d vbat_low=%d\n", fifo_length, chip->vbat_low); done: qg_master_hold(chip, false); /* FIFOs restarted */ chip->last_fifo_update_time = ktime_get(); return rc; return rc; } } Loading Loading @@ -690,6 +772,22 @@ static int qg_vbat_thresholds_config(struct qpnp_qg *chip) return rc; return rc; } } static int qg_fast_charge_config(struct qpnp_qg *chip) { int rc = 0; if (!chip->dt.qg_fast_chg_cfg) return 0; rc = qg_config_s2_state(chip, S2_FAST_CHARGING, (chip->charge_status == POWER_SUPPLY_STATUS_CHARGING) ? true : false, false); if (rc < 0) pr_err("Failed to exit S2_SLEEP rc=%d\n", rc); return rc; } static void qg_retrieve_esr_params(struct qpnp_qg *chip) static void qg_retrieve_esr_params(struct qpnp_qg *chip) { { u32 data = 0; u32 data = 0; Loading Loading @@ -1035,7 +1133,7 @@ static int qg_esr_estimate(struct qpnp_qg *chip) goto done; goto done; } } /* FIFOs restarted */ /* FIFOs restarted */ chip->last_fifo_update_time = ktime_get(); chip->last_fifo_update_time = ktime_get_boottime(); if (chip->esr_avg) { if (chip->esr_avg) { chip->kdata.param[QG_ESR].data = chip->esr_avg; chip->kdata.param[QG_ESR].data = chip->esr_avg; Loading Loading @@ -1122,7 +1220,7 @@ static void process_udata_work(struct work_struct *work) #define MAX_FIFO_DELTA_PERCENT 10 #define MAX_FIFO_DELTA_PERCENT 10 static irqreturn_t qg_fifo_update_done_handler(int irq, void *data) static irqreturn_t qg_fifo_update_done_handler(int irq, void *data) { { ktime_t now = ktime_get(); ktime_t now = ktime_get_boottime(); int rc, hw_delta_ms = 0, margin_ms = 0; int rc, hw_delta_ms = 0, margin_ms = 0; u32 fifo_length = 0; u32 fifo_length = 0; s64 time_delta_ms = 0; s64 time_delta_ms = 0; Loading Loading @@ -1153,6 +1251,10 @@ static irqreturn_t qg_fifo_update_done_handler(int irq, void *data) if (rc < 0) if (rc < 0) pr_err("Failed to apply VBAT EMPTY config rc=%d\n", rc); pr_err("Failed to apply VBAT EMPTY config rc=%d\n", rc); rc = qg_fast_charge_config(chip); if (rc < 0) pr_err("Failed to apply fast-charge config rc=%d\n", rc); rc = qg_vbat_low_wa(chip); rc = qg_vbat_low_wa(chip); if (rc < 0) { if (rc < 0) { pr_err("Failed to apply VBAT LOW WA, rc=%d\n", rc); pr_err("Failed to apply VBAT LOW WA, rc=%d\n", rc); Loading Loading @@ -1893,6 +1995,9 @@ static int qg_psy_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_CAPACITY: case POWER_SUPPLY_PROP_CAPACITY: rc = qg_get_battery_capacity(chip, &pval->intval); rc = qg_get_battery_capacity(chip, &pval->intval); break; break; case POWER_SUPPLY_PROP_CAPACITY_RAW: pval->intval = chip->sys_soc; break; case POWER_SUPPLY_PROP_REAL_CAPACITY: case POWER_SUPPLY_PROP_REAL_CAPACITY: rc = qg_get_battery_capacity_real(chip, &pval->intval); rc = qg_get_battery_capacity_real(chip, &pval->intval); break; break; Loading Loading @@ -2022,6 +2127,7 @@ static int qg_property_is_writeable(struct power_supply *psy, static enum power_supply_property qg_psy_props[] = { static enum power_supply_property qg_psy_props[] = { POWER_SUPPLY_PROP_CAPACITY, POWER_SUPPLY_PROP_CAPACITY, POWER_SUPPLY_PROP_CAPACITY_RAW, POWER_SUPPLY_PROP_REAL_CAPACITY, POWER_SUPPLY_PROP_REAL_CAPACITY, POWER_SUPPLY_PROP_TEMP, POWER_SUPPLY_PROP_TEMP, POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_VOLTAGE_NOW, Loading Loading @@ -2308,6 +2414,33 @@ static int qg_battery_status_update(struct qpnp_qg *chip) return rc; return rc; } } static void qg_sleep_exit_work(struct work_struct *work) { int rc; struct qpnp_qg *chip = container_of(work, struct qpnp_qg, qg_sleep_exit_work.work); vote(chip->awake_votable, SLEEP_EXIT_VOTER, true, 0); mutex_lock(&chip->data_lock); /* * if this work is executing, the system has been active * for a while. So, force back the S2 active configuration */ qg_dbg(chip, QG_DEBUG_STATUS, "sleep_exit_work: exit S2_SLEEP\n"); rc = qg_config_s2_state(chip, S2_SLEEP, false, true); if (rc < 0) pr_err("Failed to exit S2_SLEEP rc=%d\n", rc); vote(chip->awake_votable, SLEEP_EXIT_DATA_VOTER, true, 0); /* signal the read thread */ chip->data_ready = true; wake_up_interruptible(&chip->qg_wait_q); mutex_unlock(&chip->data_lock); vote(chip->awake_votable, SLEEP_EXIT_VOTER, false, 0); } static void qg_status_change_work(struct work_struct *work) static void qg_status_change_work(struct work_struct *work) { { Loading Loading @@ -2480,6 +2613,7 @@ static ssize_t qg_device_read(struct file *file, char __user *buf, size_t count, vote(chip->awake_votable, FIFO_DONE_VOTER, false, 0); vote(chip->awake_votable, FIFO_DONE_VOTER, false, 0); vote(chip->awake_votable, FIFO_RT_DONE_VOTER, false, 0); vote(chip->awake_votable, FIFO_RT_DONE_VOTER, false, 0); vote(chip->awake_votable, SUSPEND_DATA_VOTER, false, 0); vote(chip->awake_votable, SUSPEND_DATA_VOTER, false, 0); vote(chip->awake_votable, SLEEP_EXIT_DATA_VOTER, false, 0); qg_dbg(chip, QG_DEBUG_DEVICE, qg_dbg(chip, QG_DEBUG_DEVICE, "QG device read complete Seq_no=%u Size=%ld\n", "QG device read complete Seq_no=%u Size=%ld\n", Loading Loading @@ -2822,7 +2956,7 @@ static int qg_determine_pon_soc(struct qpnp_qg *chip) bool use_pon_ocv = true; bool use_pon_ocv = true; unsigned long rtc_sec = 0; unsigned long rtc_sec = 0; u32 ocv_uv = 0, soc = 0, pon_soc = 0, full_soc = 0, cutoff_soc = 0; u32 ocv_uv = 0, soc = 0, pon_soc = 0, full_soc = 0, cutoff_soc = 0; u32 shutdown[SDAM_MAX] = {0}; u32 shutdown[SDAM_MAX] = {0}, soc_raw = 0; char ocv_type[20] = "NONE"; char ocv_type[20] = "NONE"; if (!chip->profile_loaded) { if (!chip->profile_loaded) { Loading Loading @@ -2901,6 +3035,7 @@ static int qg_determine_pon_soc(struct qpnp_qg *chip) use_pon_ocv = false; use_pon_ocv = false; ocv_uv = shutdown[SDAM_OCV_UV]; ocv_uv = shutdown[SDAM_OCV_UV]; soc = shutdown[SDAM_SOC]; soc = shutdown[SDAM_SOC]; soc_raw = shutdown[SDAM_SOC] * 100; strlcpy(ocv_type, "SHUTDOWN_SOC", 20); strlcpy(ocv_type, "SHUTDOWN_SOC", 20); qg_dbg(chip, QG_DEBUG_PON, "Using SHUTDOWN_SOC @ PON\n"); qg_dbg(chip, QG_DEBUG_PON, "Using SHUTDOWN_SOC @ PON\n"); Loading Loading @@ -2968,7 +3103,9 @@ static int qg_determine_pon_soc(struct qpnp_qg *chip) soc = DIV_ROUND_UP(((pon_soc - cutoff_soc) * 100), soc = DIV_ROUND_UP(((pon_soc - cutoff_soc) * 100), (full_soc - cutoff_soc)); (full_soc - cutoff_soc)); soc = CAP(0, 100, soc); soc = CAP(0, 100, soc); soc_raw = soc * 100; } else { } else { soc_raw = pon_soc * 100; soc = pon_soc; soc = pon_soc; } } Loading @@ -2982,6 +3119,7 @@ static int qg_determine_pon_soc(struct qpnp_qg *chip) return rc; return rc; } } chip->cc_soc = chip->sys_soc = soc_raw; chip->last_adj_ssoc = chip->catch_up_soc = chip->msoc = soc; chip->last_adj_ssoc = chip->catch_up_soc = chip->msoc = soc; chip->kdata.param[QG_PON_OCV_UV].data = ocv_uv; chip->kdata.param[QG_PON_OCV_UV].data = ocv_uv; chip->kdata.param[QG_PON_OCV_UV].valid = true; chip->kdata.param[QG_PON_OCV_UV].valid = true; Loading Loading @@ -3098,6 +3236,8 @@ static int qg_hw_init(struct qpnp_qg *chip) } } } } chip->s2_state = S2_DEFAULT; chip->s2_state_mask |= S2_DEFAULT; /* signal the read thread */ /* signal the read thread */ chip->data_ready = true; chip->data_ready = true; wake_up_interruptible(&chip->qg_wait_q); wake_up_interruptible(&chip->qg_wait_q); Loading @@ -3108,7 +3248,7 @@ static int qg_hw_init(struct qpnp_qg *chip) pr_err("Failed to release master, rc=%d\n", rc); pr_err("Failed to release master, rc=%d\n", rc); return rc; return rc; } } chip->last_fifo_update_time = ktime_get(); chip->last_fifo_update_time = ktime_get_boottime(); if (chip->dt.ocv_timer_expiry_min != -EINVAL) { if (chip->dt.ocv_timer_expiry_min != -EINVAL) { if (chip->dt.ocv_timer_expiry_min < 2) if (chip->dt.ocv_timer_expiry_min < 2) Loading Loading @@ -3452,6 +3592,10 @@ static void qg_create_debugfs(struct qpnp_qg *chip) #define DEFAULT_S2_VBAT_LOW_LENGTH 2 #define DEFAULT_S2_VBAT_LOW_LENGTH 2 #define DEFAULT_S2_ACC_LENGTH 128 #define DEFAULT_S2_ACC_LENGTH 128 #define DEFAULT_S2_ACC_INTVL_MS 100 #define DEFAULT_S2_ACC_INTVL_MS 100 #define DEFAULT_SLEEP_S2_FIFO_LENGTH 8 #define DEFAULT_SLEEP_S2_ACC_LENGTH 256 #define DEFAULT_SLEEP_S2_ACC_INTVL_MS 200 #define DEFAULT_FAST_CHG_S2_FIFO_LENGTH 1 static int qg_parse_s2_dt(struct qpnp_qg *chip) static int qg_parse_s2_dt(struct qpnp_qg *chip) { { int rc; int rc; Loading Loading @@ -3487,6 +3631,48 @@ static int qg_parse_s2_dt(struct qpnp_qg *chip) chip->dt.s2_fifo_length, chip->dt.s2_vbat_low_fifo_length, chip->dt.s2_fifo_length, chip->dt.s2_vbat_low_fifo_length, chip->dt.s2_acc_length, chip->dt.s2_acc_intvl_ms); chip->dt.s2_acc_length, chip->dt.s2_acc_intvl_ms); if (of_property_read_bool(node, "qcom,qg-sleep-config")) { chip->dt.qg_sleep_config = true; rc = of_property_read_u32(node, "qcom,sleep-s2-fifo-length", &temp); if (rc < 0) chip->dt.sleep_s2_fifo_length = DEFAULT_SLEEP_S2_FIFO_LENGTH; else chip->dt.sleep_s2_fifo_length = temp; rc = of_property_read_u32(node, "qcom,sleep-s2-acc-length", &temp); if (rc < 0) chip->dt.sleep_s2_acc_length = DEFAULT_SLEEP_S2_ACC_LENGTH; else chip->dt.sleep_s2_acc_length = temp; rc = of_property_read_u32(node, "qcom,sleep-s2-acc-intvl-ms", &temp); if (rc < 0) chip->dt.sleep_s2_acc_intvl_ms = DEFAULT_SLEEP_S2_ACC_INTVL_MS; else chip->dt.sleep_s2_acc_intvl_ms = temp; } if (of_property_read_bool(node, "qcom,qg-fast-chg-config")) { chip->dt.qg_fast_chg_cfg = true; rc = of_property_read_u32(node, "qcom,fast-chg-s2-fifo-length", &temp); if (rc < 0) chip->dt.fast_chg_s2_fifo_length = DEFAULT_FAST_CHG_S2_FIFO_LENGTH; else chip->dt.fast_chg_s2_fifo_length = temp; } return 0; return 0; } } Loading Loading @@ -3842,6 +4028,7 @@ static int process_suspend(struct qpnp_qg *chip) return 0; return 0; cancel_delayed_work_sync(&chip->ttf->ttf_work); cancel_delayed_work_sync(&chip->ttf->ttf_work); cancel_delayed_work_sync(&chip->qg_sleep_exit_work); chip->suspend_data = false; chip->suspend_data = false; Loading @@ -3850,6 +4037,13 @@ static int process_suspend(struct qpnp_qg *chip) /* ignore any suspend processing if we are charging */ /* ignore any suspend processing if we are charging */ if (chip->charge_status == POWER_SUPPLY_STATUS_CHARGING) { if (chip->charge_status == POWER_SUPPLY_STATUS_CHARGING) { /* Reset the sleep config if we are charging */ if (chip->dt.qg_sleep_config) { qg_dbg(chip, QG_DEBUG_STATUS, "Suspend: Charging - Exit S2_SLEEP\n"); rc = qg_config_s2_state(chip, S2_SLEEP, false, true); if (rc < 0) pr_err("Failed to exit S2-sleep rc=%d\n", rc); } qg_dbg(chip, QG_DEBUG_PM, "Charging @ suspend - ignore processing\n"); qg_dbg(chip, QG_DEBUG_PM, "Charging @ suspend - ignore processing\n"); return 0; return 0; } } Loading @@ -3867,12 +4061,23 @@ static int process_suspend(struct qpnp_qg *chip) return rc; return rc; } } sleep_fifo_length &= SLEEP_IBAT_QUALIFIED_LENGTH_MASK; sleep_fifo_length &= SLEEP_IBAT_QUALIFIED_LENGTH_MASK; if (chip->dt.qg_sleep_config) { qg_dbg(chip, QG_DEBUG_STATUS, "Suspend: Forcing S2_SLEEP\n"); rc = qg_config_s2_state(chip, S2_SLEEP, true, true); if (rc < 0) pr_err("Failed to config S2_SLEEP rc=%d\n", rc); if (chip->kdata.fifo_length > 0) chip->suspend_data = true; } else if (fifo_rt_length >= (chip->dt.s2_fifo_length - sleep_fifo_length)) { /* /* * If the real-time FIFO count is greater than * If the real-time FIFO count is greater than * the the #fifo to enter sleep, save the FIFO data * the the #fifo to enter sleep, save the FIFO data * and reset the fifo count. * and reset the fifo count. This is avoid a gauranteed wakeup * due to fifo_done event as the curent FIFO length is already * beyond the sleep length. */ */ if (fifo_rt_length >= (chip->dt.s2_fifo_length - sleep_fifo_length)) { rc = qg_master_hold(chip, true); rc = qg_master_hold(chip, true); if (rc < 0) { if (rc < 0) { pr_err("Failed to hold master, rc=%d\n", rc); pr_err("Failed to hold master, rc=%d\n", rc); Loading @@ -3892,7 +4097,7 @@ static int process_suspend(struct qpnp_qg *chip) return rc; return rc; } } /* FIFOs restarted */ /* FIFOs restarted */ chip->last_fifo_update_time = ktime_get(); chip->last_fifo_update_time = ktime_get_boottime(); chip->suspend_data = true; chip->suspend_data = true; } } Loading @@ -3907,6 +4112,7 @@ static int process_suspend(struct qpnp_qg *chip) return rc; return rc; } } #define QG_SLEEP_EXIT_TIME_MS 15000 /* 15 secs */ static int process_resume(struct qpnp_qg *chip) static int process_resume(struct qpnp_qg *chip) { { u8 status2 = 0, rt_status = 0; u8 status2 = 0, rt_status = 0; Loading @@ -3921,6 +4127,10 @@ static int process_resume(struct qpnp_qg *chip) get_rtc_time(&rtc_sec); get_rtc_time(&rtc_sec); sleep_time_secs = rtc_sec - chip->suspend_time; sleep_time_secs = rtc_sec - chip->suspend_time; if (chip->dt.qg_sleep_config) schedule_delayed_work(&chip->qg_sleep_exit_work, msecs_to_jiffies(QG_SLEEP_EXIT_TIME_MS)); rc = qg_read(chip, chip->qg_base + QG_STATUS2_REG, &status2, 1); rc = qg_read(chip, chip->qg_base + QG_STATUS2_REG, &status2, 1); if (rc < 0) { if (rc < 0) { pr_err("Failed to read status2 register, rc=%d\n", rc); pr_err("Failed to read status2 register, rc=%d\n", rc); Loading Loading @@ -4088,6 +4298,7 @@ static int qpnp_qg_probe(struct platform_device *pdev) platform_set_drvdata(pdev, chip); platform_set_drvdata(pdev, chip); INIT_WORK(&chip->udata_work, process_udata_work); INIT_WORK(&chip->udata_work, process_udata_work); INIT_WORK(&chip->qg_status_change_work, qg_status_change_work); INIT_WORK(&chip->qg_status_change_work, qg_status_change_work); INIT_DELAYED_WORK(&chip->qg_sleep_exit_work, qg_sleep_exit_work); mutex_init(&chip->bus_lock); mutex_init(&chip->bus_lock); mutex_init(&chip->soc_lock); mutex_init(&chip->soc_lock); mutex_init(&chip->data_lock); mutex_init(&chip->data_lock); Loading Loading @@ -4256,6 +4467,7 @@ static int qpnp_qg_remove(struct platform_device *pdev) qg_batterydata_exit(); qg_batterydata_exit(); qg_soc_exit(chip); qg_soc_exit(chip); cancel_delayed_work_sync(&chip->qg_sleep_exit_work); cancel_work_sync(&chip->udata_work); cancel_work_sync(&chip->udata_work); cancel_work_sync(&chip->qg_status_change_work); cancel_work_sync(&chip->qg_status_change_work); sysfs_remove_groups(&chip->dev->kobj, qg_groups); sysfs_remove_groups(&chip->dev->kobj, qg_groups); Loading Loading
drivers/power/supply/qcom/qg-core.h +16 −0 Original line number Original line Diff line number Diff line Loading @@ -36,6 +36,10 @@ struct qg_dt { int s2_vbat_low_fifo_length; int s2_vbat_low_fifo_length; int s2_acc_length; int s2_acc_length; int s2_acc_intvl_ms; int s2_acc_intvl_ms; int sleep_s2_fifo_length; int sleep_s2_acc_length; int sleep_s2_acc_intvl_ms; int fast_chg_s2_fifo_length; int ocv_timer_expiry_min; int ocv_timer_expiry_min; int ocv_tol_threshold_uv; int ocv_tol_threshold_uv; int s3_entry_fifo_length; int s3_entry_fifo_length; Loading @@ -61,6 +65,8 @@ struct qg_dt { bool esr_discharge_enable; bool esr_discharge_enable; bool qg_ext_sense; bool qg_ext_sense; bool use_s7_ocv; bool use_s7_ocv; bool qg_sleep_config; bool qg_fast_chg_cfg; }; }; struct qg_esr_data { struct qg_esr_data { Loading @@ -86,6 +92,7 @@ struct qpnp_qg { struct work_struct udata_work; struct work_struct udata_work; struct work_struct scale_soc_work; struct work_struct scale_soc_work; struct work_struct qg_status_change_work; struct work_struct qg_status_change_work; struct delayed_work qg_sleep_exit_work; struct notifier_block nb; struct notifier_block nb; struct mutex bus_lock; struct mutex bus_lock; struct mutex data_lock; struct mutex data_lock; Loading Loading @@ -136,6 +143,8 @@ struct qpnp_qg { u32 charge_counter_uah; u32 charge_counter_uah; u32 esr_avg; u32 esr_avg; u32 esr_last; u32 esr_last; u32 s2_state; u32 s2_state_mask; ktime_t last_user_update_time; ktime_t last_user_update_time; ktime_t last_fifo_update_time; ktime_t last_fifo_update_time; unsigned long last_maint_soc_update_time; unsigned long last_maint_soc_update_time; Loading Loading @@ -182,6 +191,13 @@ enum ocv_type { PON_OCV_MAX, PON_OCV_MAX, }; }; enum s2_state { S2_FAST_CHARGING = BIT(0), S2_LOW_VBAT = BIT(1), S2_SLEEP = BIT(2), S2_DEFAULT = BIT(3), }; enum debug_mask { enum debug_mask { QG_DEBUG_PON = BIT(0), QG_DEBUG_PON = BIT(0), QG_DEBUG_PROFILE = BIT(1), QG_DEBUG_PROFILE = BIT(1), Loading
drivers/power/supply/qcom/qg-defs.h +3 −1 Original line number Original line Diff line number Diff line /* SPDX-License-Identifier: GPL-2.0 */ /* SPDX-License-Identifier: GPL-2.0 */ /* /* * Copyright (c) 2018 The Linux Foundation. All rights reserved. * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. */ */ #ifndef __QG_DEFS_H__ #ifndef __QG_DEFS_H__ Loading Loading @@ -28,6 +28,8 @@ #define PROFILE_IRQ_DISABLE "NO_PROFILE_IRQ_DISABLE" #define PROFILE_IRQ_DISABLE "NO_PROFILE_IRQ_DISABLE" #define QG_INIT_STATE_IRQ_DISABLE "QG_INIT_STATE_IRQ_DISABLE" #define QG_INIT_STATE_IRQ_DISABLE "QG_INIT_STATE_IRQ_DISABLE" #define TTF_AWAKE_VOTER "TTF_AWAKE_VOTER" #define TTF_AWAKE_VOTER "TTF_AWAKE_VOTER" #define SLEEP_EXIT_DATA_VOTER "SLEEP_EXIT_DATA_VOTER" #define SLEEP_EXIT_VOTER "SLEEP_EXIT_VOTER" #define V_RAW_TO_UV(V_RAW) div_u64(194637ULL * (u64)V_RAW, 1000) #define V_RAW_TO_UV(V_RAW) div_u64(194637ULL * (u64)V_RAW, 1000) #define FIFO_V_RESET_VAL 0x8000 #define FIFO_V_RESET_VAL 0x8000 Loading
drivers/power/supply/qcom/qpnp-qg.c +256 −44 Original line number Original line Diff line number Diff line Loading @@ -20,6 +20,7 @@ #include <linux/power_supply.h> #include <linux/power_supply.h> #include <linux/regmap.h> #include <linux/regmap.h> #include <linux/slab.h> #include <linux/slab.h> #include <linux/timekeeping.h> #include <linux/uaccess.h> #include <linux/uaccess.h> #include <linux/pmic-voter.h> #include <linux/pmic-voter.h> #include <linux/iio/consumer.h> #include <linux/iio/consumer.h> Loading Loading @@ -89,6 +90,8 @@ static struct attribute *qg_attrs[] = { }; }; ATTRIBUTE_GROUPS(qg); ATTRIBUTE_GROUPS(qg); static int qg_process_rt_fifo(struct qpnp_qg *chip); static bool is_battery_present(struct qpnp_qg *chip) static bool is_battery_present(struct qpnp_qg *chip) { { u8 reg = 0; u8 reg = 0; Loading Loading @@ -179,7 +182,7 @@ static int qg_update_fifo_length(struct qpnp_qg *chip, u8 length) pr_err("Failed to write S2 FIFO length, rc=%d\n", rc); pr_err("Failed to write S2 FIFO length, rc=%d\n", rc); /* update the S3 FIFO length, when S2 length is updated */ /* update the S3 FIFO length, when S2 length is updated */ if (length > 3) if (length > 3 && !chip->dt.qg_sleep_config) s3_entry_fifo_length = (chip->dt.s3_entry_fifo_length > 0) ? s3_entry_fifo_length = (chip->dt.s3_entry_fifo_length > 0) ? chip->dt.s3_entry_fifo_length : DEFAULT_S3_FIFO_LENGTH; chip->dt.s3_entry_fifo_length : DEFAULT_S3_FIFO_LENGTH; else /* Use S3 length as 1 for any S2 length <= 3 */ else /* Use S3 length as 1 for any S2 length <= 3 */ Loading Loading @@ -308,6 +311,111 @@ static int qg_store_soc_params(struct qpnp_qg *chip) return rc; return rc; } } static int qg_config_s2_state(struct qpnp_qg *chip, enum s2_state requested_state, bool state_enable, bool process_fifo) { int rc, acc_interval, acc_length; u8 fifo_length, reg = 0, state = S2_DEFAULT; if ((chip->s2_state_mask & requested_state) && (state_enable == true)) return 0; /* No change in state */ if (!(chip->s2_state_mask & requested_state) && (state_enable == false)) return 0; /* No change in state */ if (state_enable) chip->s2_state_mask |= requested_state; else chip->s2_state_mask &= ~requested_state; /* define the priority of the states */ if (chip->s2_state_mask & S2_FAST_CHARGING) state = S2_FAST_CHARGING; else if (chip->s2_state_mask & S2_LOW_VBAT) state = S2_LOW_VBAT; else if (chip->s2_state_mask & S2_SLEEP) state = S2_SLEEP; else state = S2_DEFAULT; if (state == chip->s2_state) return 0; switch (state) { case S2_FAST_CHARGING: fifo_length = chip->dt.fast_chg_s2_fifo_length; acc_interval = chip->dt.s2_acc_intvl_ms; acc_length = chip->dt.s2_acc_length; break; case S2_LOW_VBAT: fifo_length = chip->dt.s2_vbat_low_fifo_length; acc_interval = chip->dt.s2_acc_intvl_ms; acc_length = chip->dt.s2_acc_length; break; case S2_SLEEP: fifo_length = chip->dt.sleep_s2_fifo_length; acc_interval = chip->dt.sleep_s2_acc_intvl_ms; acc_length = chip->dt.sleep_s2_acc_length; break; case S2_DEFAULT: fifo_length = chip->dt.s2_fifo_length; acc_interval = chip->dt.s2_acc_intvl_ms; acc_length = chip->dt.s2_acc_length; break; default: pr_err("Invalid S2 state %d\n", state); return -EINVAL; } rc = qg_master_hold(chip, true); if (rc < 0) { pr_err("Failed to hold master, rc=%d\n", rc); return rc; } if (process_fifo) { rc = qg_process_rt_fifo(chip); if (rc < 0) { pr_err("Failed to process FIFO real-time, rc=%d\n", rc); goto done; } } rc = qg_update_fifo_length(chip, fifo_length); if (rc < 0) { pr_err("Failed to update S2 fifo-length, rc=%d\n", rc); goto done; } reg = acc_interval / 10; rc = qg_write(chip, chip->qg_base + QG_S2_NORMAL_MEAS_CTL3_REG, ®, 1); if (rc < 0) { pr_err("Failed to update S2 acc intrvl, rc=%d\n", rc); goto done; } reg = ilog2(acc_length) - 1; rc = qg_masked_write(chip, chip->qg_base + QG_S2_NORMAL_MEAS_CTL2_REG, NUM_OF_ACCUM_MASK, reg); if (rc < 0) { pr_err("Failed to update S2 ACC length, rc=%d\n", rc); goto done; } chip->s2_state = state; qg_dbg(chip, QG_DEBUG_STATUS, "S2 New state=%x fifo_length=%d interval=%d acc_length=%d\n", state, fifo_length, acc_interval, acc_length); done: qg_master_hold(chip, false); /* FIFO restarted */ chip->last_fifo_update_time = ktime_get_boottime(); return rc; } static int qg_process_fifo(struct qpnp_qg *chip, u32 fifo_length) static int qg_process_fifo(struct qpnp_qg *chip, u32 fifo_length) { { int rc = 0, i, j = 0, temp; int rc = 0, i, j = 0, temp; Loading Loading @@ -492,7 +600,7 @@ static int qg_process_rt_fifo(struct qpnp_qg *chip) static int process_rt_fifo_data(struct qpnp_qg *chip, bool update_smb) static int process_rt_fifo_data(struct qpnp_qg *chip, bool update_smb) { { int rc = 0; int rc = 0; ktime_t now = ktime_get(); ktime_t now = ktime_get_boottime(); s64 time_delta; s64 time_delta; /* /* Loading Loading @@ -537,7 +645,7 @@ static int process_rt_fifo_data(struct qpnp_qg *chip, bool update_smb) goto done; goto done; } } /* FIFOs restarted */ /* FIFOs restarted */ chip->last_fifo_update_time = ktime_get(); chip->last_fifo_update_time = ktime_get_boottime(); /* signal the read thread */ /* signal the read thread */ chip->data_ready = true; chip->data_ready = true; Loading @@ -559,7 +667,7 @@ static int process_rt_fifo_data(struct qpnp_qg *chip, bool update_smb) static int qg_vbat_low_wa(struct qpnp_qg *chip) static int qg_vbat_low_wa(struct qpnp_qg *chip) { { int rc, i, temp = 0; int rc, i, temp = 0; u32 vbat_low_uv = 0, fifo_length = 0; u32 vbat_low_uv = 0; if ((chip->wa_flags & QG_VBAT_LOW_WA) && chip->vbat_low) { if ((chip->wa_flags & QG_VBAT_LOW_WA) && chip->vbat_low) { rc = qg_get_battery_temp(chip, &temp); rc = qg_get_battery_temp(chip, &temp); Loading Loading @@ -589,37 +697,11 @@ static int qg_vbat_low_wa(struct qpnp_qg *chip) } } } } rc = get_fifo_length(chip, &fifo_length, false); rc = qg_config_s2_state(chip, S2_LOW_VBAT, if (rc < 0) { chip->vbat_low ? true : false, false); pr_err("Failed to get FIFO length, rc=%d\n", rc); return rc; } if (chip->vbat_low && fifo_length == chip->dt.s2_vbat_low_fifo_length) return 0; if (!chip->vbat_low && fifo_length == chip->dt.s2_fifo_length) return 0; rc = qg_master_hold(chip, true); if (rc < 0) { pr_err("Failed to hold master, rc=%d\n", rc); goto done; } fifo_length = chip->vbat_low ? chip->dt.s2_vbat_low_fifo_length : chip->dt.s2_fifo_length; rc = qg_update_fifo_length(chip, fifo_length); if (rc < 0) if (rc < 0) goto done; pr_err("Failed to configure for VBAT_LOW rc=%d\n", rc); qg_dbg(chip, QG_DEBUG_STATUS, "FIFO length updated to %d vbat_low=%d\n", fifo_length, chip->vbat_low); done: qg_master_hold(chip, false); /* FIFOs restarted */ chip->last_fifo_update_time = ktime_get(); return rc; return rc; } } Loading Loading @@ -690,6 +772,22 @@ static int qg_vbat_thresholds_config(struct qpnp_qg *chip) return rc; return rc; } } static int qg_fast_charge_config(struct qpnp_qg *chip) { int rc = 0; if (!chip->dt.qg_fast_chg_cfg) return 0; rc = qg_config_s2_state(chip, S2_FAST_CHARGING, (chip->charge_status == POWER_SUPPLY_STATUS_CHARGING) ? true : false, false); if (rc < 0) pr_err("Failed to exit S2_SLEEP rc=%d\n", rc); return rc; } static void qg_retrieve_esr_params(struct qpnp_qg *chip) static void qg_retrieve_esr_params(struct qpnp_qg *chip) { { u32 data = 0; u32 data = 0; Loading Loading @@ -1035,7 +1133,7 @@ static int qg_esr_estimate(struct qpnp_qg *chip) goto done; goto done; } } /* FIFOs restarted */ /* FIFOs restarted */ chip->last_fifo_update_time = ktime_get(); chip->last_fifo_update_time = ktime_get_boottime(); if (chip->esr_avg) { if (chip->esr_avg) { chip->kdata.param[QG_ESR].data = chip->esr_avg; chip->kdata.param[QG_ESR].data = chip->esr_avg; Loading Loading @@ -1122,7 +1220,7 @@ static void process_udata_work(struct work_struct *work) #define MAX_FIFO_DELTA_PERCENT 10 #define MAX_FIFO_DELTA_PERCENT 10 static irqreturn_t qg_fifo_update_done_handler(int irq, void *data) static irqreturn_t qg_fifo_update_done_handler(int irq, void *data) { { ktime_t now = ktime_get(); ktime_t now = ktime_get_boottime(); int rc, hw_delta_ms = 0, margin_ms = 0; int rc, hw_delta_ms = 0, margin_ms = 0; u32 fifo_length = 0; u32 fifo_length = 0; s64 time_delta_ms = 0; s64 time_delta_ms = 0; Loading Loading @@ -1153,6 +1251,10 @@ static irqreturn_t qg_fifo_update_done_handler(int irq, void *data) if (rc < 0) if (rc < 0) pr_err("Failed to apply VBAT EMPTY config rc=%d\n", rc); pr_err("Failed to apply VBAT EMPTY config rc=%d\n", rc); rc = qg_fast_charge_config(chip); if (rc < 0) pr_err("Failed to apply fast-charge config rc=%d\n", rc); rc = qg_vbat_low_wa(chip); rc = qg_vbat_low_wa(chip); if (rc < 0) { if (rc < 0) { pr_err("Failed to apply VBAT LOW WA, rc=%d\n", rc); pr_err("Failed to apply VBAT LOW WA, rc=%d\n", rc); Loading Loading @@ -1893,6 +1995,9 @@ static int qg_psy_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_CAPACITY: case POWER_SUPPLY_PROP_CAPACITY: rc = qg_get_battery_capacity(chip, &pval->intval); rc = qg_get_battery_capacity(chip, &pval->intval); break; break; case POWER_SUPPLY_PROP_CAPACITY_RAW: pval->intval = chip->sys_soc; break; case POWER_SUPPLY_PROP_REAL_CAPACITY: case POWER_SUPPLY_PROP_REAL_CAPACITY: rc = qg_get_battery_capacity_real(chip, &pval->intval); rc = qg_get_battery_capacity_real(chip, &pval->intval); break; break; Loading Loading @@ -2022,6 +2127,7 @@ static int qg_property_is_writeable(struct power_supply *psy, static enum power_supply_property qg_psy_props[] = { static enum power_supply_property qg_psy_props[] = { POWER_SUPPLY_PROP_CAPACITY, POWER_SUPPLY_PROP_CAPACITY, POWER_SUPPLY_PROP_CAPACITY_RAW, POWER_SUPPLY_PROP_REAL_CAPACITY, POWER_SUPPLY_PROP_REAL_CAPACITY, POWER_SUPPLY_PROP_TEMP, POWER_SUPPLY_PROP_TEMP, POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_VOLTAGE_NOW, Loading Loading @@ -2308,6 +2414,33 @@ static int qg_battery_status_update(struct qpnp_qg *chip) return rc; return rc; } } static void qg_sleep_exit_work(struct work_struct *work) { int rc; struct qpnp_qg *chip = container_of(work, struct qpnp_qg, qg_sleep_exit_work.work); vote(chip->awake_votable, SLEEP_EXIT_VOTER, true, 0); mutex_lock(&chip->data_lock); /* * if this work is executing, the system has been active * for a while. So, force back the S2 active configuration */ qg_dbg(chip, QG_DEBUG_STATUS, "sleep_exit_work: exit S2_SLEEP\n"); rc = qg_config_s2_state(chip, S2_SLEEP, false, true); if (rc < 0) pr_err("Failed to exit S2_SLEEP rc=%d\n", rc); vote(chip->awake_votable, SLEEP_EXIT_DATA_VOTER, true, 0); /* signal the read thread */ chip->data_ready = true; wake_up_interruptible(&chip->qg_wait_q); mutex_unlock(&chip->data_lock); vote(chip->awake_votable, SLEEP_EXIT_VOTER, false, 0); } static void qg_status_change_work(struct work_struct *work) static void qg_status_change_work(struct work_struct *work) { { Loading Loading @@ -2480,6 +2613,7 @@ static ssize_t qg_device_read(struct file *file, char __user *buf, size_t count, vote(chip->awake_votable, FIFO_DONE_VOTER, false, 0); vote(chip->awake_votable, FIFO_DONE_VOTER, false, 0); vote(chip->awake_votable, FIFO_RT_DONE_VOTER, false, 0); vote(chip->awake_votable, FIFO_RT_DONE_VOTER, false, 0); vote(chip->awake_votable, SUSPEND_DATA_VOTER, false, 0); vote(chip->awake_votable, SUSPEND_DATA_VOTER, false, 0); vote(chip->awake_votable, SLEEP_EXIT_DATA_VOTER, false, 0); qg_dbg(chip, QG_DEBUG_DEVICE, qg_dbg(chip, QG_DEBUG_DEVICE, "QG device read complete Seq_no=%u Size=%ld\n", "QG device read complete Seq_no=%u Size=%ld\n", Loading Loading @@ -2822,7 +2956,7 @@ static int qg_determine_pon_soc(struct qpnp_qg *chip) bool use_pon_ocv = true; bool use_pon_ocv = true; unsigned long rtc_sec = 0; unsigned long rtc_sec = 0; u32 ocv_uv = 0, soc = 0, pon_soc = 0, full_soc = 0, cutoff_soc = 0; u32 ocv_uv = 0, soc = 0, pon_soc = 0, full_soc = 0, cutoff_soc = 0; u32 shutdown[SDAM_MAX] = {0}; u32 shutdown[SDAM_MAX] = {0}, soc_raw = 0; char ocv_type[20] = "NONE"; char ocv_type[20] = "NONE"; if (!chip->profile_loaded) { if (!chip->profile_loaded) { Loading Loading @@ -2901,6 +3035,7 @@ static int qg_determine_pon_soc(struct qpnp_qg *chip) use_pon_ocv = false; use_pon_ocv = false; ocv_uv = shutdown[SDAM_OCV_UV]; ocv_uv = shutdown[SDAM_OCV_UV]; soc = shutdown[SDAM_SOC]; soc = shutdown[SDAM_SOC]; soc_raw = shutdown[SDAM_SOC] * 100; strlcpy(ocv_type, "SHUTDOWN_SOC", 20); strlcpy(ocv_type, "SHUTDOWN_SOC", 20); qg_dbg(chip, QG_DEBUG_PON, "Using SHUTDOWN_SOC @ PON\n"); qg_dbg(chip, QG_DEBUG_PON, "Using SHUTDOWN_SOC @ PON\n"); Loading Loading @@ -2968,7 +3103,9 @@ static int qg_determine_pon_soc(struct qpnp_qg *chip) soc = DIV_ROUND_UP(((pon_soc - cutoff_soc) * 100), soc = DIV_ROUND_UP(((pon_soc - cutoff_soc) * 100), (full_soc - cutoff_soc)); (full_soc - cutoff_soc)); soc = CAP(0, 100, soc); soc = CAP(0, 100, soc); soc_raw = soc * 100; } else { } else { soc_raw = pon_soc * 100; soc = pon_soc; soc = pon_soc; } } Loading @@ -2982,6 +3119,7 @@ static int qg_determine_pon_soc(struct qpnp_qg *chip) return rc; return rc; } } chip->cc_soc = chip->sys_soc = soc_raw; chip->last_adj_ssoc = chip->catch_up_soc = chip->msoc = soc; chip->last_adj_ssoc = chip->catch_up_soc = chip->msoc = soc; chip->kdata.param[QG_PON_OCV_UV].data = ocv_uv; chip->kdata.param[QG_PON_OCV_UV].data = ocv_uv; chip->kdata.param[QG_PON_OCV_UV].valid = true; chip->kdata.param[QG_PON_OCV_UV].valid = true; Loading Loading @@ -3098,6 +3236,8 @@ static int qg_hw_init(struct qpnp_qg *chip) } } } } chip->s2_state = S2_DEFAULT; chip->s2_state_mask |= S2_DEFAULT; /* signal the read thread */ /* signal the read thread */ chip->data_ready = true; chip->data_ready = true; wake_up_interruptible(&chip->qg_wait_q); wake_up_interruptible(&chip->qg_wait_q); Loading @@ -3108,7 +3248,7 @@ static int qg_hw_init(struct qpnp_qg *chip) pr_err("Failed to release master, rc=%d\n", rc); pr_err("Failed to release master, rc=%d\n", rc); return rc; return rc; } } chip->last_fifo_update_time = ktime_get(); chip->last_fifo_update_time = ktime_get_boottime(); if (chip->dt.ocv_timer_expiry_min != -EINVAL) { if (chip->dt.ocv_timer_expiry_min != -EINVAL) { if (chip->dt.ocv_timer_expiry_min < 2) if (chip->dt.ocv_timer_expiry_min < 2) Loading Loading @@ -3452,6 +3592,10 @@ static void qg_create_debugfs(struct qpnp_qg *chip) #define DEFAULT_S2_VBAT_LOW_LENGTH 2 #define DEFAULT_S2_VBAT_LOW_LENGTH 2 #define DEFAULT_S2_ACC_LENGTH 128 #define DEFAULT_S2_ACC_LENGTH 128 #define DEFAULT_S2_ACC_INTVL_MS 100 #define DEFAULT_S2_ACC_INTVL_MS 100 #define DEFAULT_SLEEP_S2_FIFO_LENGTH 8 #define DEFAULT_SLEEP_S2_ACC_LENGTH 256 #define DEFAULT_SLEEP_S2_ACC_INTVL_MS 200 #define DEFAULT_FAST_CHG_S2_FIFO_LENGTH 1 static int qg_parse_s2_dt(struct qpnp_qg *chip) static int qg_parse_s2_dt(struct qpnp_qg *chip) { { int rc; int rc; Loading Loading @@ -3487,6 +3631,48 @@ static int qg_parse_s2_dt(struct qpnp_qg *chip) chip->dt.s2_fifo_length, chip->dt.s2_vbat_low_fifo_length, chip->dt.s2_fifo_length, chip->dt.s2_vbat_low_fifo_length, chip->dt.s2_acc_length, chip->dt.s2_acc_intvl_ms); chip->dt.s2_acc_length, chip->dt.s2_acc_intvl_ms); if (of_property_read_bool(node, "qcom,qg-sleep-config")) { chip->dt.qg_sleep_config = true; rc = of_property_read_u32(node, "qcom,sleep-s2-fifo-length", &temp); if (rc < 0) chip->dt.sleep_s2_fifo_length = DEFAULT_SLEEP_S2_FIFO_LENGTH; else chip->dt.sleep_s2_fifo_length = temp; rc = of_property_read_u32(node, "qcom,sleep-s2-acc-length", &temp); if (rc < 0) chip->dt.sleep_s2_acc_length = DEFAULT_SLEEP_S2_ACC_LENGTH; else chip->dt.sleep_s2_acc_length = temp; rc = of_property_read_u32(node, "qcom,sleep-s2-acc-intvl-ms", &temp); if (rc < 0) chip->dt.sleep_s2_acc_intvl_ms = DEFAULT_SLEEP_S2_ACC_INTVL_MS; else chip->dt.sleep_s2_acc_intvl_ms = temp; } if (of_property_read_bool(node, "qcom,qg-fast-chg-config")) { chip->dt.qg_fast_chg_cfg = true; rc = of_property_read_u32(node, "qcom,fast-chg-s2-fifo-length", &temp); if (rc < 0) chip->dt.fast_chg_s2_fifo_length = DEFAULT_FAST_CHG_S2_FIFO_LENGTH; else chip->dt.fast_chg_s2_fifo_length = temp; } return 0; return 0; } } Loading Loading @@ -3842,6 +4028,7 @@ static int process_suspend(struct qpnp_qg *chip) return 0; return 0; cancel_delayed_work_sync(&chip->ttf->ttf_work); cancel_delayed_work_sync(&chip->ttf->ttf_work); cancel_delayed_work_sync(&chip->qg_sleep_exit_work); chip->suspend_data = false; chip->suspend_data = false; Loading @@ -3850,6 +4037,13 @@ static int process_suspend(struct qpnp_qg *chip) /* ignore any suspend processing if we are charging */ /* ignore any suspend processing if we are charging */ if (chip->charge_status == POWER_SUPPLY_STATUS_CHARGING) { if (chip->charge_status == POWER_SUPPLY_STATUS_CHARGING) { /* Reset the sleep config if we are charging */ if (chip->dt.qg_sleep_config) { qg_dbg(chip, QG_DEBUG_STATUS, "Suspend: Charging - Exit S2_SLEEP\n"); rc = qg_config_s2_state(chip, S2_SLEEP, false, true); if (rc < 0) pr_err("Failed to exit S2-sleep rc=%d\n", rc); } qg_dbg(chip, QG_DEBUG_PM, "Charging @ suspend - ignore processing\n"); qg_dbg(chip, QG_DEBUG_PM, "Charging @ suspend - ignore processing\n"); return 0; return 0; } } Loading @@ -3867,12 +4061,23 @@ static int process_suspend(struct qpnp_qg *chip) return rc; return rc; } } sleep_fifo_length &= SLEEP_IBAT_QUALIFIED_LENGTH_MASK; sleep_fifo_length &= SLEEP_IBAT_QUALIFIED_LENGTH_MASK; if (chip->dt.qg_sleep_config) { qg_dbg(chip, QG_DEBUG_STATUS, "Suspend: Forcing S2_SLEEP\n"); rc = qg_config_s2_state(chip, S2_SLEEP, true, true); if (rc < 0) pr_err("Failed to config S2_SLEEP rc=%d\n", rc); if (chip->kdata.fifo_length > 0) chip->suspend_data = true; } else if (fifo_rt_length >= (chip->dt.s2_fifo_length - sleep_fifo_length)) { /* /* * If the real-time FIFO count is greater than * If the real-time FIFO count is greater than * the the #fifo to enter sleep, save the FIFO data * the the #fifo to enter sleep, save the FIFO data * and reset the fifo count. * and reset the fifo count. This is avoid a gauranteed wakeup * due to fifo_done event as the curent FIFO length is already * beyond the sleep length. */ */ if (fifo_rt_length >= (chip->dt.s2_fifo_length - sleep_fifo_length)) { rc = qg_master_hold(chip, true); rc = qg_master_hold(chip, true); if (rc < 0) { if (rc < 0) { pr_err("Failed to hold master, rc=%d\n", rc); pr_err("Failed to hold master, rc=%d\n", rc); Loading @@ -3892,7 +4097,7 @@ static int process_suspend(struct qpnp_qg *chip) return rc; return rc; } } /* FIFOs restarted */ /* FIFOs restarted */ chip->last_fifo_update_time = ktime_get(); chip->last_fifo_update_time = ktime_get_boottime(); chip->suspend_data = true; chip->suspend_data = true; } } Loading @@ -3907,6 +4112,7 @@ static int process_suspend(struct qpnp_qg *chip) return rc; return rc; } } #define QG_SLEEP_EXIT_TIME_MS 15000 /* 15 secs */ static int process_resume(struct qpnp_qg *chip) static int process_resume(struct qpnp_qg *chip) { { u8 status2 = 0, rt_status = 0; u8 status2 = 0, rt_status = 0; Loading @@ -3921,6 +4127,10 @@ static int process_resume(struct qpnp_qg *chip) get_rtc_time(&rtc_sec); get_rtc_time(&rtc_sec); sleep_time_secs = rtc_sec - chip->suspend_time; sleep_time_secs = rtc_sec - chip->suspend_time; if (chip->dt.qg_sleep_config) schedule_delayed_work(&chip->qg_sleep_exit_work, msecs_to_jiffies(QG_SLEEP_EXIT_TIME_MS)); rc = qg_read(chip, chip->qg_base + QG_STATUS2_REG, &status2, 1); rc = qg_read(chip, chip->qg_base + QG_STATUS2_REG, &status2, 1); if (rc < 0) { if (rc < 0) { pr_err("Failed to read status2 register, rc=%d\n", rc); pr_err("Failed to read status2 register, rc=%d\n", rc); Loading Loading @@ -4088,6 +4298,7 @@ static int qpnp_qg_probe(struct platform_device *pdev) platform_set_drvdata(pdev, chip); platform_set_drvdata(pdev, chip); INIT_WORK(&chip->udata_work, process_udata_work); INIT_WORK(&chip->udata_work, process_udata_work); INIT_WORK(&chip->qg_status_change_work, qg_status_change_work); INIT_WORK(&chip->qg_status_change_work, qg_status_change_work); INIT_DELAYED_WORK(&chip->qg_sleep_exit_work, qg_sleep_exit_work); mutex_init(&chip->bus_lock); mutex_init(&chip->bus_lock); mutex_init(&chip->soc_lock); mutex_init(&chip->soc_lock); mutex_init(&chip->data_lock); mutex_init(&chip->data_lock); Loading Loading @@ -4256,6 +4467,7 @@ static int qpnp_qg_remove(struct platform_device *pdev) qg_batterydata_exit(); qg_batterydata_exit(); qg_soc_exit(chip); qg_soc_exit(chip); cancel_delayed_work_sync(&chip->qg_sleep_exit_work); cancel_work_sync(&chip->udata_work); cancel_work_sync(&chip->udata_work); cancel_work_sync(&chip->qg_status_change_work); cancel_work_sync(&chip->qg_status_change_work); sysfs_remove_groups(&chip->dev->kobj, qg_groups); sysfs_remove_groups(&chip->dev->kobj, qg_groups); Loading