blob: 3d74f7e4452c9d7d980876150b8198f47fad7ebe [file] [log] [blame]
Subbaraman Narayanamurthy390aa5e2018-01-26 12:49:57 -08001/* 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) "ALG: %s: " fmt, __func__
14
15#include <linux/err.h>
16#include <linux/kernel.h>
17#include <linux/mutex.h>
18#include <linux/power_supply.h>
19#include "fg-alg.h"
20
21#define FULL_SOC_RAW 255
22#define CAPACITY_DELTA_DECIPCT 500
23
24/* Cycle counter APIs */
25
26/**
27 * restore_cycle_count -
28 * @counter: Cycle counter object
29 *
30 * Restores all the counters back from FG/QG during boot
31 *
32 */
33int restore_cycle_count(struct cycle_counter *counter)
34{
35 int rc = 0;
36
37 if (!counter)
38 return -ENODEV;
39
40 mutex_lock(&counter->lock);
41 rc = counter->restore_count(counter->data, counter->count,
42 BUCKET_COUNT);
43 if (rc < 0)
44 pr_err("failed to restore cycle counter rc=%d\n", rc);
45 mutex_unlock(&counter->lock);
46
47 return rc;
48}
49
50/**
51 * clear_cycle_count -
52 * @counter: Cycle counter object
53 *
54 * Clears all the counters stored by FG/QG when a battery is inserted
55 * or the profile is re-loaded.
56 *
57 */
58void clear_cycle_count(struct cycle_counter *counter)
59{
60 int rc = 0, i;
61
62 if (!counter)
63 return;
64
65 mutex_lock(&counter->lock);
66 memset(counter->count, 0, sizeof(counter->count));
67 for (i = 0; i < BUCKET_COUNT; i++) {
68 counter->started[i] = false;
69 counter->last_soc[i] = 0;
70 }
71
72 rc = counter->store_count(counter->data, counter->count, 0,
73 BUCKET_COUNT * 2);
74 if (rc < 0)
75 pr_err("failed to clear cycle counter rc=%d\n", rc);
76
77 mutex_unlock(&counter->lock);
78}
79
80/**
81 * store_cycle_count -
82 * @counter: Cycle counter object
83 * @id: Cycle counter bucket id
84 *
85 * Stores the cycle counter for a bucket in FG/QG.
86 *
87 */
88static int store_cycle_count(struct cycle_counter *counter, int id)
89{
90 int rc = 0;
91 u16 cyc_count;
92
93 if (!counter)
94 return -ENODEV;
95
96 if (id < 0 || (id > BUCKET_COUNT - 1)) {
97 pr_err("Invalid id %d\n", id);
98 return -EINVAL;
99 }
100
101 cyc_count = counter->count[id];
102 cyc_count++;
103
104 rc = counter->store_count(counter->data, &cyc_count, id, 2);
105 if (rc < 0) {
106 pr_err("failed to write cycle_count[%d] rc=%d\n",
107 id, rc);
108 return rc;
109 }
110
111 counter->count[id] = cyc_count;
112 pr_debug("Stored count %d in id %d\n", cyc_count, id);
113
114 return rc;
115}
116
117/**
118 * cycle_count_update -
119 * @counter: Cycle counter object
120 * @batt_soc: Battery State of Charge (SOC)
121 * @charge_status: Charging status from power supply
122 * @charge_done: Indicator for charge termination
123 * @input_present: Indicator for input presence
124 *
125 * Called by FG/QG whenever there is a state change (Charging status, SOC)
126 *
127 */
128void cycle_count_update(struct cycle_counter *counter, int batt_soc,
129 int charge_status, bool charge_done, bool input_present)
130{
131 int rc = 0, id, i, soc_thresh;
132
133 if (!counter)
134 return;
135
136 mutex_lock(&counter->lock);
137
138 /* Find out which id the SOC falls in */
139 id = batt_soc / BUCKET_SOC_PCT;
140
141 if (charge_status == POWER_SUPPLY_STATUS_CHARGING) {
142 if (!counter->started[id] && id != counter->last_bucket) {
143 counter->started[id] = true;
144 counter->last_soc[id] = batt_soc;
145 }
146 } else if (charge_done || !input_present) {
147 for (i = 0; i < BUCKET_COUNT; i++) {
148 soc_thresh = counter->last_soc[i] + BUCKET_SOC_PCT / 2;
149 if (counter->started[i] && batt_soc > soc_thresh) {
150 rc = store_cycle_count(counter, i);
151 if (rc < 0)
152 pr_err("Error in storing cycle_ctr rc: %d\n",
153 rc);
154 counter->last_soc[i] = 0;
155 counter->started[i] = false;
156 counter->last_bucket = i;
157 }
158 }
159 }
160
161 pr_debug("batt_soc: %d id: %d chg_status: %d\n", batt_soc, id,
162 charge_status);
163 mutex_unlock(&counter->lock);
164}
165
166/**
167 * get_cycle_count -
168 * @counter: Cycle counter object
169 *
170 * Returns the cycle counter for a SOC bucket.
171 *
172 */
173int get_cycle_count(struct cycle_counter *counter)
174{
175 int count;
176
177 if (!counter)
178 return 0;
179
180 if ((counter->id <= 0) || (counter->id > BUCKET_COUNT))
181 return -EINVAL;
182
183 mutex_lock(&counter->lock);
184 count = counter->count[counter->id - 1];
185 mutex_unlock(&counter->lock);
186 return count;
187}
188
189/**
190 * cycle_count_init -
191 * @counter: Cycle counter object
192 *
193 * FG/QG have to call this during driver probe to validate the required
194 * parameters after allocating cycle_counter object.
195 *
196 */
197int cycle_count_init(struct cycle_counter *counter)
198{
199 if (!counter)
200 return -ENODEV;
201
202 if (!counter->data || !counter->restore_count ||
203 !counter->store_count) {
204 pr_err("Invalid parameters for using cycle counter\n");
205 return -EINVAL;
206 }
207
208 mutex_init(&counter->lock);
209 counter->last_bucket = -1;
210 return 0;
211}
212
213/* Capacity learning algorithm APIs */
214
215/**
216 * cap_learning_post_process -
217 * @cl: Capacity learning object
218 *
219 * Does post processing on the learnt capacity based on the user specified
220 * or default parameters for the capacity learning algorithm.
221 *
222 */
223static void cap_learning_post_process(struct cap_learning *cl)
224{
225 int64_t max_inc_val, min_dec_val, old_cap;
226 int rc;
227
228 if (cl->dt.skew_decipct) {
229 pr_debug("applying skew %d on current learnt capacity %lld\n",
230 cl->dt.skew_decipct, cl->final_cap_uah);
231 cl->final_cap_uah = cl->final_cap_uah *
232 (1000 + cl->dt.skew_decipct);
233 cl->final_cap_uah = div64_u64(cl->final_cap_uah, 1000);
234 }
235
236 max_inc_val = cl->learned_cap_uah * (1000 + cl->dt.max_cap_inc);
237 max_inc_val = div64_u64(max_inc_val, 1000);
238
239 min_dec_val = cl->learned_cap_uah * (1000 - cl->dt.max_cap_dec);
240 min_dec_val = div64_u64(min_dec_val, 1000);
241
242 old_cap = cl->learned_cap_uah;
243 if (cl->final_cap_uah > max_inc_val)
244 cl->learned_cap_uah = max_inc_val;
245 else if (cl->final_cap_uah < min_dec_val)
246 cl->learned_cap_uah = min_dec_val;
247 else
248 cl->learned_cap_uah = cl->final_cap_uah;
249
250 if (cl->dt.max_cap_limit) {
251 max_inc_val = (int64_t)cl->nom_cap_uah * (1000 +
252 cl->dt.max_cap_limit);
253 max_inc_val = div64_u64(max_inc_val, 1000);
254 if (cl->final_cap_uah > max_inc_val) {
255 pr_debug("learning capacity %lld goes above max limit %lld\n",
256 cl->final_cap_uah, max_inc_val);
257 cl->learned_cap_uah = max_inc_val;
258 }
259 }
260
261 if (cl->dt.min_cap_limit) {
262 min_dec_val = (int64_t)cl->nom_cap_uah * (1000 -
263 cl->dt.min_cap_limit);
264 min_dec_val = div64_u64(min_dec_val, 1000);
265 if (cl->final_cap_uah < min_dec_val) {
266 pr_debug("learning capacity %lld goes below min limit %lld\n",
267 cl->final_cap_uah, min_dec_val);
268 cl->learned_cap_uah = min_dec_val;
269 }
270 }
271
272 if (cl->store_learned_capacity) {
273 rc = cl->store_learned_capacity(cl->data, cl->learned_cap_uah);
274 if (rc < 0)
275 pr_err("Error in storing learned_cap_uah, rc=%d\n", rc);
276 }
277
278 pr_debug("final cap_uah = %lld, learned capacity %lld -> %lld uah\n",
279 cl->final_cap_uah, old_cap, cl->learned_cap_uah);
280}
281
282/**
283 * cap_learning_process_full_data -
284 * @cl: Capacity learning object
285 *
286 * Processes the coulomb counter during charge termination and calculates the
287 * delta w.r.to the coulomb counter obtained earlier when the learning begun.
288 *
289 */
290static int cap_learning_process_full_data(struct cap_learning *cl)
291{
292 int rc, cc_soc_sw, cc_soc_delta_pct;
293 int64_t delta_cap_uah;
294
295 rc = cl->get_cc_soc(cl->data, &cc_soc_sw);
296 if (rc < 0) {
297 pr_err("Error in getting CC_SOC_SW, rc=%d\n", rc);
298 return rc;
299 }
300
301 cc_soc_delta_pct =
302 div64_s64((int64_t)(cc_soc_sw - cl->init_cc_soc_sw) * 100,
303 cl->cc_soc_max);
304
305 /* If the delta is < 50%, then skip processing full data */
306 if (cc_soc_delta_pct < 50) {
307 pr_err("cc_soc_delta_pct: %d\n", cc_soc_delta_pct);
308 return -ERANGE;
309 }
310
311 delta_cap_uah = div64_s64(cl->learned_cap_uah * cc_soc_delta_pct, 100);
312 cl->final_cap_uah = cl->init_cap_uah + delta_cap_uah;
313 pr_debug("Current cc_soc=%d cc_soc_delta_pct=%d total_cap_uah=%lld\n",
314 cc_soc_sw, cc_soc_delta_pct, cl->final_cap_uah);
315 return 0;
316}
317
318/**
319 * cap_learning_begin -
320 * @cl: Capacity learning object
321 * @batt_soc: Battery State of Charge (SOC)
322 *
323 * Gets the coulomb counter from FG/QG when the conditions are suitable for
324 * beginning capacity learning. Also, primes the coulomb counter based on
325 * battery SOC if required.
326 *
327 */
328static int cap_learning_begin(struct cap_learning *cl, u32 batt_soc)
329{
330 int rc, cc_soc_sw, batt_soc_msb;
331
332 batt_soc_msb = batt_soc >> 24;
333 if (DIV_ROUND_CLOSEST(batt_soc_msb * 100, FULL_SOC_RAW) >
334 cl->dt.start_soc) {
335 pr_debug("Battery SOC %d is high!, not starting\n",
336 batt_soc_msb);
337 return -EINVAL;
338 }
339
340 cl->init_cap_uah = div64_s64(cl->learned_cap_uah * batt_soc_msb,
341 FULL_SOC_RAW);
342
343 if (cl->prime_cc_soc) {
344 /*
345 * Prime cc_soc_sw with battery SOC when capacity learning
346 * begins.
347 */
348 rc = cl->prime_cc_soc(cl->data, batt_soc);
349 if (rc < 0) {
350 pr_err("Error in writing cc_soc_sw, rc=%d\n", rc);
351 goto out;
352 }
353 }
354
355 rc = cl->get_cc_soc(cl->data, &cc_soc_sw);
356 if (rc < 0) {
357 pr_err("Error in getting CC_SOC_SW, rc=%d\n", rc);
358 goto out;
359 }
360
361 cl->init_cc_soc_sw = cc_soc_sw;
362 pr_debug("Capacity learning started @ battery SOC %d init_cc_soc_sw:%d\n",
363 batt_soc_msb, cl->init_cc_soc_sw);
364out:
365 return rc;
366}
367
368/**
369 * cap_learning_done -
370 * @cl: Capacity learning object
371 *
372 * Top level function for getting coulomb counter and post processing the
373 * data once the capacity learning is complete after charge termination.
374 *
375 */
376static int cap_learning_done(struct cap_learning *cl)
377{
378 int rc;
379
380 rc = cap_learning_process_full_data(cl);
381 if (rc < 0) {
382 pr_err("Error in processing cap learning full data, rc=%d\n",
383 rc);
384 goto out;
385 }
386
387 if (cl->prime_cc_soc) {
388 /* Write a FULL value to cc_soc_sw */
389 rc = cl->prime_cc_soc(cl->data, cl->cc_soc_max);
390 if (rc < 0) {
391 pr_err("Error in writing cc_soc_sw, rc=%d\n", rc);
392 goto out;
393 }
394 }
395
396 cap_learning_post_process(cl);
397out:
398 return rc;
399}
400
401/**
402 * cap_learning_update -
403 * @cl: Capacity learning object
404 * @batt_temp - Battery temperature
405 * @batt_soc: Battery State of Charge (SOC)
406 * @charge_status: Charging status from power supply
407 * @charge_done: Indicator for charge termination
408 * @input_present: Indicator for input presence
409 * @qnovo_en: Indicator for Qnovo enable status
410 *
411 * Called by FG/QG driver when there is a state change (Charging status, SOC)
412 *
413 */
414void cap_learning_update(struct cap_learning *cl, int batt_temp,
415 int batt_soc, int charge_status, bool charge_done,
416 bool input_present, bool qnovo_en)
417{
418 int rc, batt_soc_msb, batt_soc_prime;
419 bool prime_cc = false;
420
421 if (!cl)
422 return;
423
424 mutex_lock(&cl->lock);
425
426 if (batt_temp > cl->dt.max_temp || batt_temp < cl->dt.min_temp ||
427 !cl->learned_cap_uah) {
428 cl->active = false;
429 cl->init_cap_uah = 0;
430 goto out;
431 }
432
433 batt_soc_msb = (u32)batt_soc >> 24;
434 pr_debug("Charge_status: %d active: %d batt_soc: %d\n",
435 charge_status, cl->active, batt_soc_msb);
436
437 /* Initialize the starting point of learning capacity */
438 if (!cl->active) {
439 if (charge_status == POWER_SUPPLY_STATUS_CHARGING) {
440 rc = cap_learning_begin(cl, batt_soc);
441 cl->active = (rc == 0);
442 } else {
443 if (charge_status == POWER_SUPPLY_STATUS_DISCHARGING ||
444 charge_done)
445 prime_cc = true;
446 }
447 } else {
448 if (charge_done) {
449 rc = cap_learning_done(cl);
450 if (rc < 0)
451 pr_err("Error in completing capacity learning, rc=%d\n",
452 rc);
453
454 cl->active = false;
455 cl->init_cap_uah = 0;
456 }
457
458 if (charge_status == POWER_SUPPLY_STATUS_DISCHARGING) {
459 if (!input_present) {
460 pr_debug("Capacity learning aborted @ battery SOC %d\n",
461 batt_soc_msb);
462 cl->active = false;
463 cl->init_cap_uah = 0;
464 prime_cc = true;
465 }
466 }
467
468 if (charge_status == POWER_SUPPLY_STATUS_NOT_CHARGING) {
469 if (qnovo_en && input_present) {
470 /*
471 * Don't abort the capacity learning when qnovo
472 * is enabled and input is present where the
473 * charging status can go to "not charging"
474 * intermittently.
475 */
476 } else {
477 pr_debug("Capacity learning aborted @ battery SOC %d\n",
478 batt_soc_msb);
479 cl->active = false;
480 cl->init_cap_uah = 0;
481 prime_cc = true;
482 }
483 }
484 }
485
486 /*
487 * Prime CC_SOC_SW when the device is not charging or during charge
488 * termination when the capacity learning is not active.
489 */
490
491 if (prime_cc && cl->prime_cc_soc) {
492 if (charge_done)
493 batt_soc_prime = cl->cc_soc_max;
494 else
495 batt_soc_prime = batt_soc;
496
497 rc = cl->prime_cc_soc(cl->data, batt_soc_prime);
498 if (rc < 0)
499 pr_err("Error in writing cc_soc_sw, rc=%d\n",
500 rc);
501 }
502
503out:
504 mutex_unlock(&cl->lock);
505}
506
507/**
508 * cap_learning_abort -
509 * @cl: Capacity learning object
510 *
511 * Aborts the capacity learning and initializes variables
512 *
513 */
514void cap_learning_abort(struct cap_learning *cl)
515{
516 if (!cl)
517 return;
518
519 mutex_lock(&cl->lock);
520 pr_debug("Aborting cap_learning\n");
521 cl->active = false;
522 cl->init_cap_uah = 0;
523 mutex_lock(&cl->lock);
524}
525
526/**
527 * cap_learning_post_profile_init -
528 * @cl: Capacity learning object
529 * @nom_cap_uah: Nominal capacity of battery in uAh
530 *
531 * Called by FG/QG once the profile load is complete and nominal capacity
532 * of battery is known. This also gets the last learned capacity back from
533 * FG/QG to feed back to the algorithm.
534 *
535 */
536int cap_learning_post_profile_init(struct cap_learning *cl, int64_t nom_cap_uah)
537{
538 int64_t delta_cap_uah, pct_nom_cap_uah;
539 int rc;
540
541 if (!cl || !cl->data)
542 return -EINVAL;
543
544 mutex_lock(&cl->lock);
545 cl->nom_cap_uah = nom_cap_uah;
546 rc = cl->get_learned_capacity(cl->data, &cl->learned_cap_uah);
547 if (rc < 0) {
548 pr_err("Couldn't get learned capacity, rc=%d\n", rc);
549 goto out;
550 }
551
552 if (cl->learned_cap_uah != cl->nom_cap_uah) {
553 if (cl->learned_cap_uah == 0)
554 cl->learned_cap_uah = cl->nom_cap_uah;
555
556 delta_cap_uah = abs(cl->learned_cap_uah - cl->nom_cap_uah);
557 pct_nom_cap_uah = div64_s64((int64_t)cl->nom_cap_uah *
558 CAPACITY_DELTA_DECIPCT, 1000);
559 /*
560 * If the learned capacity is out of range by 50% from the
561 * nominal capacity, then overwrite the learned capacity with
562 * the nominal capacity.
563 */
564 if (cl->nom_cap_uah && delta_cap_uah > pct_nom_cap_uah) {
565 pr_debug("learned_cap_uah: %lld is higher than expected, capping it to nominal: %lld\n",
566 cl->learned_cap_uah, cl->nom_cap_uah);
567 cl->learned_cap_uah = cl->nom_cap_uah;
568 }
569
570 rc = cl->store_learned_capacity(cl->data, cl->learned_cap_uah);
571 if (rc < 0)
572 pr_err("Error in storing learned_cap_uah, rc=%d\n", rc);
573 }
574
575out:
576 mutex_unlock(&cl->lock);
577 return rc;
578}
579
580/**
581 * cap_learning_init -
582 * @cl: Capacity learning object
583 *
584 * FG/QG have to call this during driver probe to validate the required
585 * parameters after allocating cap_learning object.
586 *
587 */
588int cap_learning_init(struct cap_learning *cl)
589{
590 if (!cl)
591 return -ENODEV;
592
593 if (!cl->get_learned_capacity || !cl->store_learned_capacity ||
594 !cl->get_cc_soc) {
595 pr_err("Insufficient functions for supporting capacity learning\n");
596 return -EINVAL;
597 }
598
599 if (!cl->cc_soc_max) {
600 pr_err("Insufficient parameters for supporting capacity learning\n");
601 return -EINVAL;
602 }
603
604 mutex_init(&cl->lock);
605 return 0;
606}