blob: 2d147d436aa9caca979e153e83ced2f572bca675 [file] [log] [blame]
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +05301/* Copyright (c) 2017 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#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;
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +053082 struct wakeup_source *step_chg_ws;
83 struct power_supply *batt_psy;
Fenglin Wudd8f4cb2017-10-17 11:09:46 +080084 struct power_supply *bms_psy;
Umang Agrawal271536f2018-03-15 12:55:57 +053085 struct power_supply *main_psy;
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +053086 struct delayed_work status_change_work;
Fenglin Wudd8f4cb2017-10-17 11:09:46 +080087 struct delayed_work get_config_work;
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +053088 struct notifier_block nb;
89};
90
91static struct step_chg_info *the_chip;
92
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +053093#define STEP_CHG_HYSTERISIS_DELAY_US 5000000 /* 5 secs */
94
Fenglin Wudd8f4cb2017-10-17 11:09:46 +080095#define BATT_HOT_DECIDEGREE_MAX 600
96#define GET_CONFIG_DELAY_MS 2000
97#define GET_CONFIG_RETRY_COUNT 50
98#define WAIT_BATT_ID_READY_MS 200
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +053099
100static bool is_batt_available(struct step_chg_info *chip)
101{
102 if (!chip->batt_psy)
103 chip->batt_psy = power_supply_get_by_name("battery");
104
105 if (!chip->batt_psy)
106 return false;
107
108 return true;
109}
110
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800111static bool is_bms_available(struct step_chg_info *chip)
112{
113 if (!chip->bms_psy)
114 chip->bms_psy = power_supply_get_by_name("bms");
115
116 if (!chip->bms_psy)
117 return false;
118
119 return true;
120}
121
122static int read_range_data_from_node(struct device_node *node,
123 const char *prop_str, struct range_data *ranges,
124 u32 max_threshold, u32 max_value)
125{
126 int rc = 0, i, length, per_tuple_length, tuples;
127
128 rc = of_property_count_elems_of_size(node, prop_str, sizeof(u32));
129 if (rc < 0) {
130 pr_err("Count %s failed, rc=%d\n", prop_str, rc);
131 return rc;
132 }
133
134 length = rc;
135 per_tuple_length = sizeof(struct range_data) / sizeof(u32);
136 if (length % per_tuple_length) {
137 pr_err("%s length (%d) should be multiple of %d\n",
138 prop_str, length, per_tuple_length);
139 return -EINVAL;
140 }
141 tuples = length / per_tuple_length;
142
143 if (tuples > MAX_STEP_CHG_ENTRIES) {
144 pr_err("too many entries(%d), only %d allowed\n",
145 tuples, MAX_STEP_CHG_ENTRIES);
146 return -EINVAL;
147 }
148
149 rc = of_property_read_u32_array(node, prop_str,
150 (u32 *)ranges, length);
151 if (rc) {
152 pr_err("Read %s failed, rc=%d", prop_str, rc);
153 return rc;
154 }
155
156 for (i = 0; i < tuples; i++) {
157 if (ranges[i].low_threshold >
158 ranges[i].high_threshold) {
159 pr_err("%s thresholds should be in ascendant ranges\n",
160 prop_str);
161 rc = -EINVAL;
162 goto clean;
163 }
164
165 if (i != 0) {
166 if (ranges[i - 1].high_threshold >
167 ranges[i].low_threshold) {
168 pr_err("%s thresholds should be in ascendant ranges\n",
169 prop_str);
170 rc = -EINVAL;
171 goto clean;
172 }
173 }
174
175 if (ranges[i].low_threshold > max_threshold)
176 ranges[i].low_threshold = max_threshold;
177 if (ranges[i].high_threshold > max_threshold)
178 ranges[i].high_threshold = max_threshold;
179 if (ranges[i].value > max_value)
180 ranges[i].value = max_value;
181 }
182
183 return rc;
184clean:
185 memset(ranges, 0, tuples * sizeof(struct range_data));
186 return rc;
187}
188
189static int get_step_chg_jeita_setting_from_profile(struct step_chg_info *chip)
190{
191 struct device_node *batt_node, *profile_node;
192 u32 max_fv_uv, max_fcc_ma;
193 const char *batt_type_str;
194 const __be32 *handle;
195 int batt_id_ohms, rc;
196 union power_supply_propval prop = {0, };
197
198 handle = of_get_property(chip->dev->of_node,
199 "qcom,battery-data", NULL);
200 if (!handle) {
201 pr_debug("ignore getting sw-jeita/step charging settings from profile\n");
202 return 0;
203 }
204
205 batt_node = of_find_node_by_phandle(be32_to_cpup(handle));
206 if (!batt_node) {
207 pr_err("Get battery data node failed\n");
208 return -EINVAL;
209 }
210
211 if (!is_bms_available(chip))
212 return -ENODEV;
213
214 power_supply_get_property(chip->bms_psy,
215 POWER_SUPPLY_PROP_RESISTANCE_ID, &prop);
216 batt_id_ohms = prop.intval;
217
218 /* bms_psy has not yet read the batt_id */
219 if (batt_id_ohms < 0)
220 return -EBUSY;
221
222 profile_node = of_batterydata_get_best_profile(batt_node,
223 batt_id_ohms / 1000, NULL);
224 if (IS_ERR(profile_node))
225 return PTR_ERR(profile_node);
226
227 if (!profile_node) {
228 pr_err("Couldn't find profile\n");
229 return -ENODATA;
230 }
231
232 rc = of_property_read_string(profile_node, "qcom,battery-type",
233 &batt_type_str);
234 if (rc < 0) {
235 pr_err("battery type unavailable, rc:%d\n", rc);
236 return rc;
237 }
238 pr_debug("battery: %s detected, getting sw-jeita/step charging settings\n",
239 batt_type_str);
240
241 rc = of_property_read_u32(profile_node, "qcom,max-voltage-uv",
242 &max_fv_uv);
243 if (rc < 0) {
244 pr_err("max-voltage_uv reading failed, rc=%d\n", rc);
245 return rc;
246 }
247
248 rc = of_property_read_u32(profile_node, "qcom,fastchg-current-ma",
249 &max_fcc_ma);
250 if (rc < 0) {
251 pr_err("max-fastchg-current-ma reading failed, rc=%d\n", rc);
252 return rc;
253 }
254
255 chip->soc_based_step_chg =
256 of_property_read_bool(profile_node, "qcom,soc-based-step-chg");
257 if (chip->soc_based_step_chg) {
258 chip->step_chg_config->psy_prop = POWER_SUPPLY_PROP_CAPACITY,
259 chip->step_chg_config->prop_name = "SOC";
260 chip->step_chg_config->hysteresis = 0;
261 }
262
263 chip->step_chg_cfg_valid = true;
264 rc = read_range_data_from_node(profile_node,
265 "qcom,step-chg-ranges",
266 chip->step_chg_config->fcc_cfg,
267 chip->soc_based_step_chg ? 100 : max_fv_uv,
268 max_fcc_ma * 1000);
269 if (rc < 0) {
270 pr_debug("Read qcom,step-chg-ranges failed from battery profile, rc=%d\n",
271 rc);
272 chip->step_chg_cfg_valid = false;
273 }
274
275 chip->sw_jeita_cfg_valid = true;
276 rc = read_range_data_from_node(profile_node,
277 "qcom,jeita-fcc-ranges",
278 chip->jeita_fcc_config->fcc_cfg,
279 BATT_HOT_DECIDEGREE_MAX, max_fcc_ma * 1000);
280 if (rc < 0) {
281 pr_debug("Read qcom,jeita-fcc-ranges failed from battery profile, rc=%d\n",
282 rc);
283 chip->sw_jeita_cfg_valid = false;
284 }
285
286 rc = read_range_data_from_node(profile_node,
287 "qcom,jeita-fv-ranges",
288 chip->jeita_fv_config->fv_cfg,
289 BATT_HOT_DECIDEGREE_MAX, max_fv_uv);
290 if (rc < 0) {
291 pr_debug("Read qcom,jeita-fv-ranges failed from battery profile, rc=%d\n",
292 rc);
293 chip->sw_jeita_cfg_valid = false;
294 }
295
296 return rc;
297}
298
299static void get_config_work(struct work_struct *work)
300{
301 struct step_chg_info *chip = container_of(work,
302 struct step_chg_info, get_config_work.work);
303 int i, rc;
304
305 chip->config_is_read = false;
306 rc = get_step_chg_jeita_setting_from_profile(chip);
307
308 if (rc < 0) {
309 if (rc == -ENODEV || rc == -EBUSY) {
310 if (chip->get_config_retry_count++
311 < GET_CONFIG_RETRY_COUNT) {
312 pr_debug("bms_psy is not ready, retry: %d\n",
313 chip->get_config_retry_count);
314 goto reschedule;
315 }
316 }
317 }
318
319 chip->config_is_read = true;
320
321 for (i = 0; i < MAX_STEP_CHG_ENTRIES; i++)
322 pr_debug("step-chg-cfg: %duV(SoC) ~ %duV(SoC), %duA\n",
323 chip->step_chg_config->fcc_cfg[i].low_threshold,
324 chip->step_chg_config->fcc_cfg[i].high_threshold,
325 chip->step_chg_config->fcc_cfg[i].value);
326 for (i = 0; i < MAX_STEP_CHG_ENTRIES; i++)
327 pr_debug("jeita-fcc-cfg: %ddecidegree ~ %ddecidegre, %duA\n",
328 chip->jeita_fcc_config->fcc_cfg[i].low_threshold,
329 chip->jeita_fcc_config->fcc_cfg[i].high_threshold,
330 chip->jeita_fcc_config->fcc_cfg[i].value);
331 for (i = 0; i < MAX_STEP_CHG_ENTRIES; i++)
332 pr_debug("jeita-fv-cfg: %ddecidegree ~ %ddecidegre, %duV\n",
333 chip->jeita_fv_config->fv_cfg[i].low_threshold,
334 chip->jeita_fv_config->fv_cfg[i].high_threshold,
335 chip->jeita_fv_config->fv_cfg[i].value);
336
337 return;
338
339reschedule:
340 schedule_delayed_work(&chip->get_config_work,
341 msecs_to_jiffies(GET_CONFIG_DELAY_MS));
342
343}
344
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530345static int get_val(struct range_data *range, int hysteresis, int current_index,
346 int threshold,
347 int *new_index, int *val)
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530348{
349 int i;
350
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530351 *new_index = -EINVAL;
352 /* first find the matching index without hysteresis */
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530353 for (i = 0; i < MAX_STEP_CHG_ENTRIES; i++)
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530354 if (is_between(range[i].low_threshold,
355 range[i].high_threshold, threshold)) {
356 *new_index = i;
357 *val = range[i].value;
358 }
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530359
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530360 /* if nothing was found, return -ENODATA */
361 if (*new_index == -EINVAL)
362 return -ENODATA;
363 /*
364 * If we don't have a current_index return this
365 * newfound value. There is no hysterisis from out of range
366 * to in range transition
367 */
368 if (current_index == -EINVAL)
369 return 0;
370
371 /*
372 * Check for hysteresis if it in the neighbourhood
373 * of our current index.
374 */
375 if (*new_index == current_index + 1) {
376 if (threshold < range[*new_index].low_threshold + hysteresis) {
377 /*
378 * Stay in the current index, threshold is not higher
379 * by hysteresis amount
380 */
381 *new_index = current_index;
382 *val = range[current_index].value;
383 }
384 } else if (*new_index == current_index - 1) {
385 if (threshold > range[*new_index].high_threshold - hysteresis) {
386 /*
387 * stay in the current index, threshold is not lower
388 * by hysteresis amount
389 */
390 *new_index = current_index;
391 *val = range[current_index].value;
392 }
393 }
394 return 0;
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530395}
396
397static int handle_step_chg_config(struct step_chg_info *chip)
398{
399 union power_supply_propval pval = {0, };
400 int rc = 0, fcc_ua = 0;
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530401 u64 elapsed_us;
402
403 elapsed_us = ktime_us_delta(ktime_get(), chip->step_last_update_time);
404 if (elapsed_us < STEP_CHG_HYSTERISIS_DELAY_US)
405 goto reschedule;
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530406
407 rc = power_supply_get_property(chip->batt_psy,
408 POWER_SUPPLY_PROP_STEP_CHARGING_ENABLED, &pval);
409 if (rc < 0)
410 chip->step_chg_enable = 0;
411 else
412 chip->step_chg_enable = pval.intval;
413
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800414 if (!chip->step_chg_enable || !chip->step_chg_cfg_valid) {
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530415 if (chip->fcc_votable)
416 vote(chip->fcc_votable, STEP_CHG_VOTER, false, 0);
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530417 goto update_time;
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530418 }
419
420 rc = power_supply_get_property(chip->batt_psy,
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800421 chip->step_chg_config->psy_prop, &pval);
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530422 if (rc < 0) {
423 pr_err("Couldn't read %s property rc=%d\n",
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800424 chip->step_chg_config->prop_name, rc);
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530425 return rc;
426 }
427
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800428 rc = get_val(chip->step_chg_config->fcc_cfg,
429 chip->step_chg_config->hysteresis,
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530430 chip->step_index,
431 pval.intval,
432 &chip->step_index,
433 &fcc_ua);
434 if (rc < 0) {
435 /* remove the vote if no step-based fcc is found */
436 if (chip->fcc_votable)
437 vote(chip->fcc_votable, STEP_CHG_VOTER, false, 0);
438 goto update_time;
439 }
440
441 if (!chip->fcc_votable)
442 chip->fcc_votable = find_votable("FCC");
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530443 if (!chip->fcc_votable)
444 return -EINVAL;
445
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530446 vote(chip->fcc_votable, STEP_CHG_VOTER, true, fcc_ua);
447
448 pr_debug("%s = %d Step-FCC = %duA\n",
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800449 chip->step_chg_config->prop_name, pval.intval, fcc_ua);
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530450
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530451update_time:
452 chip->step_last_update_time = ktime_get();
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530453 return 0;
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530454
455reschedule:
456 /* reschedule 1000uS after the remaining time */
457 return (STEP_CHG_HYSTERISIS_DELAY_US - elapsed_us + 1000);
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530458}
459
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530460static int handle_jeita(struct step_chg_info *chip)
461{
462 union power_supply_propval pval = {0, };
463 int rc = 0, fcc_ua = 0, fv_uv = 0;
464 u64 elapsed_us;
465
Abhijeet Dharmapurikar3a580042017-07-24 09:43:00 -0700466 rc = power_supply_get_property(chip->batt_psy,
467 POWER_SUPPLY_PROP_SW_JEITA_ENABLED, &pval);
468 if (rc < 0)
469 chip->sw_jeita_enable = 0;
470 else
471 chip->sw_jeita_enable = pval.intval;
472
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800473 if (!chip->sw_jeita_enable || !chip->sw_jeita_cfg_valid) {
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530474 if (chip->fcc_votable)
475 vote(chip->fcc_votable, JEITA_VOTER, false, 0);
476 if (chip->fv_votable)
477 vote(chip->fv_votable, JEITA_VOTER, false, 0);
478 return 0;
479 }
480
481 elapsed_us = ktime_us_delta(ktime_get(), chip->jeita_last_update_time);
482 if (elapsed_us < STEP_CHG_HYSTERISIS_DELAY_US)
483 goto reschedule;
484
485 rc = power_supply_get_property(chip->batt_psy,
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800486 chip->jeita_fcc_config->psy_prop, &pval);
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530487 if (rc < 0) {
488 pr_err("Couldn't read %s property rc=%d\n",
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800489 chip->jeita_fcc_config->prop_name, rc);
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530490 return rc;
491 }
492
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800493 rc = get_val(chip->jeita_fcc_config->fcc_cfg,
494 chip->jeita_fcc_config->hysteresis,
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530495 chip->jeita_fcc_index,
496 pval.intval,
497 &chip->jeita_fcc_index,
498 &fcc_ua);
499 if (rc < 0) {
500 /* remove the vote if no step-based fcc is found */
501 if (chip->fcc_votable)
502 vote(chip->fcc_votable, JEITA_VOTER, false, 0);
503 goto update_time;
504 }
505
506 if (!chip->fcc_votable)
507 chip->fcc_votable = find_votable("FCC");
508 if (!chip->fcc_votable)
509 /* changing FCC is a must */
510 return -EINVAL;
511
512 vote(chip->fcc_votable, JEITA_VOTER, true, fcc_ua);
513
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800514 rc = get_val(chip->jeita_fv_config->fv_cfg,
515 chip->jeita_fv_config->hysteresis,
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530516 chip->jeita_fv_index,
517 pval.intval,
518 &chip->jeita_fv_index,
519 &fv_uv);
520 if (rc < 0) {
521 /* remove the vote if no step-based fcc is found */
522 if (chip->fv_votable)
523 vote(chip->fv_votable, JEITA_VOTER, false, 0);
524 goto update_time;
525 }
526
527 chip->fv_votable = find_votable("FV");
528 if (!chip->fv_votable)
529 goto update_time;
530
531 vote(chip->fv_votable, JEITA_VOTER, true, fv_uv);
532
533 pr_debug("%s = %d FCC = %duA FV = %duV\n",
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800534 chip->jeita_fcc_config->prop_name, pval.intval, fcc_ua, fv_uv);
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530535
536update_time:
537 chip->jeita_last_update_time = ktime_get();
Umang Agrawal271536f2018-03-15 12:55:57 +0530538
539 if (!chip->main_psy)
540 chip->main_psy = power_supply_get_by_name("main");
541 if (chip->main_psy)
542 power_supply_changed(chip->main_psy);
543
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530544 return 0;
545
546reschedule:
547 /* reschedule 1000uS after the remaining time */
548 return (STEP_CHG_HYSTERISIS_DELAY_US - elapsed_us + 1000);
549}
550
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800551static int handle_battery_insertion(struct step_chg_info *chip)
552{
553 int rc;
554 union power_supply_propval pval = {0, };
555
556 rc = power_supply_get_property(chip->batt_psy,
557 POWER_SUPPLY_PROP_PRESENT, &pval);
558 if (rc < 0) {
559 pr_err("Get battery present status failed, rc=%d\n", rc);
560 return rc;
561 }
562
563 if (chip->batt_missing != (!pval.intval)) {
564 chip->batt_missing = !pval.intval;
565 pr_debug("battery %s detected\n",
566 chip->batt_missing ? "removal" : "insertion");
567 if (chip->batt_missing) {
568 chip->step_chg_cfg_valid = false;
569 chip->sw_jeita_cfg_valid = false;
570 chip->get_config_retry_count = 0;
571 } else {
572 /*
573 * Get config for the new inserted battery, delay
574 * to make sure BMS has read out the batt_id.
575 */
576 schedule_delayed_work(&chip->get_config_work,
577 msecs_to_jiffies(WAIT_BATT_ID_READY_MS));
578 }
579 }
580
581 return rc;
582}
583
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530584static void status_change_work(struct work_struct *work)
585{
586 struct step_chg_info *chip = container_of(work,
587 struct step_chg_info, status_change_work.work);
588 int rc = 0;
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530589 int reschedule_us;
590 int reschedule_jeita_work_us = 0;
591 int reschedule_step_work_us = 0;
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530592
593 if (!is_batt_available(chip))
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530594 return;
595
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800596 handle_battery_insertion(chip);
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530597 /* skip elapsed_us debounce for handling battery temperature */
598 rc = handle_jeita(chip);
599 if (rc > 0)
600 reschedule_jeita_work_us = rc;
601 else if (rc < 0)
602 pr_err("Couldn't handle sw jeita rc = %d\n", rc);
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530603
604 rc = handle_step_chg_config(chip);
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530605 if (rc > 0)
606 reschedule_step_work_us = rc;
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530607 if (rc < 0)
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530608 pr_err("Couldn't handle step rc = %d\n", rc);
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530609
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530610 reschedule_us = min(reschedule_jeita_work_us, reschedule_step_work_us);
611 if (reschedule_us == 0)
612 __pm_relax(chip->step_chg_ws);
613 else
614 schedule_delayed_work(&chip->status_change_work,
615 usecs_to_jiffies(reschedule_us));
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530616}
617
618static int step_chg_notifier_call(struct notifier_block *nb,
619 unsigned long ev, void *v)
620{
621 struct power_supply *psy = v;
622 struct step_chg_info *chip = container_of(nb, struct step_chg_info, nb);
623
624 if (ev != PSY_EVENT_PROP_CHANGED)
625 return NOTIFY_OK;
626
627 if ((strcmp(psy->desc->name, "battery") == 0)) {
628 __pm_stay_awake(chip->step_chg_ws);
629 schedule_delayed_work(&chip->status_change_work, 0);
630 }
631
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800632 if ((strcmp(psy->desc->name, "bms") == 0)) {
633 if (chip->bms_psy == NULL)
634 chip->bms_psy = psy;
635 if (!chip->config_is_read)
636 schedule_delayed_work(&chip->get_config_work, 0);
637 }
638
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530639 return NOTIFY_OK;
640}
641
642static int step_chg_register_notifier(struct step_chg_info *chip)
643{
644 int rc;
645
646 chip->nb.notifier_call = step_chg_notifier_call;
647 rc = power_supply_reg_notifier(&chip->nb);
648 if (rc < 0) {
649 pr_err("Couldn't register psy notifier rc = %d\n", rc);
650 return rc;
651 }
652
653 return 0;
654}
655
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800656int qcom_step_chg_init(struct device *dev,
657 bool step_chg_enable, bool sw_jeita_enable)
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530658{
659 int rc;
660 struct step_chg_info *chip;
661
662 if (the_chip) {
663 pr_err("Already initialized\n");
664 return -EINVAL;
665 }
666
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800667 chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530668 if (!chip)
669 return -ENOMEM;
670
671 chip->step_chg_ws = wakeup_source_register("qcom-step-chg");
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800672 if (!chip->step_chg_ws)
673 return -EINVAL;
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530674
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800675 chip->dev = dev;
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530676 chip->step_chg_enable = step_chg_enable;
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530677 chip->sw_jeita_enable = sw_jeita_enable;
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530678 chip->step_index = -EINVAL;
679 chip->jeita_fcc_index = -EINVAL;
680 chip->jeita_fv_index = -EINVAL;
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530681
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800682 chip->step_chg_config = devm_kzalloc(dev,
683 sizeof(struct step_chg_cfg), GFP_KERNEL);
684 if (!chip->step_chg_config)
685 return -ENOMEM;
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530686
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800687 chip->step_chg_config->psy_prop = POWER_SUPPLY_PROP_VOLTAGE_NOW;
688 chip->step_chg_config->prop_name = "VBATT";
689 chip->step_chg_config->hysteresis = 100000;
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530690
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800691 chip->jeita_fcc_config = devm_kzalloc(dev,
692 sizeof(struct jeita_fcc_cfg), GFP_KERNEL);
693 chip->jeita_fv_config = devm_kzalloc(dev,
694 sizeof(struct jeita_fv_cfg), GFP_KERNEL);
695 if (!chip->jeita_fcc_config || !chip->jeita_fv_config)
696 return -ENOMEM;
697
698 chip->jeita_fcc_config->psy_prop = POWER_SUPPLY_PROP_TEMP;
699 chip->jeita_fcc_config->prop_name = "BATT_TEMP";
700 chip->jeita_fcc_config->hysteresis = 10;
701 chip->jeita_fv_config->psy_prop = POWER_SUPPLY_PROP_TEMP;
702 chip->jeita_fv_config->prop_name = "BATT_TEMP";
703 chip->jeita_fv_config->hysteresis = 10;
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530704
705 INIT_DELAYED_WORK(&chip->status_change_work, status_change_work);
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800706 INIT_DELAYED_WORK(&chip->get_config_work, get_config_work);
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530707
708 rc = step_chg_register_notifier(chip);
709 if (rc < 0) {
710 pr_err("Couldn't register psy notifier rc = %d\n", rc);
711 goto release_wakeup_source;
712 }
713
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800714 schedule_delayed_work(&chip->get_config_work,
715 msecs_to_jiffies(GET_CONFIG_DELAY_MS));
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530716
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800717 the_chip = chip;
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530718
719 return 0;
720
721release_wakeup_source:
722 wakeup_source_unregister(chip->step_chg_ws);
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530723 return rc;
724}
725
726void qcom_step_chg_deinit(void)
727{
728 struct step_chg_info *chip = the_chip;
729
730 if (!chip)
731 return;
732
733 cancel_delayed_work_sync(&chip->status_change_work);
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800734 cancel_delayed_work_sync(&chip->get_config_work);
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530735 power_supply_unreg_notifier(&chip->nb);
736 wakeup_source_unregister(chip->step_chg_ws);
737 the_chip = NULL;
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530738}