blob: a75cbbbce56f6de9347e060db16b3fb9c5516847 [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;
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +053085 struct delayed_work status_change_work;
Fenglin Wudd8f4cb2017-10-17 11:09:46 +080086 struct delayed_work get_config_work;
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +053087 struct notifier_block nb;
88};
89
90static struct step_chg_info *the_chip;
91
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +053092#define STEP_CHG_HYSTERISIS_DELAY_US 5000000 /* 5 secs */
93
Fenglin Wudd8f4cb2017-10-17 11:09:46 +080094#define BATT_HOT_DECIDEGREE_MAX 600
95#define GET_CONFIG_DELAY_MS 2000
96#define GET_CONFIG_RETRY_COUNT 50
97#define WAIT_BATT_ID_READY_MS 200
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +053098
99static bool is_batt_available(struct step_chg_info *chip)
100{
101 if (!chip->batt_psy)
102 chip->batt_psy = power_supply_get_by_name("battery");
103
104 if (!chip->batt_psy)
105 return false;
106
107 return true;
108}
109
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800110static bool is_bms_available(struct step_chg_info *chip)
111{
112 if (!chip->bms_psy)
113 chip->bms_psy = power_supply_get_by_name("bms");
114
115 if (!chip->bms_psy)
116 return false;
117
118 return true;
119}
120
121static int read_range_data_from_node(struct device_node *node,
122 const char *prop_str, struct range_data *ranges,
123 u32 max_threshold, u32 max_value)
124{
125 int rc = 0, i, length, per_tuple_length, tuples;
126
127 rc = of_property_count_elems_of_size(node, prop_str, sizeof(u32));
128 if (rc < 0) {
129 pr_err("Count %s failed, rc=%d\n", prop_str, rc);
130 return rc;
131 }
132
133 length = rc;
134 per_tuple_length = sizeof(struct range_data) / sizeof(u32);
135 if (length % per_tuple_length) {
136 pr_err("%s length (%d) should be multiple of %d\n",
137 prop_str, length, per_tuple_length);
138 return -EINVAL;
139 }
140 tuples = length / per_tuple_length;
141
142 if (tuples > MAX_STEP_CHG_ENTRIES) {
143 pr_err("too many entries(%d), only %d allowed\n",
144 tuples, MAX_STEP_CHG_ENTRIES);
145 return -EINVAL;
146 }
147
148 rc = of_property_read_u32_array(node, prop_str,
149 (u32 *)ranges, length);
150 if (rc) {
151 pr_err("Read %s failed, rc=%d", prop_str, rc);
152 return rc;
153 }
154
155 for (i = 0; i < tuples; i++) {
156 if (ranges[i].low_threshold >
157 ranges[i].high_threshold) {
158 pr_err("%s thresholds should be in ascendant ranges\n",
159 prop_str);
160 rc = -EINVAL;
161 goto clean;
162 }
163
164 if (i != 0) {
165 if (ranges[i - 1].high_threshold >
166 ranges[i].low_threshold) {
167 pr_err("%s thresholds should be in ascendant ranges\n",
168 prop_str);
169 rc = -EINVAL;
170 goto clean;
171 }
172 }
173
174 if (ranges[i].low_threshold > max_threshold)
175 ranges[i].low_threshold = max_threshold;
176 if (ranges[i].high_threshold > max_threshold)
177 ranges[i].high_threshold = max_threshold;
178 if (ranges[i].value > max_value)
179 ranges[i].value = max_value;
180 }
181
182 return rc;
183clean:
184 memset(ranges, 0, tuples * sizeof(struct range_data));
185 return rc;
186}
187
188static int get_step_chg_jeita_setting_from_profile(struct step_chg_info *chip)
189{
190 struct device_node *batt_node, *profile_node;
191 u32 max_fv_uv, max_fcc_ma;
192 const char *batt_type_str;
193 const __be32 *handle;
194 int batt_id_ohms, rc;
195 union power_supply_propval prop = {0, };
196
197 handle = of_get_property(chip->dev->of_node,
198 "qcom,battery-data", NULL);
199 if (!handle) {
200 pr_debug("ignore getting sw-jeita/step charging settings from profile\n");
201 return 0;
202 }
203
204 batt_node = of_find_node_by_phandle(be32_to_cpup(handle));
205 if (!batt_node) {
206 pr_err("Get battery data node failed\n");
207 return -EINVAL;
208 }
209
210 if (!is_bms_available(chip))
211 return -ENODEV;
212
213 power_supply_get_property(chip->bms_psy,
214 POWER_SUPPLY_PROP_RESISTANCE_ID, &prop);
215 batt_id_ohms = prop.intval;
216
217 /* bms_psy has not yet read the batt_id */
218 if (batt_id_ohms < 0)
219 return -EBUSY;
220
221 profile_node = of_batterydata_get_best_profile(batt_node,
222 batt_id_ohms / 1000, NULL);
223 if (IS_ERR(profile_node))
224 return PTR_ERR(profile_node);
225
226 if (!profile_node) {
227 pr_err("Couldn't find profile\n");
228 return -ENODATA;
229 }
230
231 rc = of_property_read_string(profile_node, "qcom,battery-type",
232 &batt_type_str);
233 if (rc < 0) {
234 pr_err("battery type unavailable, rc:%d\n", rc);
235 return rc;
236 }
237 pr_debug("battery: %s detected, getting sw-jeita/step charging settings\n",
238 batt_type_str);
239
240 rc = of_property_read_u32(profile_node, "qcom,max-voltage-uv",
241 &max_fv_uv);
242 if (rc < 0) {
243 pr_err("max-voltage_uv reading failed, rc=%d\n", rc);
244 return rc;
245 }
246
247 rc = of_property_read_u32(profile_node, "qcom,fastchg-current-ma",
248 &max_fcc_ma);
249 if (rc < 0) {
250 pr_err("max-fastchg-current-ma reading failed, rc=%d\n", rc);
251 return rc;
252 }
253
254 chip->soc_based_step_chg =
255 of_property_read_bool(profile_node, "qcom,soc-based-step-chg");
256 if (chip->soc_based_step_chg) {
257 chip->step_chg_config->psy_prop = POWER_SUPPLY_PROP_CAPACITY,
258 chip->step_chg_config->prop_name = "SOC";
259 chip->step_chg_config->hysteresis = 0;
260 }
261
262 chip->step_chg_cfg_valid = true;
263 rc = read_range_data_from_node(profile_node,
264 "qcom,step-chg-ranges",
265 chip->step_chg_config->fcc_cfg,
266 chip->soc_based_step_chg ? 100 : max_fv_uv,
267 max_fcc_ma * 1000);
268 if (rc < 0) {
269 pr_debug("Read qcom,step-chg-ranges failed from battery profile, rc=%d\n",
270 rc);
271 chip->step_chg_cfg_valid = false;
272 }
273
274 chip->sw_jeita_cfg_valid = true;
275 rc = read_range_data_from_node(profile_node,
276 "qcom,jeita-fcc-ranges",
277 chip->jeita_fcc_config->fcc_cfg,
278 BATT_HOT_DECIDEGREE_MAX, max_fcc_ma * 1000);
279 if (rc < 0) {
280 pr_debug("Read qcom,jeita-fcc-ranges failed from battery profile, rc=%d\n",
281 rc);
282 chip->sw_jeita_cfg_valid = false;
283 }
284
285 rc = read_range_data_from_node(profile_node,
286 "qcom,jeita-fv-ranges",
287 chip->jeita_fv_config->fv_cfg,
288 BATT_HOT_DECIDEGREE_MAX, max_fv_uv);
289 if (rc < 0) {
290 pr_debug("Read qcom,jeita-fv-ranges failed from battery profile, rc=%d\n",
291 rc);
292 chip->sw_jeita_cfg_valid = false;
293 }
294
295 return rc;
296}
297
298static void get_config_work(struct work_struct *work)
299{
300 struct step_chg_info *chip = container_of(work,
301 struct step_chg_info, get_config_work.work);
302 int i, rc;
303
304 chip->config_is_read = false;
305 rc = get_step_chg_jeita_setting_from_profile(chip);
306
307 if (rc < 0) {
308 if (rc == -ENODEV || rc == -EBUSY) {
309 if (chip->get_config_retry_count++
310 < GET_CONFIG_RETRY_COUNT) {
311 pr_debug("bms_psy is not ready, retry: %d\n",
312 chip->get_config_retry_count);
313 goto reschedule;
314 }
315 }
316 }
317
318 chip->config_is_read = true;
319
320 for (i = 0; i < MAX_STEP_CHG_ENTRIES; i++)
321 pr_debug("step-chg-cfg: %duV(SoC) ~ %duV(SoC), %duA\n",
322 chip->step_chg_config->fcc_cfg[i].low_threshold,
323 chip->step_chg_config->fcc_cfg[i].high_threshold,
324 chip->step_chg_config->fcc_cfg[i].value);
325 for (i = 0; i < MAX_STEP_CHG_ENTRIES; i++)
326 pr_debug("jeita-fcc-cfg: %ddecidegree ~ %ddecidegre, %duA\n",
327 chip->jeita_fcc_config->fcc_cfg[i].low_threshold,
328 chip->jeita_fcc_config->fcc_cfg[i].high_threshold,
329 chip->jeita_fcc_config->fcc_cfg[i].value);
330 for (i = 0; i < MAX_STEP_CHG_ENTRIES; i++)
331 pr_debug("jeita-fv-cfg: %ddecidegree ~ %ddecidegre, %duV\n",
332 chip->jeita_fv_config->fv_cfg[i].low_threshold,
333 chip->jeita_fv_config->fv_cfg[i].high_threshold,
334 chip->jeita_fv_config->fv_cfg[i].value);
335
336 return;
337
338reschedule:
339 schedule_delayed_work(&chip->get_config_work,
340 msecs_to_jiffies(GET_CONFIG_DELAY_MS));
341
342}
343
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530344static int get_val(struct range_data *range, int hysteresis, int current_index,
345 int threshold,
346 int *new_index, int *val)
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530347{
348 int i;
349
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530350 *new_index = -EINVAL;
351 /* first find the matching index without hysteresis */
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530352 for (i = 0; i < MAX_STEP_CHG_ENTRIES; i++)
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530353 if (is_between(range[i].low_threshold,
354 range[i].high_threshold, threshold)) {
355 *new_index = i;
356 *val = range[i].value;
357 }
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530358
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530359 /* if nothing was found, return -ENODATA */
360 if (*new_index == -EINVAL)
361 return -ENODATA;
362 /*
363 * If we don't have a current_index return this
364 * newfound value. There is no hysterisis from out of range
365 * to in range transition
366 */
367 if (current_index == -EINVAL)
368 return 0;
369
370 /*
371 * Check for hysteresis if it in the neighbourhood
372 * of our current index.
373 */
374 if (*new_index == current_index + 1) {
375 if (threshold < range[*new_index].low_threshold + hysteresis) {
376 /*
377 * Stay in the current index, threshold is not higher
378 * by hysteresis amount
379 */
380 *new_index = current_index;
381 *val = range[current_index].value;
382 }
383 } else if (*new_index == current_index - 1) {
384 if (threshold > range[*new_index].high_threshold - hysteresis) {
385 /*
386 * stay in the current index, threshold is not lower
387 * by hysteresis amount
388 */
389 *new_index = current_index;
390 *val = range[current_index].value;
391 }
392 }
393 return 0;
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530394}
395
396static int handle_step_chg_config(struct step_chg_info *chip)
397{
398 union power_supply_propval pval = {0, };
399 int rc = 0, fcc_ua = 0;
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530400 u64 elapsed_us;
401
402 elapsed_us = ktime_us_delta(ktime_get(), chip->step_last_update_time);
403 if (elapsed_us < STEP_CHG_HYSTERISIS_DELAY_US)
404 goto reschedule;
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530405
406 rc = power_supply_get_property(chip->batt_psy,
407 POWER_SUPPLY_PROP_STEP_CHARGING_ENABLED, &pval);
408 if (rc < 0)
409 chip->step_chg_enable = 0;
410 else
411 chip->step_chg_enable = pval.intval;
412
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800413 if (!chip->step_chg_enable || !chip->step_chg_cfg_valid) {
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530414 if (chip->fcc_votable)
415 vote(chip->fcc_votable, STEP_CHG_VOTER, false, 0);
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530416 goto update_time;
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530417 }
418
419 rc = power_supply_get_property(chip->batt_psy,
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800420 chip->step_chg_config->psy_prop, &pval);
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530421 if (rc < 0) {
422 pr_err("Couldn't read %s property rc=%d\n",
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800423 chip->step_chg_config->prop_name, rc);
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530424 return rc;
425 }
426
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800427 rc = get_val(chip->step_chg_config->fcc_cfg,
428 chip->step_chg_config->hysteresis,
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530429 chip->step_index,
430 pval.intval,
431 &chip->step_index,
432 &fcc_ua);
433 if (rc < 0) {
434 /* remove the vote if no step-based fcc is found */
435 if (chip->fcc_votable)
436 vote(chip->fcc_votable, STEP_CHG_VOTER, false, 0);
437 goto update_time;
438 }
439
440 if (!chip->fcc_votable)
441 chip->fcc_votable = find_votable("FCC");
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530442 if (!chip->fcc_votable)
443 return -EINVAL;
444
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530445 vote(chip->fcc_votable, STEP_CHG_VOTER, true, fcc_ua);
446
447 pr_debug("%s = %d Step-FCC = %duA\n",
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800448 chip->step_chg_config->prop_name, pval.intval, fcc_ua);
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530449
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530450update_time:
451 chip->step_last_update_time = ktime_get();
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530452 return 0;
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530453
454reschedule:
455 /* reschedule 1000uS after the remaining time */
456 return (STEP_CHG_HYSTERISIS_DELAY_US - elapsed_us + 1000);
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530457}
458
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530459static int handle_jeita(struct step_chg_info *chip)
460{
461 union power_supply_propval pval = {0, };
462 int rc = 0, fcc_ua = 0, fv_uv = 0;
463 u64 elapsed_us;
464
Abhijeet Dharmapurikar3a580042017-07-24 09:43:00 -0700465 rc = power_supply_get_property(chip->batt_psy,
466 POWER_SUPPLY_PROP_SW_JEITA_ENABLED, &pval);
467 if (rc < 0)
468 chip->sw_jeita_enable = 0;
469 else
470 chip->sw_jeita_enable = pval.intval;
471
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800472 if (!chip->sw_jeita_enable || !chip->sw_jeita_cfg_valid) {
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530473 if (chip->fcc_votable)
474 vote(chip->fcc_votable, JEITA_VOTER, false, 0);
475 if (chip->fv_votable)
476 vote(chip->fv_votable, JEITA_VOTER, false, 0);
477 return 0;
478 }
479
480 elapsed_us = ktime_us_delta(ktime_get(), chip->jeita_last_update_time);
481 if (elapsed_us < STEP_CHG_HYSTERISIS_DELAY_US)
482 goto reschedule;
483
484 rc = power_supply_get_property(chip->batt_psy,
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800485 chip->jeita_fcc_config->psy_prop, &pval);
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530486 if (rc < 0) {
487 pr_err("Couldn't read %s property rc=%d\n",
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800488 chip->jeita_fcc_config->prop_name, rc);
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530489 return rc;
490 }
491
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800492 rc = get_val(chip->jeita_fcc_config->fcc_cfg,
493 chip->jeita_fcc_config->hysteresis,
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530494 chip->jeita_fcc_index,
495 pval.intval,
496 &chip->jeita_fcc_index,
497 &fcc_ua);
498 if (rc < 0) {
499 /* remove the vote if no step-based fcc is found */
500 if (chip->fcc_votable)
501 vote(chip->fcc_votable, JEITA_VOTER, false, 0);
502 goto update_time;
503 }
504
505 if (!chip->fcc_votable)
506 chip->fcc_votable = find_votable("FCC");
507 if (!chip->fcc_votable)
508 /* changing FCC is a must */
509 return -EINVAL;
510
511 vote(chip->fcc_votable, JEITA_VOTER, true, fcc_ua);
512
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800513 rc = get_val(chip->jeita_fv_config->fv_cfg,
514 chip->jeita_fv_config->hysteresis,
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530515 chip->jeita_fv_index,
516 pval.intval,
517 &chip->jeita_fv_index,
518 &fv_uv);
519 if (rc < 0) {
520 /* remove the vote if no step-based fcc is found */
521 if (chip->fv_votable)
522 vote(chip->fv_votable, JEITA_VOTER, false, 0);
523 goto update_time;
524 }
525
526 chip->fv_votable = find_votable("FV");
527 if (!chip->fv_votable)
528 goto update_time;
529
530 vote(chip->fv_votable, JEITA_VOTER, true, fv_uv);
531
532 pr_debug("%s = %d FCC = %duA FV = %duV\n",
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800533 chip->jeita_fcc_config->prop_name, pval.intval, fcc_ua, fv_uv);
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530534
535update_time:
536 chip->jeita_last_update_time = ktime_get();
537 return 0;
538
539reschedule:
540 /* reschedule 1000uS after the remaining time */
541 return (STEP_CHG_HYSTERISIS_DELAY_US - elapsed_us + 1000);
542}
543
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800544static int handle_battery_insertion(struct step_chg_info *chip)
545{
546 int rc;
547 union power_supply_propval pval = {0, };
548
549 rc = power_supply_get_property(chip->batt_psy,
550 POWER_SUPPLY_PROP_PRESENT, &pval);
551 if (rc < 0) {
552 pr_err("Get battery present status failed, rc=%d\n", rc);
553 return rc;
554 }
555
556 if (chip->batt_missing != (!pval.intval)) {
557 chip->batt_missing = !pval.intval;
558 pr_debug("battery %s detected\n",
559 chip->batt_missing ? "removal" : "insertion");
560 if (chip->batt_missing) {
561 chip->step_chg_cfg_valid = false;
562 chip->sw_jeita_cfg_valid = false;
563 chip->get_config_retry_count = 0;
564 } else {
565 /*
566 * Get config for the new inserted battery, delay
567 * to make sure BMS has read out the batt_id.
568 */
569 schedule_delayed_work(&chip->get_config_work,
570 msecs_to_jiffies(WAIT_BATT_ID_READY_MS));
571 }
572 }
573
574 return rc;
575}
576
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530577static void status_change_work(struct work_struct *work)
578{
579 struct step_chg_info *chip = container_of(work,
580 struct step_chg_info, status_change_work.work);
581 int rc = 0;
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530582 int reschedule_us;
583 int reschedule_jeita_work_us = 0;
584 int reschedule_step_work_us = 0;
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530585
586 if (!is_batt_available(chip))
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530587 return;
588
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800589 handle_battery_insertion(chip);
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530590 /* skip elapsed_us debounce for handling battery temperature */
591 rc = handle_jeita(chip);
592 if (rc > 0)
593 reschedule_jeita_work_us = rc;
594 else if (rc < 0)
595 pr_err("Couldn't handle sw jeita rc = %d\n", rc);
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530596
597 rc = handle_step_chg_config(chip);
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530598 if (rc > 0)
599 reschedule_step_work_us = rc;
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530600 if (rc < 0)
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530601 pr_err("Couldn't handle step rc = %d\n", rc);
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530602
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530603 reschedule_us = min(reschedule_jeita_work_us, reschedule_step_work_us);
604 if (reschedule_us == 0)
605 __pm_relax(chip->step_chg_ws);
606 else
607 schedule_delayed_work(&chip->status_change_work,
608 usecs_to_jiffies(reschedule_us));
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530609}
610
611static int step_chg_notifier_call(struct notifier_block *nb,
612 unsigned long ev, void *v)
613{
614 struct power_supply *psy = v;
615 struct step_chg_info *chip = container_of(nb, struct step_chg_info, nb);
616
617 if (ev != PSY_EVENT_PROP_CHANGED)
618 return NOTIFY_OK;
619
620 if ((strcmp(psy->desc->name, "battery") == 0)) {
621 __pm_stay_awake(chip->step_chg_ws);
622 schedule_delayed_work(&chip->status_change_work, 0);
623 }
624
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800625 if ((strcmp(psy->desc->name, "bms") == 0)) {
626 if (chip->bms_psy == NULL)
627 chip->bms_psy = psy;
628 if (!chip->config_is_read)
629 schedule_delayed_work(&chip->get_config_work, 0);
630 }
631
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530632 return NOTIFY_OK;
633}
634
635static int step_chg_register_notifier(struct step_chg_info *chip)
636{
637 int rc;
638
639 chip->nb.notifier_call = step_chg_notifier_call;
640 rc = power_supply_reg_notifier(&chip->nb);
641 if (rc < 0) {
642 pr_err("Couldn't register psy notifier rc = %d\n", rc);
643 return rc;
644 }
645
646 return 0;
647}
648
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800649int qcom_step_chg_init(struct device *dev,
650 bool step_chg_enable, bool sw_jeita_enable)
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530651{
652 int rc;
653 struct step_chg_info *chip;
654
655 if (the_chip) {
656 pr_err("Already initialized\n");
657 return -EINVAL;
658 }
659
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800660 chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530661 if (!chip)
662 return -ENOMEM;
663
664 chip->step_chg_ws = wakeup_source_register("qcom-step-chg");
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800665 if (!chip->step_chg_ws)
666 return -EINVAL;
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530667
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800668 chip->dev = dev;
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530669 chip->step_chg_enable = step_chg_enable;
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530670 chip->sw_jeita_enable = sw_jeita_enable;
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530671 chip->step_index = -EINVAL;
672 chip->jeita_fcc_index = -EINVAL;
673 chip->jeita_fv_index = -EINVAL;
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530674
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800675 chip->step_chg_config = devm_kzalloc(dev,
676 sizeof(struct step_chg_cfg), GFP_KERNEL);
677 if (!chip->step_chg_config)
678 return -ENOMEM;
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530679
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800680 chip->step_chg_config->psy_prop = POWER_SUPPLY_PROP_VOLTAGE_NOW;
681 chip->step_chg_config->prop_name = "VBATT";
682 chip->step_chg_config->hysteresis = 100000;
Ashay Jaiswal9aba44a2017-07-20 17:41:36 +0530683
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800684 chip->jeita_fcc_config = devm_kzalloc(dev,
685 sizeof(struct jeita_fcc_cfg), GFP_KERNEL);
686 chip->jeita_fv_config = devm_kzalloc(dev,
687 sizeof(struct jeita_fv_cfg), GFP_KERNEL);
688 if (!chip->jeita_fcc_config || !chip->jeita_fv_config)
689 return -ENOMEM;
690
691 chip->jeita_fcc_config->psy_prop = POWER_SUPPLY_PROP_TEMP;
692 chip->jeita_fcc_config->prop_name = "BATT_TEMP";
693 chip->jeita_fcc_config->hysteresis = 10;
694 chip->jeita_fv_config->psy_prop = POWER_SUPPLY_PROP_TEMP;
695 chip->jeita_fv_config->prop_name = "BATT_TEMP";
696 chip->jeita_fv_config->hysteresis = 10;
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530697
698 INIT_DELAYED_WORK(&chip->status_change_work, status_change_work);
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800699 INIT_DELAYED_WORK(&chip->get_config_work, get_config_work);
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530700
701 rc = step_chg_register_notifier(chip);
702 if (rc < 0) {
703 pr_err("Couldn't register psy notifier rc = %d\n", rc);
704 goto release_wakeup_source;
705 }
706
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800707 schedule_delayed_work(&chip->get_config_work,
708 msecs_to_jiffies(GET_CONFIG_DELAY_MS));
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530709
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800710 the_chip = chip;
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530711
712 return 0;
713
714release_wakeup_source:
715 wakeup_source_unregister(chip->step_chg_ws);
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530716 return rc;
717}
718
719void qcom_step_chg_deinit(void)
720{
721 struct step_chg_info *chip = the_chip;
722
723 if (!chip)
724 return;
725
726 cancel_delayed_work_sync(&chip->status_change_work);
Fenglin Wudd8f4cb2017-10-17 11:09:46 +0800727 cancel_delayed_work_sync(&chip->get_config_work);
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530728 power_supply_unreg_notifier(&chip->nb);
729 wakeup_source_unregister(chip->step_chg_ws);
730 the_chip = NULL;
Anirudh Ghayalfa10fac2017-07-11 09:17:50 +0530731}