Loading Documentation/devicetree/bindings/power/supply/qcom/qpnp-qg.txt +8 −0 Original line number Diff line number Diff line Loading @@ -156,6 +156,14 @@ First Level Node - QGAUGE device Definition: A boolean property that when defined holds SOC at 100% when the battery is full until recharge starts. - qcom,linearize-soc Usage: optional Value type: <empty> Definition: A boolean property that when defined linearizes SOC when the SOC drops after charge termination monotonically to improve the user experience. This is applicable only if "qcom,hold-soc-while-full" is specified. ========================================================== Second Level Nodes - Peripherals managed by QGAUGE driver ========================================================== Loading drivers/power/supply/qcom/qg-battery-profile.c +1 −1 Original line number Diff line number Diff line Loading @@ -163,7 +163,7 @@ static long qg_battery_data_ioctl(struct file *file, unsigned int cmd, } break; case BPIOCXVAR: if (bp.table_index < TABLE_Z1 || bp.table_index > TABLE_MAX) { if (bp.table_index < TABLE_Z1 || bp.table_index >= TABLE_MAX) { pr_err("Invalid table index %d for VAR lookup\n", bp.table_index); rc = -EINVAL; Loading drivers/power/supply/qcom/qg-core.h +2 −0 Original line number Diff line number Diff line Loading @@ -45,6 +45,7 @@ struct qg_dt { int rbat_conn_mohm; int ignore_shutdown_soc_secs; bool hold_soc_while_full; bool linearize_soc; }; struct qpnp_qg { Loading Loading @@ -102,6 +103,7 @@ struct qpnp_qg { /* soc params */ int catch_up_soc; int maint_soc; int msoc; int pon_soc; struct alarm alarm_timer; Loading drivers/power/supply/qcom/qg-soc.c +55 −28 Original line number Diff line number Diff line Loading @@ -13,6 +13,7 @@ #define pr_fmt(fmt) "QG-K: %s: " fmt, __func__ #include <linux/alarmtimer.h> #include <linux/module.h> #include <linux/power_supply.h> #include <uapi/linux/qg.h> #include "qg-sdam.h" Loading @@ -23,27 +24,45 @@ #define DEFAULT_UPDATE_TIME_MS 64000 #define SOC_SCALE_HYST_MS 2000 static void get_next_update_time(struct qpnp_qg *chip, int *time_ms) #define SOC_SCALE_LOW_TEMP_THRESHOLD 100 static int qg_delta_soc_interval_ms = 20000; module_param_named( delta_soc_interval_ms, qg_delta_soc_interval_ms, int, 0600 ); static void get_next_update_time(struct qpnp_qg *chip) { int rc = 0, full_time_ms = 0, rt_time_ms = 0; int soc_points = 0, batt_temp = 0; int min_delta_soc_interval_ms = qg_delta_soc_interval_ms; int rc = 0, rt_time_ms = 0, full_time_ms = DEFAULT_UPDATE_TIME_MS; *time_ms = DEFAULT_UPDATE_TIME_MS; get_fifo_done_time(chip, false, &full_time_ms); get_fifo_done_time(chip, true, &rt_time_ms); rc = get_fifo_done_time(chip, false, &full_time_ms); if (rc < 0) return; full_time_ms = CAP(0, DEFAULT_UPDATE_TIME_MS, full_time_ms - rt_time_ms); soc_points = abs(chip->msoc - chip->catch_up_soc); if (chip->maint_soc > 0) soc_points = max(abs(chip->msoc - chip->maint_soc), soc_points); soc_points /= chip->dt.delta_soc; rc = get_fifo_done_time(chip, true, &rt_time_ms); /* Lower the delta soc interval by half at cold */ rc = qg_get_battery_temp(chip, &batt_temp); if (rc < 0) return; pr_err("Failed to read battery temperature rc=%d\n", rc); *time_ms = full_time_ms - rt_time_ms; if (batt_temp < SOC_SCALE_LOW_TEMP_THRESHOLD) min_delta_soc_interval_ms = min_delta_soc_interval_ms / 2; if (*time_ms < 0) *time_ms = 0; chip->next_wakeup_ms = (full_time_ms / (soc_points + 1)) - SOC_SCALE_HYST_MS; chip->next_wakeup_ms = max(chip->next_wakeup_ms, min_delta_soc_interval_ms); qg_dbg(chip, QG_DEBUG_SOC, "SOC scale next-update-time %d secs\n", *time_ms / 1000); qg_dbg(chip, QG_DEBUG_SOC, "fifo_full_time=%d secs fifo_real_time=%d secs soc_scale_points=%d\n", full_time_ms / 1000, rt_time_ms / 1000, soc_points); } static bool is_scaling_required(struct qpnp_qg *chip) Loading @@ -51,6 +70,10 @@ static bool is_scaling_required(struct qpnp_qg *chip) if (!chip->profile_loaded) return false; if (chip->maint_soc > 0 && (abs(chip->maint_soc - chip->msoc) >= chip->dt.delta_soc)) return true; if ((abs(chip->catch_up_soc - chip->msoc) < chip->dt.delta_soc) && chip->catch_up_soc != 0 && chip->catch_up_soc != 100) return false; Loading @@ -75,25 +98,36 @@ static void update_msoc(struct qpnp_qg *chip) /* SOC increased */ if (is_usb_present(chip)) /* Increment if USB is present */ chip->msoc += chip->dt.delta_soc; } else { } else if (chip->catch_up_soc < chip->msoc) { /* SOC dropped */ chip->msoc -= chip->dt.delta_soc; } chip->msoc = CAP(0, 100, chip->msoc); if (chip->maint_soc > 0 && chip->msoc < chip->maint_soc) { chip->maint_soc -= chip->dt.delta_soc; chip->maint_soc = CAP(0, 100, chip->maint_soc); } /* maint_soc dropped below msoc, skip using it */ if (chip->maint_soc <= chip->msoc) chip->maint_soc = -EINVAL; /* update the SOC register */ rc = qg_write_monotonic_soc(chip, chip->msoc); if (rc < 0) pr_err("Failed to update MSOC register rc=%d\n", rc); /* update SDAM with the new MSOC */ chip->sdam_data[SDAM_SOC] = chip->msoc; rc = qg_sdam_write(SDAM_SOC, chip->msoc); if (rc < 0) pr_err("Failed to update SDAM with MSOC rc=%d\n", rc); qg_dbg(chip, QG_DEBUG_SOC, "SOC scale: Update msoc=%d catch_up_soc=%d delta_soc=%d\n", chip->msoc, chip->catch_up_soc, chip->dt.delta_soc); "SOC scale: Update maint_soc=%d msoc=%d catch_up_soc=%d delta_soc=%d\n", chip->maint_soc, chip->msoc, chip->catch_up_soc, chip->dt.delta_soc); } static void scale_soc_stop(struct qpnp_qg *chip) Loading Loading @@ -155,8 +189,7 @@ static enum alarmtimer_restart int qg_scale_soc(struct qpnp_qg *chip, bool force_soc) { int soc_points = 0; int rc = 0, time_ms = 0; int rc = 0; mutex_lock(&chip->soc_lock); Loading @@ -183,13 +216,7 @@ int qg_scale_soc(struct qpnp_qg *chip, bool force_soc) update_msoc(chip); if (is_scaling_required(chip)) { get_next_update_time(chip, &time_ms); soc_points = abs(chip->msoc - chip->catch_up_soc) / chip->dt.delta_soc; chip->next_wakeup_ms = (time_ms / (soc_points + 1)) - SOC_SCALE_HYST_MS; if (chip->next_wakeup_ms < 0) chip->next_wakeup_ms = 1; /* wake up immediately */ get_next_update_time(chip); alarm_start_relative(&chip->alarm_timer, ms_to_ktime(chip->next_wakeup_ms)); } else { Loading @@ -198,9 +225,9 @@ int qg_scale_soc(struct qpnp_qg *chip, bool force_soc) } qg_dbg(chip, QG_DEBUG_SOC, "SOC scale: msoc=%d catch_up_soc=%d delta_soc=%d soc_points=%d next_wakeup=%d sec\n", "SOC scale: msoc=%d catch_up_soc=%d delta_soc=%d next_wakeup=%d sec\n", chip->msoc, chip->catch_up_soc, chip->dt.delta_soc, soc_points, chip->next_wakeup_ms / 1000); chip->next_wakeup_ms / 1000); done_psy: power_supply_changed(chip->qg_psy); Loading drivers/power/supply/qcom/qg-util.c +26 −0 Original line number Diff line number Diff line Loading @@ -15,6 +15,7 @@ #include <linux/device.h> #include <linux/interrupt.h> #include <linux/power_supply.h> #include <linux/qpnp/qpnp-adc.h> #include <linux/regmap.h> #include <linux/rtc.h> #include <uapi/linux/qg.h> Loading @@ -22,6 +23,7 @@ #include "qg-core.h" #include "qg-reg.h" #include "qg-defs.h" #include "qg-util.h" static inline bool is_sticky_register(u32 addr) { Loading Loading @@ -290,3 +292,27 @@ int qg_write_monotonic_soc(struct qpnp_qg *chip, int msoc) return rc; } int qg_get_battery_temp(struct qpnp_qg *chip, int *temp) { int rc = 0; struct qpnp_vadc_result result; if (chip->battery_missing) { *temp = 250; return 0; } rc = qpnp_vadc_read(chip->vadc_dev, VADC_BAT_THERM_PU2, &result); if (rc) { pr_err("Failed reading adc channel=%d, rc=%d\n", VADC_BAT_THERM_PU2, rc); return rc; } pr_debug("batt_temp = %lld meas = 0x%llx\n", result.physical, result.measurement); *temp = (int)result.physical; return rc; } Loading
Documentation/devicetree/bindings/power/supply/qcom/qpnp-qg.txt +8 −0 Original line number Diff line number Diff line Loading @@ -156,6 +156,14 @@ First Level Node - QGAUGE device Definition: A boolean property that when defined holds SOC at 100% when the battery is full until recharge starts. - qcom,linearize-soc Usage: optional Value type: <empty> Definition: A boolean property that when defined linearizes SOC when the SOC drops after charge termination monotonically to improve the user experience. This is applicable only if "qcom,hold-soc-while-full" is specified. ========================================================== Second Level Nodes - Peripherals managed by QGAUGE driver ========================================================== Loading
drivers/power/supply/qcom/qg-battery-profile.c +1 −1 Original line number Diff line number Diff line Loading @@ -163,7 +163,7 @@ static long qg_battery_data_ioctl(struct file *file, unsigned int cmd, } break; case BPIOCXVAR: if (bp.table_index < TABLE_Z1 || bp.table_index > TABLE_MAX) { if (bp.table_index < TABLE_Z1 || bp.table_index >= TABLE_MAX) { pr_err("Invalid table index %d for VAR lookup\n", bp.table_index); rc = -EINVAL; Loading
drivers/power/supply/qcom/qg-core.h +2 −0 Original line number Diff line number Diff line Loading @@ -45,6 +45,7 @@ struct qg_dt { int rbat_conn_mohm; int ignore_shutdown_soc_secs; bool hold_soc_while_full; bool linearize_soc; }; struct qpnp_qg { Loading Loading @@ -102,6 +103,7 @@ struct qpnp_qg { /* soc params */ int catch_up_soc; int maint_soc; int msoc; int pon_soc; struct alarm alarm_timer; Loading
drivers/power/supply/qcom/qg-soc.c +55 −28 Original line number Diff line number Diff line Loading @@ -13,6 +13,7 @@ #define pr_fmt(fmt) "QG-K: %s: " fmt, __func__ #include <linux/alarmtimer.h> #include <linux/module.h> #include <linux/power_supply.h> #include <uapi/linux/qg.h> #include "qg-sdam.h" Loading @@ -23,27 +24,45 @@ #define DEFAULT_UPDATE_TIME_MS 64000 #define SOC_SCALE_HYST_MS 2000 static void get_next_update_time(struct qpnp_qg *chip, int *time_ms) #define SOC_SCALE_LOW_TEMP_THRESHOLD 100 static int qg_delta_soc_interval_ms = 20000; module_param_named( delta_soc_interval_ms, qg_delta_soc_interval_ms, int, 0600 ); static void get_next_update_time(struct qpnp_qg *chip) { int rc = 0, full_time_ms = 0, rt_time_ms = 0; int soc_points = 0, batt_temp = 0; int min_delta_soc_interval_ms = qg_delta_soc_interval_ms; int rc = 0, rt_time_ms = 0, full_time_ms = DEFAULT_UPDATE_TIME_MS; *time_ms = DEFAULT_UPDATE_TIME_MS; get_fifo_done_time(chip, false, &full_time_ms); get_fifo_done_time(chip, true, &rt_time_ms); rc = get_fifo_done_time(chip, false, &full_time_ms); if (rc < 0) return; full_time_ms = CAP(0, DEFAULT_UPDATE_TIME_MS, full_time_ms - rt_time_ms); soc_points = abs(chip->msoc - chip->catch_up_soc); if (chip->maint_soc > 0) soc_points = max(abs(chip->msoc - chip->maint_soc), soc_points); soc_points /= chip->dt.delta_soc; rc = get_fifo_done_time(chip, true, &rt_time_ms); /* Lower the delta soc interval by half at cold */ rc = qg_get_battery_temp(chip, &batt_temp); if (rc < 0) return; pr_err("Failed to read battery temperature rc=%d\n", rc); *time_ms = full_time_ms - rt_time_ms; if (batt_temp < SOC_SCALE_LOW_TEMP_THRESHOLD) min_delta_soc_interval_ms = min_delta_soc_interval_ms / 2; if (*time_ms < 0) *time_ms = 0; chip->next_wakeup_ms = (full_time_ms / (soc_points + 1)) - SOC_SCALE_HYST_MS; chip->next_wakeup_ms = max(chip->next_wakeup_ms, min_delta_soc_interval_ms); qg_dbg(chip, QG_DEBUG_SOC, "SOC scale next-update-time %d secs\n", *time_ms / 1000); qg_dbg(chip, QG_DEBUG_SOC, "fifo_full_time=%d secs fifo_real_time=%d secs soc_scale_points=%d\n", full_time_ms / 1000, rt_time_ms / 1000, soc_points); } static bool is_scaling_required(struct qpnp_qg *chip) Loading @@ -51,6 +70,10 @@ static bool is_scaling_required(struct qpnp_qg *chip) if (!chip->profile_loaded) return false; if (chip->maint_soc > 0 && (abs(chip->maint_soc - chip->msoc) >= chip->dt.delta_soc)) return true; if ((abs(chip->catch_up_soc - chip->msoc) < chip->dt.delta_soc) && chip->catch_up_soc != 0 && chip->catch_up_soc != 100) return false; Loading @@ -75,25 +98,36 @@ static void update_msoc(struct qpnp_qg *chip) /* SOC increased */ if (is_usb_present(chip)) /* Increment if USB is present */ chip->msoc += chip->dt.delta_soc; } else { } else if (chip->catch_up_soc < chip->msoc) { /* SOC dropped */ chip->msoc -= chip->dt.delta_soc; } chip->msoc = CAP(0, 100, chip->msoc); if (chip->maint_soc > 0 && chip->msoc < chip->maint_soc) { chip->maint_soc -= chip->dt.delta_soc; chip->maint_soc = CAP(0, 100, chip->maint_soc); } /* maint_soc dropped below msoc, skip using it */ if (chip->maint_soc <= chip->msoc) chip->maint_soc = -EINVAL; /* update the SOC register */ rc = qg_write_monotonic_soc(chip, chip->msoc); if (rc < 0) pr_err("Failed to update MSOC register rc=%d\n", rc); /* update SDAM with the new MSOC */ chip->sdam_data[SDAM_SOC] = chip->msoc; rc = qg_sdam_write(SDAM_SOC, chip->msoc); if (rc < 0) pr_err("Failed to update SDAM with MSOC rc=%d\n", rc); qg_dbg(chip, QG_DEBUG_SOC, "SOC scale: Update msoc=%d catch_up_soc=%d delta_soc=%d\n", chip->msoc, chip->catch_up_soc, chip->dt.delta_soc); "SOC scale: Update maint_soc=%d msoc=%d catch_up_soc=%d delta_soc=%d\n", chip->maint_soc, chip->msoc, chip->catch_up_soc, chip->dt.delta_soc); } static void scale_soc_stop(struct qpnp_qg *chip) Loading Loading @@ -155,8 +189,7 @@ static enum alarmtimer_restart int qg_scale_soc(struct qpnp_qg *chip, bool force_soc) { int soc_points = 0; int rc = 0, time_ms = 0; int rc = 0; mutex_lock(&chip->soc_lock); Loading @@ -183,13 +216,7 @@ int qg_scale_soc(struct qpnp_qg *chip, bool force_soc) update_msoc(chip); if (is_scaling_required(chip)) { get_next_update_time(chip, &time_ms); soc_points = abs(chip->msoc - chip->catch_up_soc) / chip->dt.delta_soc; chip->next_wakeup_ms = (time_ms / (soc_points + 1)) - SOC_SCALE_HYST_MS; if (chip->next_wakeup_ms < 0) chip->next_wakeup_ms = 1; /* wake up immediately */ get_next_update_time(chip); alarm_start_relative(&chip->alarm_timer, ms_to_ktime(chip->next_wakeup_ms)); } else { Loading @@ -198,9 +225,9 @@ int qg_scale_soc(struct qpnp_qg *chip, bool force_soc) } qg_dbg(chip, QG_DEBUG_SOC, "SOC scale: msoc=%d catch_up_soc=%d delta_soc=%d soc_points=%d next_wakeup=%d sec\n", "SOC scale: msoc=%d catch_up_soc=%d delta_soc=%d next_wakeup=%d sec\n", chip->msoc, chip->catch_up_soc, chip->dt.delta_soc, soc_points, chip->next_wakeup_ms / 1000); chip->next_wakeup_ms / 1000); done_psy: power_supply_changed(chip->qg_psy); Loading
drivers/power/supply/qcom/qg-util.c +26 −0 Original line number Diff line number Diff line Loading @@ -15,6 +15,7 @@ #include <linux/device.h> #include <linux/interrupt.h> #include <linux/power_supply.h> #include <linux/qpnp/qpnp-adc.h> #include <linux/regmap.h> #include <linux/rtc.h> #include <uapi/linux/qg.h> Loading @@ -22,6 +23,7 @@ #include "qg-core.h" #include "qg-reg.h" #include "qg-defs.h" #include "qg-util.h" static inline bool is_sticky_register(u32 addr) { Loading Loading @@ -290,3 +292,27 @@ int qg_write_monotonic_soc(struct qpnp_qg *chip, int msoc) return rc; } int qg_get_battery_temp(struct qpnp_qg *chip, int *temp) { int rc = 0; struct qpnp_vadc_result result; if (chip->battery_missing) { *temp = 250; return 0; } rc = qpnp_vadc_read(chip->vadc_dev, VADC_BAT_THERM_PU2, &result); if (rc) { pr_err("Failed reading adc channel=%d, rc=%d\n", VADC_BAT_THERM_PU2, rc); return rc; } pr_debug("batt_temp = %lld meas = 0x%llx\n", result.physical, result.measurement); *temp = (int)result.physical; return rc; }