Anirudh Ghayal | e6b2f4a | 2018-01-02 19:35:40 +0530 | [diff] [blame] | 1 | /* Copyright (c) 2018 The Linux Foundation. All rights reserved. |
| 2 | * |
| 3 | * This program is free software; you can redistribute it and/or modify |
| 4 | * it under the terms of the GNU General Public License version 2 and |
| 5 | * only version 2 as published by the Free Software Foundation. |
| 6 | * |
| 7 | * This program is distributed in the hope that it will be useful, |
| 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 10 | * GNU General Public License for more details. |
| 11 | */ |
| 12 | |
| 13 | #define pr_fmt(fmt) "QG-K: %s: " fmt, __func__ |
| 14 | |
| 15 | #include <linux/alarmtimer.h> |
Vamshi Krishna B V | a791d52 | 2018-04-10 18:20:42 +0530 | [diff] [blame] | 16 | #include <linux/kernel.h> |
Vamshi Krishna B V | 2b61cf6 | 2018-03-13 13:54:33 +0530 | [diff] [blame] | 17 | #include <linux/module.h> |
Anirudh Ghayal | e6b2f4a | 2018-01-02 19:35:40 +0530 | [diff] [blame] | 18 | #include <linux/power_supply.h> |
| 19 | #include <uapi/linux/qg.h> |
Vamshi Krishna B V | a791d52 | 2018-04-10 18:20:42 +0530 | [diff] [blame] | 20 | #include "fg-alg.h" |
Anirudh Ghayal | e6b2f4a | 2018-01-02 19:35:40 +0530 | [diff] [blame] | 21 | #include "qg-sdam.h" |
| 22 | #include "qg-core.h" |
| 23 | #include "qg-reg.h" |
| 24 | #include "qg-util.h" |
| 25 | #include "qg-defs.h" |
| 26 | |
| 27 | #define DEFAULT_UPDATE_TIME_MS 64000 |
| 28 | #define SOC_SCALE_HYST_MS 2000 |
Vamshi Krishna B V | 2b61cf6 | 2018-03-13 13:54:33 +0530 | [diff] [blame] | 29 | |
| 30 | static int qg_delta_soc_interval_ms = 20000; |
| 31 | module_param_named( |
Anirudh Ghayal | 20386de | 2018-04-16 14:29:56 +0530 | [diff] [blame] | 32 | soc_interval_ms, qg_delta_soc_interval_ms, int, 0600 |
| 33 | ); |
| 34 | |
| 35 | static int qg_delta_soc_cold_interval_ms = 4000; |
| 36 | module_param_named( |
| 37 | soc_cold_interval_ms, qg_delta_soc_cold_interval_ms, int, 0600 |
Vamshi Krishna B V | 2b61cf6 | 2018-03-13 13:54:33 +0530 | [diff] [blame] | 38 | ); |
| 39 | |
| 40 | static void get_next_update_time(struct qpnp_qg *chip) |
Anirudh Ghayal | e6b2f4a | 2018-01-02 19:35:40 +0530 | [diff] [blame] | 41 | { |
Vamshi Krishna B V | 2b61cf6 | 2018-03-13 13:54:33 +0530 | [diff] [blame] | 42 | int soc_points = 0, batt_temp = 0; |
| 43 | int min_delta_soc_interval_ms = qg_delta_soc_interval_ms; |
| 44 | int rc = 0, rt_time_ms = 0, full_time_ms = DEFAULT_UPDATE_TIME_MS; |
Anirudh Ghayal | e6b2f4a | 2018-01-02 19:35:40 +0530 | [diff] [blame] | 45 | |
Vamshi Krishna B V | 2b61cf6 | 2018-03-13 13:54:33 +0530 | [diff] [blame] | 46 | get_fifo_done_time(chip, false, &full_time_ms); |
| 47 | get_fifo_done_time(chip, true, &rt_time_ms); |
Anirudh Ghayal | e6b2f4a | 2018-01-02 19:35:40 +0530 | [diff] [blame] | 48 | |
Vamshi Krishna B V | 2b61cf6 | 2018-03-13 13:54:33 +0530 | [diff] [blame] | 49 | full_time_ms = CAP(0, DEFAULT_UPDATE_TIME_MS, |
| 50 | full_time_ms - rt_time_ms); |
| 51 | |
| 52 | soc_points = abs(chip->msoc - chip->catch_up_soc); |
| 53 | if (chip->maint_soc > 0) |
| 54 | soc_points = max(abs(chip->msoc - chip->maint_soc), soc_points); |
| 55 | soc_points /= chip->dt.delta_soc; |
| 56 | |
| 57 | /* Lower the delta soc interval by half at cold */ |
| 58 | rc = qg_get_battery_temp(chip, &batt_temp); |
Anirudh Ghayal | e6b2f4a | 2018-01-02 19:35:40 +0530 | [diff] [blame] | 59 | if (rc < 0) |
Vamshi Krishna B V | 2b61cf6 | 2018-03-13 13:54:33 +0530 | [diff] [blame] | 60 | pr_err("Failed to read battery temperature rc=%d\n", rc); |
Anirudh Ghayal | 20386de | 2018-04-16 14:29:56 +0530 | [diff] [blame] | 61 | else if (batt_temp < chip->dt.cold_temp_threshold) |
| 62 | min_delta_soc_interval_ms = qg_delta_soc_cold_interval_ms; |
Anirudh Ghayal | e6b2f4a | 2018-01-02 19:35:40 +0530 | [diff] [blame] | 63 | |
Anirudh Ghayal | 20386de | 2018-04-16 14:29:56 +0530 | [diff] [blame] | 64 | if (!min_delta_soc_interval_ms) |
| 65 | min_delta_soc_interval_ms = 1000; /* 1 second */ |
Anirudh Ghayal | e6b2f4a | 2018-01-02 19:35:40 +0530 | [diff] [blame] | 66 | |
Vamshi Krishna B V | 2b61cf6 | 2018-03-13 13:54:33 +0530 | [diff] [blame] | 67 | chip->next_wakeup_ms = (full_time_ms / (soc_points + 1)) |
| 68 | - SOC_SCALE_HYST_MS; |
| 69 | chip->next_wakeup_ms = max(chip->next_wakeup_ms, |
| 70 | min_delta_soc_interval_ms); |
Anirudh Ghayal | e6b2f4a | 2018-01-02 19:35:40 +0530 | [diff] [blame] | 71 | |
Vamshi Krishna B V | 2b61cf6 | 2018-03-13 13:54:33 +0530 | [diff] [blame] | 72 | qg_dbg(chip, QG_DEBUG_SOC, "fifo_full_time=%d secs fifo_real_time=%d secs soc_scale_points=%d\n", |
| 73 | full_time_ms / 1000, rt_time_ms / 1000, soc_points); |
Anirudh Ghayal | e6b2f4a | 2018-01-02 19:35:40 +0530 | [diff] [blame] | 74 | } |
| 75 | |
| 76 | static bool is_scaling_required(struct qpnp_qg *chip) |
| 77 | { |
Anirudh Ghayal | e492338 | 2018-03-11 20:32:10 +0530 | [diff] [blame] | 78 | if (!chip->profile_loaded) |
| 79 | return false; |
| 80 | |
Vamshi Krishna B V | 2b61cf6 | 2018-03-13 13:54:33 +0530 | [diff] [blame] | 81 | if (chip->maint_soc > 0 && |
| 82 | (abs(chip->maint_soc - chip->msoc) >= chip->dt.delta_soc)) |
| 83 | return true; |
| 84 | |
Anirudh Ghayal | e6b2f4a | 2018-01-02 19:35:40 +0530 | [diff] [blame] | 85 | if ((abs(chip->catch_up_soc - chip->msoc) < chip->dt.delta_soc) && |
| 86 | chip->catch_up_soc != 0 && chip->catch_up_soc != 100) |
| 87 | return false; |
| 88 | |
| 89 | if (chip->catch_up_soc == chip->msoc) |
| 90 | /* SOC has not changed */ |
| 91 | return false; |
| 92 | |
| 93 | |
| 94 | if (chip->catch_up_soc > chip->msoc && !is_usb_present(chip)) |
| 95 | /* USB is not present and SOC has increased */ |
| 96 | return false; |
| 97 | |
| 98 | return true; |
| 99 | } |
| 100 | |
| 101 | static void update_msoc(struct qpnp_qg *chip) |
| 102 | { |
Vamshi Krishna B V | a791d52 | 2018-04-10 18:20:42 +0530 | [diff] [blame] | 103 | int rc = 0, batt_temp = 0, batt_soc_32bit = 0; |
| 104 | bool usb_present = is_usb_present(chip); |
Anirudh Ghayal | e6b2f4a | 2018-01-02 19:35:40 +0530 | [diff] [blame] | 105 | |
| 106 | if (chip->catch_up_soc > chip->msoc) { |
| 107 | /* SOC increased */ |
Vamshi Krishna B V | a791d52 | 2018-04-10 18:20:42 +0530 | [diff] [blame] | 108 | if (usb_present) /* Increment if USB is present */ |
Anirudh Ghayal | e6b2f4a | 2018-01-02 19:35:40 +0530 | [diff] [blame] | 109 | chip->msoc += chip->dt.delta_soc; |
Vamshi Krishna B V | 2b61cf6 | 2018-03-13 13:54:33 +0530 | [diff] [blame] | 110 | } else if (chip->catch_up_soc < chip->msoc) { |
Anirudh Ghayal | e6b2f4a | 2018-01-02 19:35:40 +0530 | [diff] [blame] | 111 | /* SOC dropped */ |
| 112 | chip->msoc -= chip->dt.delta_soc; |
| 113 | } |
| 114 | chip->msoc = CAP(0, 100, chip->msoc); |
| 115 | |
Vamshi Krishna B V | 2b61cf6 | 2018-03-13 13:54:33 +0530 | [diff] [blame] | 116 | if (chip->maint_soc > 0 && chip->msoc < chip->maint_soc) { |
| 117 | chip->maint_soc -= chip->dt.delta_soc; |
| 118 | chip->maint_soc = CAP(0, 100, chip->maint_soc); |
| 119 | } |
| 120 | |
| 121 | /* maint_soc dropped below msoc, skip using it */ |
| 122 | if (chip->maint_soc <= chip->msoc) |
| 123 | chip->maint_soc = -EINVAL; |
| 124 | |
Anirudh Ghayal | e6b2f4a | 2018-01-02 19:35:40 +0530 | [diff] [blame] | 125 | /* update the SOC register */ |
| 126 | rc = qg_write_monotonic_soc(chip, chip->msoc); |
| 127 | if (rc < 0) |
| 128 | pr_err("Failed to update MSOC register rc=%d\n", rc); |
| 129 | |
| 130 | /* update SDAM with the new MSOC */ |
Vamshi Krishna B V | 2b61cf6 | 2018-03-13 13:54:33 +0530 | [diff] [blame] | 131 | chip->sdam_data[SDAM_SOC] = chip->msoc; |
Anirudh Ghayal | e6b2f4a | 2018-01-02 19:35:40 +0530 | [diff] [blame] | 132 | rc = qg_sdam_write(SDAM_SOC, chip->msoc); |
| 133 | if (rc < 0) |
| 134 | pr_err("Failed to update SDAM with MSOC rc=%d\n", rc); |
| 135 | |
Vamshi Krishna B V | a791d52 | 2018-04-10 18:20:42 +0530 | [diff] [blame] | 136 | if (!chip->dt.cl_disable && chip->cl->active) { |
| 137 | rc = qg_get_battery_temp(chip, &batt_temp); |
| 138 | if (rc < 0) { |
| 139 | pr_err("Failed to read BATT_TEMP rc=%d\n", rc); |
| 140 | } else { |
| 141 | batt_soc_32bit = div64_u64( |
| 142 | chip->batt_soc * BATT_SOC_32BIT, |
| 143 | QG_SOC_FULL); |
| 144 | cap_learning_update(chip->cl, batt_temp, batt_soc_32bit, |
| 145 | chip->charge_status, chip->charge_done, |
| 146 | usb_present, false); |
| 147 | } |
| 148 | } |
| 149 | |
| 150 | cycle_count_update(chip->counter, |
| 151 | DIV_ROUND_CLOSEST(chip->msoc * 255, 100), |
| 152 | chip->charge_status, chip->charge_done, |
| 153 | usb_present); |
| 154 | |
Anirudh Ghayal | e6b2f4a | 2018-01-02 19:35:40 +0530 | [diff] [blame] | 155 | qg_dbg(chip, QG_DEBUG_SOC, |
Vamshi Krishna B V | 2b61cf6 | 2018-03-13 13:54:33 +0530 | [diff] [blame] | 156 | "SOC scale: Update maint_soc=%d msoc=%d catch_up_soc=%d delta_soc=%d\n", |
| 157 | chip->maint_soc, chip->msoc, |
| 158 | chip->catch_up_soc, chip->dt.delta_soc); |
Anirudh Ghayal | e6b2f4a | 2018-01-02 19:35:40 +0530 | [diff] [blame] | 159 | } |
| 160 | |
| 161 | static void scale_soc_stop(struct qpnp_qg *chip) |
| 162 | { |
| 163 | chip->next_wakeup_ms = 0; |
| 164 | alarm_cancel(&chip->alarm_timer); |
| 165 | |
| 166 | qg_dbg(chip, QG_DEBUG_SOC, |
| 167 | "SOC scale stopped: msoc=%d catch_up_soc=%d\n", |
| 168 | chip->msoc, chip->catch_up_soc); |
| 169 | } |
| 170 | |
| 171 | static void scale_soc_work(struct work_struct *work) |
| 172 | { |
| 173 | struct qpnp_qg *chip = container_of(work, |
| 174 | struct qpnp_qg, scale_soc_work); |
| 175 | |
| 176 | mutex_lock(&chip->soc_lock); |
| 177 | |
| 178 | if (!is_scaling_required(chip)) { |
| 179 | scale_soc_stop(chip); |
| 180 | goto done; |
| 181 | } |
| 182 | |
| 183 | update_msoc(chip); |
| 184 | |
| 185 | if (is_scaling_required(chip)) { |
| 186 | alarm_start_relative(&chip->alarm_timer, |
| 187 | ms_to_ktime(chip->next_wakeup_ms)); |
| 188 | } else { |
| 189 | scale_soc_stop(chip); |
| 190 | goto done_psy; |
| 191 | } |
| 192 | |
| 193 | qg_dbg(chip, QG_DEBUG_SOC, |
| 194 | "SOC scale: Work msoc=%d catch_up_soc=%d delta_soc=%d next_wakeup=%d sec\n", |
| 195 | chip->msoc, chip->catch_up_soc, chip->dt.delta_soc, |
| 196 | chip->next_wakeup_ms / 1000); |
| 197 | |
| 198 | done_psy: |
| 199 | power_supply_changed(chip->qg_psy); |
| 200 | done: |
| 201 | pm_relax(chip->dev); |
| 202 | mutex_unlock(&chip->soc_lock); |
| 203 | } |
| 204 | |
| 205 | static enum alarmtimer_restart |
| 206 | qpnp_msoc_timer(struct alarm *alarm, ktime_t now) |
| 207 | { |
| 208 | struct qpnp_qg *chip = container_of(alarm, |
| 209 | struct qpnp_qg, alarm_timer); |
| 210 | |
| 211 | /* timer callback runs in atomic context, cannot use voter */ |
| 212 | pm_stay_awake(chip->dev); |
| 213 | schedule_work(&chip->scale_soc_work); |
| 214 | |
| 215 | return ALARMTIMER_NORESTART; |
| 216 | } |
| 217 | |
| 218 | int qg_scale_soc(struct qpnp_qg *chip, bool force_soc) |
| 219 | { |
Vamshi Krishna B V | 2b61cf6 | 2018-03-13 13:54:33 +0530 | [diff] [blame] | 220 | int rc = 0; |
Anirudh Ghayal | e6b2f4a | 2018-01-02 19:35:40 +0530 | [diff] [blame] | 221 | |
| 222 | mutex_lock(&chip->soc_lock); |
| 223 | |
| 224 | qg_dbg(chip, QG_DEBUG_SOC, |
| 225 | "SOC scale: Start msoc=%d catch_up_soc=%d delta_soc=%d\n", |
| 226 | chip->msoc, chip->catch_up_soc, chip->dt.delta_soc); |
| 227 | |
| 228 | if (force_soc) { |
| 229 | chip->msoc = chip->catch_up_soc; |
| 230 | rc = qg_write_monotonic_soc(chip, chip->msoc); |
| 231 | if (rc < 0) |
| 232 | pr_err("Failed to update MSOC register rc=%d\n", rc); |
| 233 | |
| 234 | qg_dbg(chip, QG_DEBUG_SOC, |
| 235 | "SOC scale: Forced msoc=%d\n", chip->msoc); |
| 236 | goto done_psy; |
| 237 | } |
| 238 | |
| 239 | if (!is_scaling_required(chip)) { |
| 240 | scale_soc_stop(chip); |
| 241 | goto done; |
| 242 | } |
| 243 | |
| 244 | update_msoc(chip); |
| 245 | |
| 246 | if (is_scaling_required(chip)) { |
Vamshi Krishna B V | 2b61cf6 | 2018-03-13 13:54:33 +0530 | [diff] [blame] | 247 | get_next_update_time(chip); |
Anirudh Ghayal | e6b2f4a | 2018-01-02 19:35:40 +0530 | [diff] [blame] | 248 | alarm_start_relative(&chip->alarm_timer, |
| 249 | ms_to_ktime(chip->next_wakeup_ms)); |
| 250 | } else { |
| 251 | scale_soc_stop(chip); |
| 252 | goto done_psy; |
| 253 | } |
| 254 | |
| 255 | qg_dbg(chip, QG_DEBUG_SOC, |
Vamshi Krishna B V | 2b61cf6 | 2018-03-13 13:54:33 +0530 | [diff] [blame] | 256 | "SOC scale: msoc=%d catch_up_soc=%d delta_soc=%d next_wakeup=%d sec\n", |
| 257 | chip->msoc, chip->catch_up_soc, chip->dt.delta_soc, |
| 258 | chip->next_wakeup_ms / 1000); |
Anirudh Ghayal | e6b2f4a | 2018-01-02 19:35:40 +0530 | [diff] [blame] | 259 | |
| 260 | done_psy: |
| 261 | power_supply_changed(chip->qg_psy); |
| 262 | done: |
| 263 | mutex_unlock(&chip->soc_lock); |
| 264 | return rc; |
| 265 | } |
| 266 | |
| 267 | int qg_soc_init(struct qpnp_qg *chip) |
| 268 | { |
| 269 | if (alarmtimer_get_rtcdev()) { |
| 270 | alarm_init(&chip->alarm_timer, ALARM_BOOTTIME, |
| 271 | qpnp_msoc_timer); |
| 272 | } else { |
| 273 | pr_err("Failed to get soc alarm-timer\n"); |
| 274 | return -EINVAL; |
| 275 | } |
| 276 | INIT_WORK(&chip->scale_soc_work, scale_soc_work); |
| 277 | |
| 278 | return 0; |
| 279 | } |
| 280 | |
| 281 | void qg_soc_exit(struct qpnp_qg *chip) |
| 282 | { |
| 283 | alarm_cancel(&chip->alarm_timer); |
| 284 | } |