Loading Documentation/devicetree/bindings/power/supply/qcom/qpnp-qg.txt +15 −0 Original line number Diff line number Diff line Loading @@ -389,6 +389,21 @@ First Level Node - QGAUGE device 'qcom,qg-sleep-config' is enabled. the default value if not specified is 200ms. - qcom,qg-fast-chg-config Usage: optional Value type: bool Definition: Enables fast-charge configurtion for QG. This allows configuring the FIFO length during fast charge. - qcom,fast-chg-s2-fifo-length Usage: optional Value type: <u32> Definition: The FIFO length to be applied when system enters fast-chargging. Takes effect only if 'qcom,qg-fast-chg-config' is enabled. The default value if not specified is 1. ========================================================== Second Level Nodes - Peripherals managed by QGAUGE driver ========================================================== Loading drivers/power/supply/qcom/qg-core.h +16 −0 Original line number Diff line number Diff line Loading @@ -42,6 +42,10 @@ struct qg_dt { int s2_vbat_low_fifo_length; int s2_acc_length; 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_tol_threshold_uv; int s3_entry_fifo_length; Loading @@ -67,6 +71,8 @@ struct qg_dt { bool esr_discharge_enable; bool qg_ext_sense; bool use_s7_ocv; bool qg_sleep_config; bool qg_fast_chg_cfg; }; struct qg_esr_data { Loading @@ -91,6 +97,7 @@ struct qpnp_qg { struct work_struct udata_work; struct work_struct scale_soc_work; struct work_struct qg_status_change_work; struct delayed_work qg_sleep_exit_work; struct notifier_block nb; struct mutex bus_lock; struct mutex data_lock; Loading Loading @@ -141,6 +148,8 @@ struct qpnp_qg { u32 charge_counter_uah; u32 esr_avg; u32 esr_last; u32 s2_state; u32 s2_state_mask; ktime_t last_user_update_time; ktime_t last_fifo_update_time; unsigned long last_maint_soc_update_time; Loading Loading @@ -187,6 +196,13 @@ enum ocv_type { 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 { QG_DEBUG_PON = BIT(0), QG_DEBUG_PROFILE = BIT(1), Loading drivers/power/supply/qcom/qg-defs.h +3 −1 Original line number Diff line number Diff line /* Copyright (c) 2018 The Linux Foundation. All rights reserved. /* Copyright (c) 2018,2019 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 @@ -35,6 +35,8 @@ #define PROFILE_IRQ_DISABLE "NO_PROFILE_IRQ_DISABLE" #define QG_INIT_STATE_IRQ_DISABLE "QG_INIT_STATE_IRQ_DISABLE" #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 FIFO_V_RESET_VAL 0x8000 Loading drivers/power/supply/qcom/qpnp-qg.c +240 −37 Original line number Diff line number Diff line Loading @@ -56,6 +56,8 @@ module_param_named( esr_count, qg_esr_count, int, 0600 ); static int qg_process_rt_fifo(struct qpnp_qg *chip); static bool is_battery_present(struct qpnp_qg *chip) { u8 reg = 0; Loading Loading @@ -146,7 +148,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); /* 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) ? chip->dt.s3_entry_fifo_length : DEFAULT_S3_FIFO_LENGTH; else /* Use S3 length as 1 for any S2 length <= 3 */ Loading Loading @@ -275,6 +277,111 @@ static int qg_store_soc_params(struct qpnp_qg *chip) 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(); return rc; } static int qg_process_fifo(struct qpnp_qg *chip, u32 fifo_length) { int rc = 0, i, j = 0, temp; Loading Loading @@ -526,7 +633,7 @@ static int process_rt_fifo_data(struct qpnp_qg *chip, bool update_smb) static int qg_vbat_low_wa(struct qpnp_qg *chip) { 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) { rc = qg_get_battery_temp(chip, &temp); Loading Loading @@ -556,37 +663,11 @@ static int qg_vbat_low_wa(struct qpnp_qg *chip) } } rc = get_fifo_length(chip, &fifo_length, false); if (rc < 0) { 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); rc = qg_config_s2_state(chip, S2_LOW_VBAT, chip->vbat_low ? true : false, false); 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; } Loading Loading @@ -657,6 +738,22 @@ static int qg_vbat_thresholds_config(struct qpnp_qg *chip) 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) { u32 data = 0; Loading Loading @@ -1120,6 +1217,10 @@ static irqreturn_t qg_fifo_update_done_handler(int irq, void *data) if (rc < 0) 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); if (rc < 0) { pr_err("Failed to apply VBAT LOW WA, rc=%d\n", rc); Loading Loading @@ -2275,6 +2376,33 @@ static int qg_battery_status_update(struct qpnp_qg *chip) 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) { Loading Loading @@ -2449,6 +2577,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_RT_DONE_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 device read complete Seq_no=%u Size=%ld\n", Loading Loading @@ -3064,6 +3193,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 */ chip->data_ready = true; wake_up_interruptible(&chip->qg_wait_q); Loading Loading @@ -3399,6 +3530,9 @@ static int qg_alg_init(struct qpnp_qg *chip) #define DEFAULT_S2_VBAT_LOW_LENGTH 2 #define DEFAULT_S2_ACC_LENGTH 128 #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_DELTA_SOC 1 #define DEFAULT_SHUTDOWN_SOC_SECS 360 #define DEFAULT_COLD_TEMP_THRESHOLD 0 Loading @@ -3418,6 +3552,7 @@ static int qg_alg_init(struct qpnp_qg *chip) #define ESR_CHG_MIN_IBAT_UA (-450000) #define DEFAULT_SLEEP_TIME_SECS 1800 /* 30 mins */ #define DEFAULT_SYS_MIN_VOLT_MV 2800 #define DEFAULT_FAST_CHG_S2_FIFO_LENGTH 1 static int qg_parse_dt(struct qpnp_qg *chip) { int rc = 0; Loading Loading @@ -3660,6 +3795,48 @@ static int qg_parse_dt(struct qpnp_qg *chip) else chip->dt.sys_min_volt_mv = temp; 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; } chip->dt.qg_ext_sense = of_property_read_bool(node, "qcom,qg-ext-sns"); chip->dt.use_s7_ocv = of_property_read_bool(node, "qcom,qg-use-s7-ocv"); Loading Loading @@ -3755,6 +3932,7 @@ static int process_suspend(struct qpnp_qg *chip) return 0; cancel_delayed_work_sync(&chip->ttf->ttf_work); cancel_delayed_work_sync(&chip->qg_sleep_exit_work); chip->suspend_data = false; Loading @@ -3763,6 +3941,13 @@ static int process_suspend(struct qpnp_qg *chip) /* ignore any suspend processing if we are 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"); return 0; } Loading @@ -3780,12 +3965,23 @@ static int process_suspend(struct qpnp_qg *chip) return rc; } 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 * 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); if (rc < 0) { pr_err("Failed to hold master, rc=%d\n", rc); Loading Loading @@ -3820,6 +4016,7 @@ static int process_suspend(struct qpnp_qg *chip) return rc; } #define QG_SLEEP_EXIT_TIME_MS 15000 /* 15 secs */ static int process_resume(struct qpnp_qg *chip) { u8 status2 = 0, rt_status = 0; Loading @@ -3834,6 +4031,10 @@ static int process_resume(struct qpnp_qg *chip) get_rtc_time(&rtc_sec); 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); if (rc < 0) { pr_err("Failed to read status2 register, rc=%d\n", rc); Loading Loading @@ -4001,6 +4202,7 @@ static int qpnp_qg_probe(struct platform_device *pdev) platform_set_drvdata(pdev, chip); INIT_WORK(&chip->udata_work, process_udata_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->soc_lock); mutex_init(&chip->data_lock); Loading Loading @@ -4161,6 +4363,7 @@ static int qpnp_qg_remove(struct platform_device *pdev) qg_batterydata_exit(); qg_soc_exit(chip); cancel_delayed_work_sync(&chip->qg_sleep_exit_work); cancel_work_sync(&chip->udata_work); cancel_work_sync(&chip->qg_status_change_work); device_destroy(chip->qg_class, chip->dev_no); Loading Loading
Documentation/devicetree/bindings/power/supply/qcom/qpnp-qg.txt +15 −0 Original line number Diff line number Diff line Loading @@ -389,6 +389,21 @@ First Level Node - QGAUGE device 'qcom,qg-sleep-config' is enabled. the default value if not specified is 200ms. - qcom,qg-fast-chg-config Usage: optional Value type: bool Definition: Enables fast-charge configurtion for QG. This allows configuring the FIFO length during fast charge. - qcom,fast-chg-s2-fifo-length Usage: optional Value type: <u32> Definition: The FIFO length to be applied when system enters fast-chargging. Takes effect only if 'qcom,qg-fast-chg-config' is enabled. The default value if not specified is 1. ========================================================== Second Level Nodes - Peripherals managed by QGAUGE driver ========================================================== Loading
drivers/power/supply/qcom/qg-core.h +16 −0 Original line number Diff line number Diff line Loading @@ -42,6 +42,10 @@ struct qg_dt { int s2_vbat_low_fifo_length; int s2_acc_length; 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_tol_threshold_uv; int s3_entry_fifo_length; Loading @@ -67,6 +71,8 @@ struct qg_dt { bool esr_discharge_enable; bool qg_ext_sense; bool use_s7_ocv; bool qg_sleep_config; bool qg_fast_chg_cfg; }; struct qg_esr_data { Loading @@ -91,6 +97,7 @@ struct qpnp_qg { struct work_struct udata_work; struct work_struct scale_soc_work; struct work_struct qg_status_change_work; struct delayed_work qg_sleep_exit_work; struct notifier_block nb; struct mutex bus_lock; struct mutex data_lock; Loading Loading @@ -141,6 +148,8 @@ struct qpnp_qg { u32 charge_counter_uah; u32 esr_avg; u32 esr_last; u32 s2_state; u32 s2_state_mask; ktime_t last_user_update_time; ktime_t last_fifo_update_time; unsigned long last_maint_soc_update_time; Loading Loading @@ -187,6 +196,13 @@ enum ocv_type { 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 { QG_DEBUG_PON = BIT(0), QG_DEBUG_PROFILE = BIT(1), Loading
drivers/power/supply/qcom/qg-defs.h +3 −1 Original line number Diff line number Diff line /* Copyright (c) 2018 The Linux Foundation. All rights reserved. /* Copyright (c) 2018,2019 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 @@ -35,6 +35,8 @@ #define PROFILE_IRQ_DISABLE "NO_PROFILE_IRQ_DISABLE" #define QG_INIT_STATE_IRQ_DISABLE "QG_INIT_STATE_IRQ_DISABLE" #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 FIFO_V_RESET_VAL 0x8000 Loading
drivers/power/supply/qcom/qpnp-qg.c +240 −37 Original line number Diff line number Diff line Loading @@ -56,6 +56,8 @@ module_param_named( esr_count, qg_esr_count, int, 0600 ); static int qg_process_rt_fifo(struct qpnp_qg *chip); static bool is_battery_present(struct qpnp_qg *chip) { u8 reg = 0; Loading Loading @@ -146,7 +148,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); /* 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) ? chip->dt.s3_entry_fifo_length : DEFAULT_S3_FIFO_LENGTH; else /* Use S3 length as 1 for any S2 length <= 3 */ Loading Loading @@ -275,6 +277,111 @@ static int qg_store_soc_params(struct qpnp_qg *chip) 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(); return rc; } static int qg_process_fifo(struct qpnp_qg *chip, u32 fifo_length) { int rc = 0, i, j = 0, temp; Loading Loading @@ -526,7 +633,7 @@ static int process_rt_fifo_data(struct qpnp_qg *chip, bool update_smb) static int qg_vbat_low_wa(struct qpnp_qg *chip) { 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) { rc = qg_get_battery_temp(chip, &temp); Loading Loading @@ -556,37 +663,11 @@ static int qg_vbat_low_wa(struct qpnp_qg *chip) } } rc = get_fifo_length(chip, &fifo_length, false); if (rc < 0) { 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); rc = qg_config_s2_state(chip, S2_LOW_VBAT, chip->vbat_low ? true : false, false); 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; } Loading Loading @@ -657,6 +738,22 @@ static int qg_vbat_thresholds_config(struct qpnp_qg *chip) 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) { u32 data = 0; Loading Loading @@ -1120,6 +1217,10 @@ static irqreturn_t qg_fifo_update_done_handler(int irq, void *data) if (rc < 0) 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); if (rc < 0) { pr_err("Failed to apply VBAT LOW WA, rc=%d\n", rc); Loading Loading @@ -2275,6 +2376,33 @@ static int qg_battery_status_update(struct qpnp_qg *chip) 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) { Loading Loading @@ -2449,6 +2577,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_RT_DONE_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 device read complete Seq_no=%u Size=%ld\n", Loading Loading @@ -3064,6 +3193,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 */ chip->data_ready = true; wake_up_interruptible(&chip->qg_wait_q); Loading Loading @@ -3399,6 +3530,9 @@ static int qg_alg_init(struct qpnp_qg *chip) #define DEFAULT_S2_VBAT_LOW_LENGTH 2 #define DEFAULT_S2_ACC_LENGTH 128 #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_DELTA_SOC 1 #define DEFAULT_SHUTDOWN_SOC_SECS 360 #define DEFAULT_COLD_TEMP_THRESHOLD 0 Loading @@ -3418,6 +3552,7 @@ static int qg_alg_init(struct qpnp_qg *chip) #define ESR_CHG_MIN_IBAT_UA (-450000) #define DEFAULT_SLEEP_TIME_SECS 1800 /* 30 mins */ #define DEFAULT_SYS_MIN_VOLT_MV 2800 #define DEFAULT_FAST_CHG_S2_FIFO_LENGTH 1 static int qg_parse_dt(struct qpnp_qg *chip) { int rc = 0; Loading Loading @@ -3660,6 +3795,48 @@ static int qg_parse_dt(struct qpnp_qg *chip) else chip->dt.sys_min_volt_mv = temp; 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; } chip->dt.qg_ext_sense = of_property_read_bool(node, "qcom,qg-ext-sns"); chip->dt.use_s7_ocv = of_property_read_bool(node, "qcom,qg-use-s7-ocv"); Loading Loading @@ -3755,6 +3932,7 @@ static int process_suspend(struct qpnp_qg *chip) return 0; cancel_delayed_work_sync(&chip->ttf->ttf_work); cancel_delayed_work_sync(&chip->qg_sleep_exit_work); chip->suspend_data = false; Loading @@ -3763,6 +3941,13 @@ static int process_suspend(struct qpnp_qg *chip) /* ignore any suspend processing if we are 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"); return 0; } Loading @@ -3780,12 +3965,23 @@ static int process_suspend(struct qpnp_qg *chip) return rc; } 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 * 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); if (rc < 0) { pr_err("Failed to hold master, rc=%d\n", rc); Loading Loading @@ -3820,6 +4016,7 @@ static int process_suspend(struct qpnp_qg *chip) return rc; } #define QG_SLEEP_EXIT_TIME_MS 15000 /* 15 secs */ static int process_resume(struct qpnp_qg *chip) { u8 status2 = 0, rt_status = 0; Loading @@ -3834,6 +4031,10 @@ static int process_resume(struct qpnp_qg *chip) get_rtc_time(&rtc_sec); 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); if (rc < 0) { pr_err("Failed to read status2 register, rc=%d\n", rc); Loading Loading @@ -4001,6 +4202,7 @@ static int qpnp_qg_probe(struct platform_device *pdev) platform_set_drvdata(pdev, chip); INIT_WORK(&chip->udata_work, process_udata_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->soc_lock); mutex_init(&chip->data_lock); Loading Loading @@ -4161,6 +4363,7 @@ static int qpnp_qg_remove(struct platform_device *pdev) qg_batterydata_exit(); qg_soc_exit(chip); cancel_delayed_work_sync(&chip->qg_sleep_exit_work); cancel_work_sync(&chip->udata_work); cancel_work_sync(&chip->qg_status_change_work); device_destroy(chip->qg_class, chip->dev_no); Loading