blob: 1d7616ea31037e81f6343cfadbdeda0e0afb06b4 [file] [log] [blame]
Ashay Jaiswald7a53152018-07-23 16:12:25 +05301/* Copyright (c) 2017-2018 The Linux Foundation. All rights reserved.
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +05302 *
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#define pr_fmt(fmt) "QCOM-STEPCHG: %s: " fmt, __func__
13
14#include <linux/delay.h>
15#include <linux/module.h>
Fenglin Wudd8f4cb2017-10-17 11:09:46 +080016#include <linux/of.h>
17#include <linux/of_batterydata.h>
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +053018#include <linux/power_supply.h>
19#include <linux/slab.h>
20#include <linux/pmic-voter.h>
21#include "step-chg-jeita.h"
22
23#define MAX_STEP_CHG_ENTRIES 8
24#define STEP_CHG_VOTER "STEP_CHG_VOTER"
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +053025#define JEITA_VOTER "JEITA_VOTER"
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +053026
27#define is_between(left, right, value) \
28 (((left) >= (right) && (left) >= (value) \
29 && (value) >= (right)) \
30 || ((left) <= (right) && (left) <= (value) \
31 && (value) <= (right)))
32
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +053033struct range_data {
34 u32 low_threshold;
35 u32 high_threshold;
36 u32 value;
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +053037};
38
39struct step_chg_cfg {
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +053040 u32 psy_prop;
41 char *prop_name;
42 int hysteresis;
43 struct range_data fcc_cfg[MAX_STEP_CHG_ENTRIES];
44};
45
46struct jeita_fcc_cfg {
47 u32 psy_prop;
48 char *prop_name;
49 int hysteresis;
50 struct range_data fcc_cfg[MAX_STEP_CHG_ENTRIES];
51};
52
53struct jeita_fv_cfg {
54 u32 psy_prop;
55 char *prop_name;
56 int hysteresis;
57 struct range_data fv_cfg[MAX_STEP_CHG_ENTRIES];
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +053058};
59
60struct step_chg_info {
Fenglin Wudd8f4cb2017-10-17 11:09:46 +080061 struct device *dev;
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +053062 ktime_t step_last_update_time;
63 ktime_t jeita_last_update_time;
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +053064 bool step_chg_enable;
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +053065 bool sw_jeita_enable;
Fenglin Wudd8f4cb2017-10-17 11:09:46 +080066 bool config_is_read;
67 bool step_chg_cfg_valid;
68 bool sw_jeita_cfg_valid;
69 bool soc_based_step_chg;
70 bool batt_missing;
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +053071 int jeita_fcc_index;
72 int jeita_fv_index;
73 int step_index;
Fenglin Wudd8f4cb2017-10-17 11:09:46 +080074 int get_config_retry_count;
75
76 struct step_chg_cfg *step_chg_config;
77 struct jeita_fcc_cfg *jeita_fcc_config;
78 struct jeita_fv_cfg *jeita_fv_config;
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +053079
80 struct votable *fcc_votable;
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +053081 struct votable *fv_votable;
Ashay Jaiswald7a53152018-07-23 16:12:25 +053082 struct votable *usb_icl_votable;
jessicatsengcb6c2482020-04-27 19:09:56 +080083 struct votable *rechg_vol_votable;
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +053084 struct wakeup_source *step_chg_ws;
85 struct power_supply *batt_psy;
Fenglin Wudd8f4cb2017-10-17 11:09:46 +080086 struct power_supply *bms_psy;
Umang Agrawal271536f2018-03-15 12:55:57 +053087 struct power_supply *main_psy;
Ashay Jaiswald7a53152018-07-23 16:12:25 +053088 struct power_supply *usb_psy;
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +053089 struct delayed_work status_change_work;
Fenglin Wudd8f4cb2017-10-17 11:09:46 +080090 struct delayed_work get_config_work;
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +053091 struct notifier_block nb;
92};
93
94static struct step_chg_info *the_chip;
95
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +053096#define STEP_CHG_HYSTERISIS_DELAY_US 5000000 /* 5 secs */
97
Fenglin Wudd8f4cb2017-10-17 11:09:46 +080098#define BATT_HOT_DECIDEGREE_MAX 600
99#define GET_CONFIG_DELAY_MS 2000
100#define GET_CONFIG_RETRY_COUNT 50
101#define WAIT_BATT_ID_READY_MS 200
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530102
103static bool is_batt_available(struct step_chg_info *chip)
104{
105 if (!chip->batt_psy)
106 chip->batt_psy = power_supply_get_by_name("battery");
107
108 if (!chip->batt_psy)
109 return false;
110
111 return true;
112}
113
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800114static bool is_bms_available(struct step_chg_info *chip)
115{
116 if (!chip->bms_psy)
117 chip->bms_psy = power_supply_get_by_name("bms");
118
119 if (!chip->bms_psy)
120 return false;
121
122 return true;
123}
124
Ashay Jaiswald7a53152018-07-23 16:12:25 +0530125static bool is_usb_available(struct step_chg_info *chip)
126{
127 if (!chip->usb_psy)
128 chip->usb_psy = power_supply_get_by_name("usb");
129
130 if (!chip->usb_psy)
131 return false;
132
133 return true;
134}
135
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800136static int read_range_data_from_node(struct device_node *node,
137 const char *prop_str, struct range_data *ranges,
138 u32 max_threshold, u32 max_value)
139{
140 int rc = 0, i, length, per_tuple_length, tuples;
141
142 rc = of_property_count_elems_of_size(node, prop_str, sizeof(u32));
143 if (rc < 0) {
144 pr_err("Count %s failed, rc=%d\n", prop_str, rc);
145 return rc;
146 }
147
148 length = rc;
149 per_tuple_length = sizeof(struct range_data) / sizeof(u32);
150 if (length % per_tuple_length) {
151 pr_err("%s length (%d) should be multiple of %d\n",
152 prop_str, length, per_tuple_length);
153 return -EINVAL;
154 }
155 tuples = length / per_tuple_length;
156
157 if (tuples > MAX_STEP_CHG_ENTRIES) {
158 pr_err("too many entries(%d), only %d allowed\n",
159 tuples, MAX_STEP_CHG_ENTRIES);
160 return -EINVAL;
161 }
162
163 rc = of_property_read_u32_array(node, prop_str,
164 (u32 *)ranges, length);
165 if (rc) {
166 pr_err("Read %s failed, rc=%d", prop_str, rc);
167 return rc;
168 }
169
170 for (i = 0; i < tuples; i++) {
171 if (ranges[i].low_threshold >
172 ranges[i].high_threshold) {
173 pr_err("%s thresholds should be in ascendant ranges\n",
174 prop_str);
175 rc = -EINVAL;
176 goto clean;
177 }
178
179 if (i != 0) {
180 if (ranges[i - 1].high_threshold >
181 ranges[i].low_threshold) {
182 pr_err("%s thresholds should be in ascendant ranges\n",
183 prop_str);
184 rc = -EINVAL;
185 goto clean;
186 }
187 }
188
189 if (ranges[i].low_threshold > max_threshold)
190 ranges[i].low_threshold = max_threshold;
191 if (ranges[i].high_threshold > max_threshold)
192 ranges[i].high_threshold = max_threshold;
193 if (ranges[i].value > max_value)
194 ranges[i].value = max_value;
195 }
196
197 return rc;
198clean:
199 memset(ranges, 0, tuples * sizeof(struct range_data));
200 return rc;
201}
202
203static int get_step_chg_jeita_setting_from_profile(struct step_chg_info *chip)
204{
205 struct device_node *batt_node, *profile_node;
206 u32 max_fv_uv, max_fcc_ma;
207 const char *batt_type_str;
208 const __be32 *handle;
209 int batt_id_ohms, rc;
210 union power_supply_propval prop = {0, };
211
212 handle = of_get_property(chip->dev->of_node,
213 "qcom,battery-data", NULL);
214 if (!handle) {
215 pr_debug("ignore getting sw-jeita/step charging settings from profile\n");
216 return 0;
217 }
218
219 batt_node = of_find_node_by_phandle(be32_to_cpup(handle));
220 if (!batt_node) {
221 pr_err("Get battery data node failed\n");
222 return -EINVAL;
223 }
224
225 if (!is_bms_available(chip))
226 return -ENODEV;
227
228 power_supply_get_property(chip->bms_psy,
229 POWER_SUPPLY_PROP_RESISTANCE_ID, &prop);
230 batt_id_ohms = prop.intval;
231
232 /* bms_psy has not yet read the batt_id */
233 if (batt_id_ohms < 0)
234 return -EBUSY;
235
236 profile_node = of_batterydata_get_best_profile(batt_node,
237 batt_id_ohms / 1000, NULL);
238 if (IS_ERR(profile_node))
239 return PTR_ERR(profile_node);
240
241 if (!profile_node) {
242 pr_err("Couldn't find profile\n");
243 return -ENODATA;
244 }
245
246 rc = of_property_read_string(profile_node, "qcom,battery-type",
247 &batt_type_str);
248 if (rc < 0) {
249 pr_err("battery type unavailable, rc:%d\n", rc);
250 return rc;
251 }
252 pr_debug("battery: %s detected, getting sw-jeita/step charging settings\n",
253 batt_type_str);
254
255 rc = of_property_read_u32(profile_node, "qcom,max-voltage-uv",
256 &max_fv_uv);
257 if (rc < 0) {
258 pr_err("max-voltage_uv reading failed, rc=%d\n", rc);
259 return rc;
260 }
261
262 rc = of_property_read_u32(profile_node, "qcom,fastchg-current-ma",
263 &max_fcc_ma);
264 if (rc < 0) {
265 pr_err("max-fastchg-current-ma reading failed, rc=%d\n", rc);
266 return rc;
267 }
268
269 chip->soc_based_step_chg =
270 of_property_read_bool(profile_node, "qcom,soc-based-step-chg");
271 if (chip->soc_based_step_chg) {
272 chip->step_chg_config->psy_prop = POWER_SUPPLY_PROP_CAPACITY,
273 chip->step_chg_config->prop_name = "SOC";
274 chip->step_chg_config->hysteresis = 0;
275 }
276
277 chip->step_chg_cfg_valid = true;
278 rc = read_range_data_from_node(profile_node,
279 "qcom,step-chg-ranges",
280 chip->step_chg_config->fcc_cfg,
281 chip->soc_based_step_chg ? 100 : max_fv_uv,
282 max_fcc_ma * 1000);
283 if (rc < 0) {
284 pr_debug("Read qcom,step-chg-ranges failed from battery profile, rc=%d\n",
285 rc);
286 chip->step_chg_cfg_valid = false;
287 }
288
289 chip->sw_jeita_cfg_valid = true;
290 rc = read_range_data_from_node(profile_node,
291 "qcom,jeita-fcc-ranges",
292 chip->jeita_fcc_config->fcc_cfg,
293 BATT_HOT_DECIDEGREE_MAX, max_fcc_ma * 1000);
294 if (rc < 0) {
295 pr_debug("Read qcom,jeita-fcc-ranges failed from battery profile, rc=%d\n",
296 rc);
297 chip->sw_jeita_cfg_valid = false;
298 }
299
300 rc = read_range_data_from_node(profile_node,
301 "qcom,jeita-fv-ranges",
302 chip->jeita_fv_config->fv_cfg,
303 BATT_HOT_DECIDEGREE_MAX, max_fv_uv);
304 if (rc < 0) {
305 pr_debug("Read qcom,jeita-fv-ranges failed from battery profile, rc=%d\n",
306 rc);
307 chip->sw_jeita_cfg_valid = false;
308 }
309
310 return rc;
311}
312
313static void get_config_work(struct work_struct *work)
314{
315 struct step_chg_info *chip = container_of(work,
316 struct step_chg_info, get_config_work.work);
317 int i, rc;
318
319 chip->config_is_read = false;
320 rc = get_step_chg_jeita_setting_from_profile(chip);
321
322 if (rc < 0) {
323 if (rc == -ENODEV || rc == -EBUSY) {
324 if (chip->get_config_retry_count++
325 < GET_CONFIG_RETRY_COUNT) {
326 pr_debug("bms_psy is not ready, retry: %d\n",
327 chip->get_config_retry_count);
328 goto reschedule;
329 }
330 }
331 }
332
333 chip->config_is_read = true;
334
335 for (i = 0; i < MAX_STEP_CHG_ENTRIES; i++)
336 pr_debug("step-chg-cfg: %duV(SoC) ~ %duV(SoC), %duA\n",
337 chip->step_chg_config->fcc_cfg[i].low_threshold,
338 chip->step_chg_config->fcc_cfg[i].high_threshold,
339 chip->step_chg_config->fcc_cfg[i].value);
340 for (i = 0; i < MAX_STEP_CHG_ENTRIES; i++)
341 pr_debug("jeita-fcc-cfg: %ddecidegree ~ %ddecidegre, %duA\n",
342 chip->jeita_fcc_config->fcc_cfg[i].low_threshold,
343 chip->jeita_fcc_config->fcc_cfg[i].high_threshold,
344 chip->jeita_fcc_config->fcc_cfg[i].value);
345 for (i = 0; i < MAX_STEP_CHG_ENTRIES; i++)
346 pr_debug("jeita-fv-cfg: %ddecidegree ~ %ddecidegre, %duV\n",
347 chip->jeita_fv_config->fv_cfg[i].low_threshold,
348 chip->jeita_fv_config->fv_cfg[i].high_threshold,
349 chip->jeita_fv_config->fv_cfg[i].value);
350
351 return;
352
353reschedule:
354 schedule_delayed_work(&chip->get_config_work,
355 msecs_to_jiffies(GET_CONFIG_DELAY_MS));
356
357}
358
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530359static int get_val(struct range_data *range, int hysteresis, int current_index,
360 int threshold,
361 int *new_index, int *val)
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530362{
363 int i;
364
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530365 *new_index = -EINVAL;
Guru Das Srinagesh9a57ce12018-06-26 18:08:19 -0700366
367 /*
368 * If the threshold is lesser than the minimum allowed range,
369 * return -ENODATA.
370 */
371 if (threshold < range[0].low_threshold)
372 return -ENODATA;
373
374 /* First try to find the matching index without hysteresis */
375 for (i = 0; i < MAX_STEP_CHG_ENTRIES; i++) {
376 if (!range[i].high_threshold && !range[i].low_threshold) {
377 /* First invalid table entry; exit loop */
378 break;
379 }
380
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530381 if (is_between(range[i].low_threshold,
382 range[i].high_threshold, threshold)) {
383 *new_index = i;
384 *val = range[i].value;
Guru Das Srinagesh9a57ce12018-06-26 18:08:19 -0700385 break;
386 }
387 }
388
389 /*
390 * If nothing was found, the threshold exceeds the max range for sure
391 * as the other case where it is lesser than the min range is handled
392 * at the very beginning of this function. Therefore, clip it to the
393 * max allowed range value, which is the one corresponding to the last
394 * valid entry in the battery profile data array.
395 */
396 if (*new_index == -EINVAL) {
397 if (i == 0) {
398 /* Battery profile data array is completely invalid */
399 return -ENODATA;
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530400 }
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530401
Guru Das Srinagesh9a57ce12018-06-26 18:08:19 -0700402 *new_index = (i - 1);
403 *val = range[*new_index].value;
404 }
405
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530406 /*
407 * If we don't have a current_index return this
408 * newfound value. There is no hysterisis from out of range
409 * to in range transition
410 */
411 if (current_index == -EINVAL)
412 return 0;
413
414 /*
415 * Check for hysteresis if it in the neighbourhood
416 * of our current index.
417 */
418 if (*new_index == current_index + 1) {
419 if (threshold < range[*new_index].low_threshold + hysteresis) {
420 /*
421 * Stay in the current index, threshold is not higher
422 * by hysteresis amount
423 */
424 *new_index = current_index;
425 *val = range[current_index].value;
426 }
427 } else if (*new_index == current_index - 1) {
428 if (threshold > range[*new_index].high_threshold - hysteresis) {
429 /*
430 * stay in the current index, threshold is not lower
431 * by hysteresis amount
432 */
433 *new_index = current_index;
434 *val = range[current_index].value;
435 }
436 }
437 return 0;
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530438}
439
440static int handle_step_chg_config(struct step_chg_info *chip)
441{
442 union power_supply_propval pval = {0, };
443 int rc = 0, fcc_ua = 0;
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530444 u64 elapsed_us;
445
446 elapsed_us = ktime_us_delta(ktime_get(), chip->step_last_update_time);
447 if (elapsed_us < STEP_CHG_HYSTERISIS_DELAY_US)
448 goto reschedule;
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530449
450 rc = power_supply_get_property(chip->batt_psy,
451 POWER_SUPPLY_PROP_STEP_CHARGING_ENABLED, &pval);
452 if (rc < 0)
453 chip->step_chg_enable = 0;
454 else
455 chip->step_chg_enable = pval.intval;
456
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800457 if (!chip->step_chg_enable || !chip->step_chg_cfg_valid) {
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530458 if (chip->fcc_votable)
459 vote(chip->fcc_votable, STEP_CHG_VOTER, false, 0);
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530460 goto update_time;
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530461 }
462
463 rc = power_supply_get_property(chip->batt_psy,
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800464 chip->step_chg_config->psy_prop, &pval);
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530465 if (rc < 0) {
466 pr_err("Couldn't read %s property rc=%d\n",
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800467 chip->step_chg_config->prop_name, rc);
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530468 return rc;
469 }
470
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800471 rc = get_val(chip->step_chg_config->fcc_cfg,
472 chip->step_chg_config->hysteresis,
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530473 chip->step_index,
474 pval.intval,
475 &chip->step_index,
476 &fcc_ua);
477 if (rc < 0) {
478 /* remove the vote if no step-based fcc is found */
479 if (chip->fcc_votable)
480 vote(chip->fcc_votable, STEP_CHG_VOTER, false, 0);
481 goto update_time;
482 }
483
484 if (!chip->fcc_votable)
485 chip->fcc_votable = find_votable("FCC");
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530486 if (!chip->fcc_votable)
487 return -EINVAL;
488
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530489 vote(chip->fcc_votable, STEP_CHG_VOTER, true, fcc_ua);
490
491 pr_debug("%s = %d Step-FCC = %duA\n",
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800492 chip->step_chg_config->prop_name, pval.intval, fcc_ua);
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530493
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530494update_time:
495 chip->step_last_update_time = ktime_get();
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530496 return 0;
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530497
498reschedule:
499 /* reschedule 1000uS after the remaining time */
500 return (STEP_CHG_HYSTERISIS_DELAY_US - elapsed_us + 1000);
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530501}
502
jessicatseng751aa082020-05-08 17:36:49 +0800503#define JEITA_SUSPEND_HYST_UV 200000
504#define JEITA_RECHG_HYST_UV 200000
jessicatsengcb6c2482020-04-27 19:09:56 +0800505
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530506static int handle_jeita(struct step_chg_info *chip)
507{
508 union power_supply_propval pval = {0, };
509 int rc = 0, fcc_ua = 0, fv_uv = 0;
510 u64 elapsed_us;
511
Abhijeet Dharmapurikar3a580042017-07-24 09:43:00 -0700512 rc = power_supply_get_property(chip->batt_psy,
513 POWER_SUPPLY_PROP_SW_JEITA_ENABLED, &pval);
514 if (rc < 0)
515 chip->sw_jeita_enable = 0;
516 else
517 chip->sw_jeita_enable = pval.intval;
518
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800519 if (!chip->sw_jeita_enable || !chip->sw_jeita_cfg_valid) {
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530520 if (chip->fcc_votable)
521 vote(chip->fcc_votable, JEITA_VOTER, false, 0);
522 if (chip->fv_votable)
523 vote(chip->fv_votable, JEITA_VOTER, false, 0);
Ashay Jaiswald7a53152018-07-23 16:12:25 +0530524 if (chip->usb_icl_votable)
525 vote(chip->usb_icl_votable, JEITA_VOTER, false, 0);
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530526 return 0;
527 }
528
529 elapsed_us = ktime_us_delta(ktime_get(), chip->jeita_last_update_time);
530 if (elapsed_us < STEP_CHG_HYSTERISIS_DELAY_US)
531 goto reschedule;
532
533 rc = power_supply_get_property(chip->batt_psy,
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800534 chip->jeita_fcc_config->psy_prop, &pval);
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530535 if (rc < 0) {
536 pr_err("Couldn't read %s property rc=%d\n",
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800537 chip->jeita_fcc_config->prop_name, rc);
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530538 return rc;
539 }
540
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800541 rc = get_val(chip->jeita_fcc_config->fcc_cfg,
542 chip->jeita_fcc_config->hysteresis,
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530543 chip->jeita_fcc_index,
544 pval.intval,
545 &chip->jeita_fcc_index,
546 &fcc_ua);
Ashay Jaiswald7a53152018-07-23 16:12:25 +0530547 if (rc < 0)
548 fcc_ua = 0;
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530549
550 if (!chip->fcc_votable)
551 chip->fcc_votable = find_votable("FCC");
552 if (!chip->fcc_votable)
553 /* changing FCC is a must */
554 return -EINVAL;
555
Ashay Jaiswald7a53152018-07-23 16:12:25 +0530556 vote(chip->fcc_votable, JEITA_VOTER, fcc_ua ? true : false, fcc_ua);
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530557
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800558 rc = get_val(chip->jeita_fv_config->fv_cfg,
559 chip->jeita_fv_config->hysteresis,
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530560 chip->jeita_fv_index,
561 pval.intval,
562 &chip->jeita_fv_index,
563 &fv_uv);
Ashay Jaiswald7a53152018-07-23 16:12:25 +0530564 if (rc < 0)
565 fv_uv = 0;
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530566
jessicatsengcb6c2482020-04-27 19:09:56 +0800567 if (!chip->rechg_vol_votable)
568 chip->rechg_vol_votable = find_votable("RECHG_VOL");
569
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530570 chip->fv_votable = find_votable("FV");
571 if (!chip->fv_votable)
572 goto update_time;
573
Ashay Jaiswald7a53152018-07-23 16:12:25 +0530574 if (!chip->usb_icl_votable)
575 chip->usb_icl_votable = find_votable("USB_ICL");
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530576
Ashay Jaiswald7a53152018-07-23 16:12:25 +0530577 if (!chip->usb_icl_votable)
578 goto set_jeita_fv;
579
580 /*
581 * If JEITA float voltage is same as max-vfloat of battery then
582 * skip any further VBAT specific checks.
583 */
584 rc = power_supply_get_property(chip->batt_psy,
585 POWER_SUPPLY_PROP_VOLTAGE_MAX, &pval);
586 if (rc || (pval.intval == fv_uv)) {
587 vote(chip->usb_icl_votable, JEITA_VOTER, false, 0);
588 goto set_jeita_fv;
589 }
590
591 /*
592 * Suspend USB input path if battery voltage is above
593 * JEITA VFLOAT threshold.
594 */
595 if (fv_uv > 0) {
596 rc = power_supply_get_property(chip->batt_psy,
597 POWER_SUPPLY_PROP_VOLTAGE_NOW, &pval);
598 if (!rc && (pval.intval > fv_uv))
599 vote(chip->usb_icl_votable, JEITA_VOTER, true, 0);
600 else if (pval.intval < (fv_uv - JEITA_SUSPEND_HYST_UV))
601 vote(chip->usb_icl_votable, JEITA_VOTER, false, 0);
602 }
603
604set_jeita_fv:
605 vote(chip->fv_votable, JEITA_VOTER, fv_uv ? true : false, fv_uv);
jessicatsengcb6c2482020-04-27 19:09:56 +0800606 vote(chip->rechg_vol_votable,
607 JEITA_VOTER, true, (fv_uv -JEITA_RECHG_HYST_UV));
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530608
609update_time:
610 chip->jeita_last_update_time = ktime_get();
Umang Agrawal271536f2018-03-15 12:55:57 +0530611
612 if (!chip->main_psy)
613 chip->main_psy = power_supply_get_by_name("main");
614 if (chip->main_psy)
615 power_supply_changed(chip->main_psy);
616
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530617 return 0;
618
619reschedule:
620 /* reschedule 1000uS after the remaining time */
621 return (STEP_CHG_HYSTERISIS_DELAY_US - elapsed_us + 1000);
622}
623
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800624static int handle_battery_insertion(struct step_chg_info *chip)
625{
626 int rc;
627 union power_supply_propval pval = {0, };
628
629 rc = power_supply_get_property(chip->batt_psy,
630 POWER_SUPPLY_PROP_PRESENT, &pval);
631 if (rc < 0) {
632 pr_err("Get battery present status failed, rc=%d\n", rc);
633 return rc;
634 }
635
636 if (chip->batt_missing != (!pval.intval)) {
637 chip->batt_missing = !pval.intval;
638 pr_debug("battery %s detected\n",
639 chip->batt_missing ? "removal" : "insertion");
640 if (chip->batt_missing) {
641 chip->step_chg_cfg_valid = false;
642 chip->sw_jeita_cfg_valid = false;
643 chip->get_config_retry_count = 0;
644 } else {
645 /*
646 * Get config for the new inserted battery, delay
647 * to make sure BMS has read out the batt_id.
648 */
649 schedule_delayed_work(&chip->get_config_work,
650 msecs_to_jiffies(WAIT_BATT_ID_READY_MS));
651 }
652 }
653
654 return rc;
655}
656
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530657static void status_change_work(struct work_struct *work)
658{
659 struct step_chg_info *chip = container_of(work,
660 struct step_chg_info, status_change_work.work);
661 int rc = 0;
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530662 int reschedule_us;
663 int reschedule_jeita_work_us = 0;
664 int reschedule_step_work_us = 0;
Ashay Jaiswald7a53152018-07-23 16:12:25 +0530665 union power_supply_propval prop = {0, };
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530666
667 if (!is_batt_available(chip))
Ashay Jaiswald7a53152018-07-23 16:12:25 +0530668 goto exit_work;
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530669
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800670 handle_battery_insertion(chip);
Ashay Jaiswald7a53152018-07-23 16:12:25 +0530671
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530672 /* skip elapsed_us debounce for handling battery temperature */
673 rc = handle_jeita(chip);
674 if (rc > 0)
675 reschedule_jeita_work_us = rc;
676 else if (rc < 0)
677 pr_err("Couldn't handle sw jeita rc = %d\n", rc);
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530678
679 rc = handle_step_chg_config(chip);
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530680 if (rc > 0)
681 reschedule_step_work_us = rc;
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530682 if (rc < 0)
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530683 pr_err("Couldn't handle step rc = %d\n", rc);
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530684
Ashay Jaiswald7a53152018-07-23 16:12:25 +0530685 /* Remove stale votes on USB removal */
686 if (is_usb_available(chip)) {
687 prop.intval = 0;
688 power_supply_get_property(chip->usb_psy,
689 POWER_SUPPLY_PROP_PRESENT, &prop);
690 if (!prop.intval) {
691 if (chip->usb_icl_votable)
692 vote(chip->usb_icl_votable, JEITA_VOTER,
693 false, 0);
694 }
695 }
696
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530697 reschedule_us = min(reschedule_jeita_work_us, reschedule_step_work_us);
698 if (reschedule_us == 0)
Ashay Jaiswald7a53152018-07-23 16:12:25 +0530699 goto exit_work;
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530700 else
701 schedule_delayed_work(&chip->status_change_work,
702 usecs_to_jiffies(reschedule_us));
Ashay Jaiswald7a53152018-07-23 16:12:25 +0530703 return;
704
705exit_work:
706 __pm_relax(chip->step_chg_ws);
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530707}
708
709static int step_chg_notifier_call(struct notifier_block *nb,
710 unsigned long ev, void *v)
711{
712 struct power_supply *psy = v;
713 struct step_chg_info *chip = container_of(nb, struct step_chg_info, nb);
714
715 if (ev != PSY_EVENT_PROP_CHANGED)
716 return NOTIFY_OK;
717
Ashay Jaiswald7a53152018-07-23 16:12:25 +0530718 if ((strcmp(psy->desc->name, "battery") == 0)
719 || (strcmp(psy->desc->name, "usb") == 0)) {
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530720 __pm_stay_awake(chip->step_chg_ws);
721 schedule_delayed_work(&chip->status_change_work, 0);
722 }
723
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800724 if ((strcmp(psy->desc->name, "bms") == 0)) {
725 if (chip->bms_psy == NULL)
726 chip->bms_psy = psy;
727 if (!chip->config_is_read)
728 schedule_delayed_work(&chip->get_config_work, 0);
729 }
730
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530731 return NOTIFY_OK;
732}
733
734static int step_chg_register_notifier(struct step_chg_info *chip)
735{
736 int rc;
737
738 chip->nb.notifier_call = step_chg_notifier_call;
739 rc = power_supply_reg_notifier(&chip->nb);
740 if (rc < 0) {
741 pr_err("Couldn't register psy notifier rc = %d\n", rc);
742 return rc;
743 }
744
745 return 0;
746}
747
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800748int qcom_step_chg_init(struct device *dev,
749 bool step_chg_enable, bool sw_jeita_enable)
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530750{
751 int rc;
752 struct step_chg_info *chip;
753
754 if (the_chip) {
755 pr_err("Already initialized\n");
756 return -EINVAL;
757 }
758
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800759 chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530760 if (!chip)
761 return -ENOMEM;
762
763 chip->step_chg_ws = wakeup_source_register("qcom-step-chg");
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800764 if (!chip->step_chg_ws)
765 return -EINVAL;
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530766
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800767 chip->dev = dev;
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530768 chip->step_chg_enable = step_chg_enable;
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530769 chip->sw_jeita_enable = sw_jeita_enable;
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530770 chip->step_index = -EINVAL;
771 chip->jeita_fcc_index = -EINVAL;
772 chip->jeita_fv_index = -EINVAL;
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530773
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800774 chip->step_chg_config = devm_kzalloc(dev,
775 sizeof(struct step_chg_cfg), GFP_KERNEL);
776 if (!chip->step_chg_config)
777 return -ENOMEM;
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530778
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800779 chip->step_chg_config->psy_prop = POWER_SUPPLY_PROP_VOLTAGE_NOW;
780 chip->step_chg_config->prop_name = "VBATT";
781 chip->step_chg_config->hysteresis = 100000;
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530782
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800783 chip->jeita_fcc_config = devm_kzalloc(dev,
784 sizeof(struct jeita_fcc_cfg), GFP_KERNEL);
785 chip->jeita_fv_config = devm_kzalloc(dev,
786 sizeof(struct jeita_fv_cfg), GFP_KERNEL);
787 if (!chip->jeita_fcc_config || !chip->jeita_fv_config)
788 return -ENOMEM;
789
790 chip->jeita_fcc_config->psy_prop = POWER_SUPPLY_PROP_TEMP;
791 chip->jeita_fcc_config->prop_name = "BATT_TEMP";
792 chip->jeita_fcc_config->hysteresis = 10;
793 chip->jeita_fv_config->psy_prop = POWER_SUPPLY_PROP_TEMP;
794 chip->jeita_fv_config->prop_name = "BATT_TEMP";
795 chip->jeita_fv_config->hysteresis = 10;
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530796
797 INIT_DELAYED_WORK(&chip->status_change_work, status_change_work);
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800798 INIT_DELAYED_WORK(&chip->get_config_work, get_config_work);
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530799
800 rc = step_chg_register_notifier(chip);
801 if (rc < 0) {
802 pr_err("Couldn't register psy notifier rc = %d\n", rc);
803 goto release_wakeup_source;
804 }
805
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800806 schedule_delayed_work(&chip->get_config_work,
807 msecs_to_jiffies(GET_CONFIG_DELAY_MS));
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530808
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800809 the_chip = chip;
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530810
811 return 0;
812
813release_wakeup_source:
814 wakeup_source_unregister(chip->step_chg_ws);
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530815 return rc;
816}
817
818void qcom_step_chg_deinit(void)
819{
820 struct step_chg_info *chip = the_chip;
821
822 if (!chip)
823 return;
824
825 cancel_delayed_work_sync(&chip->status_change_work);
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800826 cancel_delayed_work_sync(&chip->get_config_work);
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530827 power_supply_unreg_notifier(&chip->nb);
828 wakeup_source_unregister(chip->step_chg_ws);
829 the_chip = NULL;
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530830}