summaryrefslogtreecommitdiff
path: root/firmware/target/arm/imx31/gigabeat-s/powermgmt-imx31.c
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2008-12-21 18:10:36 +0000
committerMichael Sevakis <jethead71@rockbox.org>2008-12-21 18:10:36 +0000
commit5667682dd204a07c52f057506fd2eef05bf63f2e (patch)
treea5f4f3cb22751362a9ed7774698ca55d27819d16 /firmware/target/arm/imx31/gigabeat-s/powermgmt-imx31.c
parentc3c15cce88481a2504eb492ba06b6a691d8e998d (diff)
downloadrockbox-5667682dd204a07c52f057506fd2eef05bf63f2e.tar.gz
rockbox-5667682dd204a07c52f057506fd2eef05bf63f2e.zip
Gigabeat S: Implement charging and power control to charge from AC or USB. Hold MENU while plugging USB cable to charge from USB without connecting. Under Windows, plugging USB for charging only but not connecting still needs to be properly handled (driver popup issue) but it will charge when connected normally-- no issue under Linux. Some accomodating changes made to powermgmt.c will soon be made nicer.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@19547 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware/target/arm/imx31/gigabeat-s/powermgmt-imx31.c')
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/powermgmt-imx31.c883
1 files changed, 845 insertions, 38 deletions
diff --git a/firmware/target/arm/imx31/gigabeat-s/powermgmt-imx31.c b/firmware/target/arm/imx31/gigabeat-s/powermgmt-imx31.c
index 796c781f73..c44e7ccdda 100644
--- a/firmware/target/arm/imx31/gigabeat-s/powermgmt-imx31.c
+++ b/firmware/target/arm/imx31/gigabeat-s/powermgmt-imx31.c
@@ -7,8 +7,7 @@
7 * \/ \/ \/ \/ \/ 7 * \/ \/ \/ \/ \/
8 * $Id$ 8 * $Id$
9 * 9 *
10 * Copyright (C) 2002 by Heikki Hannikainen, Uwe Freese 10 * Copyright (c) 2008 by Michael Sevakis
11 * Revisions copyright (C) 2005 by Gerald Van Baren
12 * 11 *
13 * This program is free software; you can redistribute it and/or 12 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License 13 * modify it under the terms of the GNU General Public License
@@ -19,12 +18,40 @@
19 * KIND, either express or implied. 18 * KIND, either express or implied.
20 * 19 *
21 ****************************************************************************/ 20 ****************************************************************************/
22 21#include <stdlib.h>
23/* FIXME: This is just the Gigabeat F/X file with a different name... */
24
25#include "config.h" 22#include "config.h"
23#include "system.h"
24#include "thread.h"
25#include "mc13783.h"
26#include "adc.h" 26#include "adc.h"
27#include "powermgmt.h" 27#include "powermgmt.h"
28#include "power.h"
29#include "power-imx31.h"
30
31/* TODO: Battery tests to get the right values! */
32const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT] =
33{
34 3450
35};
36
37const unsigned short battery_level_shutoff[BATTERY_TYPES_COUNT] =
38{
39 3400
40};
41
42/* voltages (millivolt) of 0%, 10%, ... 100% when charging disabled */
43const unsigned short percent_to_volt_discharge[BATTERY_TYPES_COUNT][11] =
44{
45 /* Toshiba Gigabeat Li Ion 830mAH figured from discharge curve */
46 { 3480, 3550, 3590, 3610, 3630, 3650, 3700, 3760, 3800, 3910, 3990 },
47};
48
49/* voltages (millivolt) of 0%, 10%, ... 100% when charging enabled */
50const unsigned short percent_to_volt_charge[11] =
51{
52 /* Toshiba Gigabeat Li Ion 830mAH */
53 3480, 3550, 3590, 3610, 3630, 3650, 3700, 3760, 3800, 3910, 3990
54};
28 55
29/** 56/**
30 * Fixed-point natural log 57 * Fixed-point natural log
@@ -56,31 +83,6 @@ static long flog(int x)
56 return y; 83 return y;
57} 84}
58 85
59
60const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT] =
61{
62 3450
63};
64
65const unsigned short battery_level_shutoff[BATTERY_TYPES_COUNT] =
66{
67 3400
68};
69
70/* voltages (millivolt) of 0%, 10%, ... 100% when charging disabled */
71const unsigned short percent_to_volt_discharge[BATTERY_TYPES_COUNT][11] =
72{
73 /* Toshiba Gigabeat Li Ion 830mAH figured from discharge curve */
74 { 3480, 3550, 3590, 3610, 3630, 3650, 3700, 3760, 3800, 3910, 3990 },
75};
76
77/* voltages (millivolt) of 0%, 10%, ... 100% when charging enabled */
78const unsigned short percent_to_volt_charge[11] =
79{
80 /* Toshiba Gigabeat Li Ion 830mAH */
81 3480, 3550, 3590, 3610, 3630, 3650, 3700, 3760, 3800, 3910, 3990
82};
83
84/* Returns battery voltage from ADC [millivolts] */ 86/* Returns battery voltage from ADC [millivolts] */
85unsigned int battery_adc_voltage(void) 87unsigned int battery_adc_voltage(void)
86{ 88{
@@ -88,6 +90,17 @@ unsigned int battery_adc_voltage(void)
88 return ((adc_read(ADC_BATTERY) * 2303) >> 10) + 2400; 90 return ((adc_read(ADC_BATTERY) * 2303) >> 10) + 2400;
89} 91}
90 92
93/* Returns the application supply voltage from ADC [millvolts] */
94unsigned int application_supply_adc_voltage(void)
95{
96 return ((adc_read(ADC_APPLICATION_SUPPLY) * 2303) >> 10) + 2400;
97}
98
99unsigned int chrgraw_adc_voltage(void)
100{
101 return (adc_read(ADC_CHARGER_VOLTAGE) * 23023) >> 10;
102}
103
91/* Returns battery charge current from ADC [milliamps] */ 104/* Returns battery charge current from ADC [milliamps] */
92int battery_adc_charge_current(void) 105int battery_adc_charge_current(void)
93{ 106{
@@ -95,11 +108,31 @@ int battery_adc_charge_current(void)
95 * Negative reading = battery to charger terminal 108 * Negative reading = battery to charger terminal
96 * ADC reading -512-511 = -2875mA-2875mA */ 109 * ADC reading -512-511 = -2875mA-2875mA */
97 unsigned int value = adc_read(ADC_CHARGER_CURRENT); 110 unsigned int value = adc_read(ADC_CHARGER_CURRENT);
98 return (((int)value << 22) >> 22) * 2881 >> 9; 111 int I;
112
113 if (value == ADC_READ_ERROR)
114 return INT_MIN;
115
116 I = ((((int32_t)value << 22) >> 22) * 2881) >> 9;
117 return ILEVEL_ADJUST_IN(I);
118}
119
120/* Estimate power dissipation in the charge path regulator in mW. */
121unsigned int cccv_regulator_dissipation(void)
122{
123 /* BATTISNS is shorted to BATT so we don't need to use the
124 * battery current reading. */
125 int chrgraw = (adc_read(ADC_CHARGER_VOLTAGE) * 230225) >> 10;
126 int batt = ((adc_read(ADC_BATTERY) * 23023) >> 10) + 24000;
127 int ichrgsn = adc_read(ADC_CHARGER_CURRENT);
128 ichrgsn = ((((int32_t)ichrgsn << 22) >> 22) * 2881) >> 9;
129 ichrgsn = abs(ichrgsn);
130
131 return (chrgraw - ichrgsn - batt)*ILEVEL_ADJUST_IN(ichrgsn) / 10000;
99} 132}
100 133
101/* Returns battery temperature from ADC [deg-C] */ 134/* Returns battery temperature from ADC [deg-C] */
102unsigned int battery_adc_temp(void) 135int battery_adc_temp(void)
103{ 136{
104 unsigned int value = adc_read(ADC_BATTERY_TEMP); 137 unsigned int value = adc_read(ADC_BATTERY_TEMP);
105 /* E[volts] = value * 2.3V / 1023 138 /* E[volts] = value * 2.3V / 1023
@@ -117,10 +150,784 @@ unsigned int battery_adc_temp(void)
117 * Fixed-point output matches the floating-point version for each ADC 150 * Fixed-point output matches the floating-point version for each ADC
118 * value. 151 * value.
119 */ 152 */
120 int R = 2070000 * value; 153 if (value > 0)
121 long long ln = flog(R) + 83196; 154 {
122 long long t0 = 425890304133ll; 155 int R = 2070000 * value;
123 long long t1 = 1000000*ln; 156 long long ln = flog(R) + 83196;
124 long long t3 = ln*ln*ln / 13418057; 157 long long t0 = 425890304133ll;
125 return ((32754211579494400ll / (t0 + t1 + t3)) - 27315) / 100; 158 long long t1 = 1000000*ln;
159 long long t3 = ln*ln*ln / 13418057;
160 return ((32754211579494400ll / (t0 + t1 + t3)) - 27315) / 100;
161 }
162
163 return INT_MIN;
164}
165
166/** Charger control **/
167
168/* All code has a preference for the main charger being connected over
169 * USB. USB is considered in the algorithm only if it is the sole source. */
170static uint32_t int_sense0 = 0; /* Interrupt Sense 0 bits */
171static unsigned int power_status = POWER_INPUT_NONE; /* Detect input changes */
172static int charger_total_timer = 0; /* Total allowed charging time */
173static int icharger_ave = 0; /* Filtered charging current */
174static bool charger_close = false; /* Shutdown notification */
175static bool service_wdt = true; /* Service the watchdog timer, if things
176 go very wrong, cease and shut down. */
177static uint32_t charger_setting = 0; /* Current ICHRG and VCHRG regulator
178 * setting (register bits) */
179#define CHARGER_ADJUST ((uint32_t)-1)/* Force change in regulator setting */
180static int autorecharge_counter = 0 ; /* Battery < threshold debounce */
181static int chgcurr_timer = 0; /* Countdown to CHGCURR error */
182#define AUTORECHARGE_COUNTDOWN (10*2) /* 10s debounce */
183#define WATCHDOG_TIMEOUT (10*2) /* If not serviced, poweroff in 10s */
184#define CHGCURR_TIMEOUT (2*2) /* 2s debounce */
185
186/* Temperature monitoring */
187static enum
188{
189 TEMP_STATE_NORMAL = 0, /* Within range */
190 TEMP_STATE_WAIT = 1, /* Went out of range, wait to come back */
191 TEMP_LOW_LIMIT = 0, /* Min temp */
192 TEMP_HIGH_LIMIT = 1, /* Max temp */
193} temp_state = TEMP_STATE_NORMAL;
194
195/* Set power thread priority for charging mode or not */
196static inline void charging_set_thread_priority(bool charging)
197{
198#ifdef HAVE_PRIORITY_SCHEDULING
199 thread_set_priority(THREAD_ID_CURRENT,
200 charging ? PRIORITY_REALTIME : PRIORITY_SYSTEM);
201#endif
202 (void)charging;
203}
204
205/* Update filtered charger current - exponential moving average */
206static bool charger_current_filter_step(void)
207{
208 int value = battery_adc_charge_current();
209
210 if (value == ADC_READ_ERROR)
211 return false;
212
213 icharger_ave += value - (icharger_ave / ICHARGER_AVE_SAMPLES);
214 return true;
215}
216
217/* Return true if the main charger is connected. */
218static bool main_charger_connected(void)
219{
220 return (power_status &
221 POWER_INPUT_MAIN_CHARGER &
222 POWER_INPUT_CHARGER) != 0;
223}
224
225/* Return the voltage level which should automatically trigger
226 * another recharge cycle based upon which power source is available.
227 * Assumes at least one is. */
228static unsigned int auto_recharge_voltage(void)
229{
230 if (main_charger_connected())
231 return BATT_VAUTO_RECHARGE;
232 else
233 return BATT_USB_VAUTO_RECHARGE;
234}
235
236#ifndef NO_LOW_BATTERY_SHUTDOWN
237/* Return greater of supply (BP) or filtered battery voltage. */
238static unsigned int input_millivolts(void)
239{
240 unsigned int app_millivolts = application_supply_adc_voltage();
241 unsigned int bat_millivolts = battery_voltage();
242
243 return MAX(app_millivolts, bat_millivolts);
244}
245#endif
246
247/* Get smoothed readings for initializing filtered data. */
248static int stat_battery_reading(int type)
249{
250 int high = INT_MIN, low = INT_MAX;
251 int value = 0;
252 int i;
253
254 for (i = 0; i < 7; i++)
255 {
256 int reading = ADC_READ_ERROR;
257
258 sleep(2); /* Get unique readings */
259
260 switch (type)
261 {
262 case ADC_BATTERY:
263 reading = battery_adc_voltage();
264 break;
265
266 case ADC_CHARGER_CURRENT:
267 reading = battery_adc_charge_current();
268 break;
269 }
270
271 if (reading == ADC_READ_ERROR)
272 return INT_MIN;
273
274 if (reading > high)
275 high = reading;
276
277 if (reading < low)
278 low = reading;
279
280 value += reading;
281 }
282
283 /* Discard extremes */
284 return (value - high - low) / 5;
285}
286
287/* Update filtered battery voltage instead of waiting for filter
288 * decay. */
289static bool update_filtered_battery_voltage(void)
290{
291 int millivolts = stat_battery_reading(ADC_BATTERY);
292
293 if (millivolts != INT_MIN)
294 {
295 set_filtered_battery_voltage(millivolts);
296 return true;
297 }
298
299 return false;
300}
301
302/* Sets the charge current limit based upon state. charge_state should be
303 * set before calling. */
304static bool adjust_charger_current(void)
305{
306 static const uint8_t charger_bits[][2] =
307 {
308 [DISCHARGING] =
309 {
310 /* These are actually zeros but reflect this setting */
311 MC13783_ICHRG_0MA | MC13783_VCHRG_4_050V,
312 MC13783_ICHRG_0MA | MC13783_VCHRG_4_050V,
313 },
314 /* Main(+USB): Charge slowly from the adapter until voltage is
315 * sufficient for normal charging.
316 *
317 * USB: The truth is that things will probably not make it this far.
318 * Cover the case, just in case the disk isn't used and it is
319 * manageable. */
320 [TRICKLE] =
321 {
322 BATTERY_ITRICKLE | BATTERY_VCHARGING,
323 BATTERY_ITRICKLE_USB | BATTERY_VCHARGING
324 },
325 [TOPOFF] =
326 {
327 BATTERY_IFAST | BATTERY_VCHARGING,
328 BATTERY_IFAST_USB | BATTERY_VCHARGING
329 },
330 [CHARGING] =
331 {
332 BATTERY_IFAST | BATTERY_VCHARGING,
333 BATTERY_IFAST_USB | BATTERY_VCHARGING
334 },
335 /* Must maintain battery when on USB power only - utterly nasty
336 * but true and something retailos does (it will even end up charging
337 * the battery but not reporting that it is doing so).
338 * Float lower than MAX - could end up slightly discharging after
339 * a full charge but this is safer than maxing it out. */
340 [CHARGING+1] =
341 {
342 BATTERY_IFLOAT_USB | BATTERY_VFLOAT_USB,
343 BATTERY_IMAINTAIN_USB | BATTERY_VMAINTAIN_USB
344 },
345#if 0
346 /* Slower settings to so that the linear regulator doesn't dissipate
347 * an excessive amount of power when coming out of precharge state. */
348 [CHARGING+2] =
349 {
350 BATTERY_ISLOW | BATTERY_VCHARGING,
351 BATTERY_ISLOW_USB | BATTEYR_VCHARGING
352 },
353#endif
354 };
355
356 bool success = false;
357 int usb_select;
358 uint32_t i;
359
360 usb_select = ((power_status & POWER_INPUT) == POWER_INPUT_USB)
361 ? 1 : 0;
362
363 if (charge_state == DISCHARGING && usb_select == 1)
364 {
365 /* USB-only, DISCHARGING, = maintaining battery */
366 int select = (power_status & POWER_INPUT_CHARGER) ? 0 : 1;
367 charger_setting = charger_bits[CHARGING+1][select];
368 }
369 else
370 {
371 /* Take very good care not to write garbage. */
372 int state = charge_state;
373
374 if (state < DISCHARGING || state > CHARGING)
375 state = DISCHARGING;
376
377 charger_setting = charger_bits[state][usb_select];
378 }
379
380 if (charger_setting != 0)
381 {
382 charging_set_thread_priority(true);
383
384 /* Turn regulator logically ON. Hardware may still override. */
385 i = mc13783_write_masked(MC13783_CHARGER,
386 charger_setting | MC13783_CHRGRAWPDEN,
387 MC13783_ICHRG | MC13783_VCHRG |
388 MC13783_CHRGRAWPDEN);
389
390 if (i != MC13783_DATA_ERROR)
391 {
392 int icharger;
393
394 /* Enable charge current conversion */
395 adc_enable_channel(ADC_CHARGER_CURRENT, true);
396
397 /* Charge path regulator turn on takes ~100ms max. */
398 sleep(HZ/10);
399
400 icharger = stat_battery_reading(ADC_CHARGER_CURRENT);
401
402 if (icharger != INT_MIN)
403 {
404 icharger_ave = icharger * ICHARGER_AVE_SAMPLES;
405
406 if (update_filtered_battery_voltage())
407 return true;
408 }
409 }
410
411 /* Force regulator OFF. */
412 charge_state = CHARGE_STATE_ERROR;
413 }
414
415 /* Turn regulator OFF. */
416 icharger_ave = 0;
417 i = mc13783_write_masked(MC13783_CHARGER, charger_bits[0][0],
418 MC13783_ICHRG | MC13783_VCHRG |
419 MC13783_CHRGRAWPDEN);
420
421 if (MC13783_DATA_ERROR == i)
422 {
423 /* Failed. Force poweroff by not servicing the watchdog. */
424 service_wdt = false;
425 }
426 else if (0 == charger_setting)
427 {
428 /* Here because OFF was requested state */
429 success = true;
430 }
431
432 charger_setting = 0;
433
434 adc_enable_channel(ADC_CHARGER_CURRENT, false);
435 update_filtered_battery_voltage();
436 charging_set_thread_priority(false);
437
438 return success;
439}
440
441/* Stop the charger - if USB only then the regulator will not really be
442 * turned off. ERROR or DISABLED will turn it off however. */
443static void stop_charger(void)
444{
445 charger_total_timer = 0;
446
447 if (charge_state > DISCHARGING)
448 charge_state = DISCHARGING;
449
450 adjust_charger_current();
451}
452
453/* Return OK if it is acceptable to start the regulator. */
454static bool charging_ok(void)
455{
456 bool ok = charge_state >= DISCHARGING; /* Not an error condition? */
457
458 if (ok)
459 {
460 /* Is the battery even connected? */
461 ok = (power_status & POWER_INPUT_BATTERY) != 0;
462 }
463
464 if (ok)
465 {
466 /* No tolerance for any over/under temp - wait for it to
467 * come back into safe range. */
468 static const signed char temp_ranges[2][2] =
469 {
470 { 0, 45 }, /* Temperature range before beginning charging */
471 { 5, 40 }, /* Temperature range after out-of-range detected */
472 };
473
474 int temp = battery_adc_temp();
475 const signed char *range = temp_ranges[temp_state];
476
477 ok = temp >= range[TEMP_LOW_LIMIT] &&
478 temp <= range[TEMP_HIGH_LIMIT];
479
480 switch (temp_state)
481 {
482 case TEMP_STATE_NORMAL:
483 if (!ok)
484 temp_state = TEMP_STATE_WAIT;
485 break;
486
487 case TEMP_STATE_WAIT:
488 if (ok)
489 temp_state = TEMP_STATE_NORMAL;
490 break;
491
492 default:
493 break;
494 }
495 }
496
497 if (ok)
498 {
499 /* Any events that should stop the regulator? */
500
501 /* Overvoltage at CHRGRAW? */
502 ok = (int_sense0 & MC13783_CHGOVS) == 0;
503
504 if (ok)
505 {
506 /* CHGCURR sensed? */
507 ok = (int_sense0 & MC13783_CHGCURRS) != 0;
508
509 if (!ok)
510 {
511 /* Debounce transient states */
512 if (chgcurr_timer > 0)
513 {
514 chgcurr_timer--;
515 ok = true;
516 }
517 }
518 else
519 {
520 chgcurr_timer = CHGCURR_TIMEOUT;
521 }
522 }
523
524 /* Charger may need to be reinserted */
525 if (!ok)
526 charge_state = CHARGE_STATE_ERROR;
527 }
528
529 if (charger_setting != 0)
530 {
531 if (ok)
532 {
533 /* Watch to not overheat FET (nothing should go over about 1012.7mW).
534 * Trying a higher voltage AC adapter can work (up to 6.90V) but
535 * we'll just reject that. Reducing current for adapters that bring
536 * CHRGRAW to > 4.900V is another possible action. */
537 ok = cccv_regulator_dissipation() < 1150;
538 if (!ok)
539 charge_state = CHARGE_STATE_ERROR;
540 }
541
542 if (!ok)
543 {
544 int state = charge_state;
545
546 if (state > DISCHARGING)
547 state = DISCHARGING;
548
549 /* Force off for all states including maintaining the battery level
550 * on USB. */
551 charge_state = CHARGE_STATE_ERROR;
552 stop_charger();
553 charge_state = state;
554 }
555 }
556
557 return ok;
558}
559
560void powermgmt_init_target(void)
561{
562#ifdef IMX31_ALLOW_CHARGING
563 const uint32_t regval_w =
564 MC13783_VCHRG_4_050V | MC13783_ICHRG_0MA |
565 MC13783_ICHRGTR_0MA | MC13783_OVCTRL_6_90V;
566
567 /* Use watchdog to shut system down if we lose control of the charging
568 * hardware. */
569 watchdog_init(WATCHDOG_TIMEOUT);
570
571 mc13783_write(MC13783_CHARGER, regval_w);
572
573 if (mc13783_read(MC13783_CHARGER) == regval_w)
574 {
575 /* Divide CHRGRAW input by 10 */
576 mc13783_clear(MC13783_ADC0, MC13783_CHRGRAWDIV);
577 /* Turn off BATTDETB. It's worthless on MESx0V since the battery
578 * isn't removable (nor the thermistor). */
579 mc13783_clear(MC13783_POWER_CONTROL0, MC13783_BATTDETEN);
580 }
581 else
582 {
583 /* Register has the wrong value - set error condition and disable
584 * since something is wrong. */
585 charge_state = CHARGE_STATE_DISABLED;
586 stop_charger();
587 }
588#else
589 /* Disable charger use */
590 charge_state = CHARGE_STATE_DISABLED;
591#endif
592}
593
594/* Returns CHARGING or DISCHARGING since that's all we really do. */
595int powermgmt_filter_charge_state(void)
596{
597 switch(charge_state)
598 {
599 case TRICKLE:
600 case TOPOFF:
601 case CHARGING:
602 return CHARGING;
603 default:
604 return DISCHARGING;
605 }
606}
607
608/* Returns true if the unit is charging the batteries. */
609bool charging_state(void)
610{
611 switch (charge_state)
612 {
613 case TRICKLE:
614 case TOPOFF:
615 case CHARGING:
616 return true;
617 default:
618 return false;
619 }
620}
621
622/* Filtered battery charge current */
623int battery_charge_current(void)
624{
625 return icharger_ave / ICHARGER_AVE_SAMPLES;
626}
627
628bool query_force_shutdown(void)
629{
630#ifndef NO_LOW_BATTERY_SHUTDOWN
631 return input_millivolts() < battery_level_shutoff[0];
632#else
633 return false;
634#endif
635}
636
637bool battery_level_safe(void)
638{
639#ifndef NO_LOW_BATTERY_SHUTDOWN
640 return input_millivolts() > battery_level_dangerous[0];
641#else
642 return true;
643#endif
644}
645
646static void charger_plugged(void)
647{
648 adc_enable_channel(ADC_BATTERY_TEMP, true);
649 autorecharge_counter = -1;
650}
651
652static void charger_unplugged(void)
653{
654 /* Charger pulled - turn off current sources (though hardware
655 * will have done that anyway). */
656 if (charge_state > CHARGE_STATE_DISABLED)
657 {
658 /* Reset state and clear any error. If disabled, the charger
659 * will not have been started or will have been stopped already. */
660 stop_charger();
661 charge_state = DISCHARGING;
662 }
663
664 /* Might need to reevaluate these bits in charger_none. */
665 power_status &= ~(POWER_INPUT | POWER_INPUT_CHARGER);
666 temp_state = TEMP_STATE_NORMAL;
667 autorecharge_counter = 0;
668 chgcurr_timer = 0;
669
670 adc_enable_channel(ADC_BATTERY_TEMP, false);
671}
672
673static void charger_none(void)
674{
675 unsigned int pwr = power_input_status();
676
677 if (power_status != pwr)
678 {
679 /* If battery switch state changed, reset filter. */
680 if ((power_status ^ pwr) & POWER_INPUT_BATTERY)
681 update_filtered_battery_voltage();
682
683 power_status = pwr;
684
685 if (charge_state == CHARGE_STATE_DISABLED)
686 return;
687
688 if ((pwr & (POWER_INPUT | POWER_INPUT_CHARGER)) == POWER_INPUT_USB)
689 {
690 /* USB connected but not configured. Maintain battery to the
691 * greatest degree possible. It probably won't be enough but the
692 * discharge won't be so severe. */
693 charger_plugged();
694 charger_setting = CHARGER_ADJUST;
695 }
696 else
697 {
698 charger_unplugged();
699 power_status = pwr; /* Restore status */
700 }
701 }
702 else if (charger_setting != 0)
703 {
704 /* Maintaining - keep filter going and check charge state */
705 int_sense0 = mc13783_read(MC13783_INTERRUPT_SENSE0);
706
707 if (!charger_current_filter_step())
708 {
709 /* Failed to read current */
710 charge_state = CHARGE_STATE_ERROR;
711 }
712
713 charging_ok();
714 }
715}
716
717static void charger_control(void)
718{
719 unsigned int pwr = power_input_status();
720
721 if (power_status != pwr)
722 {
723 unsigned int changed = power_status ^ pwr;
724
725 power_status = pwr;
726
727 /* If battery switch state changed, reset filter. */
728 if (changed & POWER_INPUT_BATTERY)
729 update_filtered_battery_voltage();
730
731 if (charger_setting != 0)
732 charger_setting = CHARGER_ADJUST;
733
734 if (charge_state == DISCHARGING)
735 {
736 if (main_charger_connected())
737 {
738 /* If main is connected, ignore USB plugs. */
739 if (changed & POWER_INPUT_MAIN_CHARGER)
740 {
741 /* Main charger plugged - try charge */
742 autorecharge_counter = -1;
743 }
744 }
745 else if (pwr & POWER_INPUT_USB_CHARGER
746 & POWER_INPUT_CHARGER)
747 {
748 if (changed & POWER_INPUT_USB_CHARGER)
749 {
750 /* USB charger plugged - try charge */
751 autorecharge_counter = -1;
752 }
753 }
754 }
755 }
756
757 if (charger_setting != 0 && !charger_current_filter_step())
758 {
759 /* Failed to read current */
760 charge_state = CHARGE_STATE_ERROR;
761 }
762
763 int_sense0 = mc13783_read(MC13783_INTERRUPT_SENSE0);
764
765 if (!charging_ok())
766 return;
767
768 switch (charge_state)
769 {
770 case DISCHARGING:
771 {
772 /* Battery voltage may have dropped and a charge cycle should
773 * start again. Debounced. */
774 if (autorecharge_counter < 0)
775 {
776 /* Try starting a cycle now regardless of battery level to
777 * allow user to ensure the battery is topped off. It
778 * will soon turn off if already full. */
779 autorecharge_counter = 0;
780 }
781 else if (battery_voltage() > auto_recharge_voltage())
782 {
783 /* Still above threshold - reset counter */
784 autorecharge_counter = AUTORECHARGE_COUNTDOWN;
785 break;
786 }
787 else if (autorecharge_counter > 0)
788 {
789 /* Coundown to restart */
790 autorecharge_counter--;
791 break;
792 }
793
794 charging_set_thread_priority(true);
795
796 if (stat_battery_reading(ADC_BATTERY) < BATT_VTRICKLE_CHARGE)
797 {
798 /* Battery is deeply discharged - precharge at lower current. */
799 charge_state = TRICKLE;
800 }
801 else
802 {
803 /* Ok for fast charge */
804 charge_state = CHARGING;
805 }
806
807 charger_setting = CHARGER_ADJUST;
808 charger_total_timer = CHARGER_TOTAL_TIMER*60*2;
809 break;
810 } /* DISCHARGING: */
811
812 case TRICKLE: /* Very low - precharge */
813 {
814 if (battery_voltage() <= BATT_VTRICKLE_CHARGE)
815 break;
816
817 /* Switch to normal charge mode. */
818 charge_state = CHARGING;
819 charger_setting = CHARGER_ADJUST;
820 break;
821 } /* TRICKLE: */
822
823 case CHARGING: /* Constant-current stage */
824 case TOPOFF: /* Constant-voltage stage */
825 {
826 /* Reg. mode is more informative than an operational necessity. */
827 charge_state = (int_sense0 & MC13783_CCCVS) ? TOPOFF : CHARGING;
828
829 if (main_charger_connected())
830 {
831 /* Monitor and stop if current drops below threshold. */
832 if (battery_charge_current() > BATTERY_ICHARGE_COMPLETE)
833 break;
834 }
835 else
836 {
837 /* Accurate I-level can't be determined since device also
838 * powers through the I sense. This simply stops the reporting
839 * of charging but the regulator remains on. */
840 if (battery_voltage() <= BATT_USB_VSTOP)
841 break;
842 }
843
844 stop_charger();
845 break;
846 } /* CHARGING: TOPOFF: */
847
848 default:
849 break;
850 } /* switch */
851
852 /* Check if charger timer expired and stop it if so. */
853 if (charger_total_timer > 0 && --charger_total_timer == 0)
854 {
855 charge_state = CHARGE_STATE_ERROR;
856 stop_charger(); /* Time ran out - error */
857 }
858}
859
860/* Main charging algorithm - called from powermgmt.c */
861void charging_algorithm_small_step(void)
862{
863 if (service_wdt)
864 watchdog_service();
865
866 /* Switch by input state */
867 switch (charger_input_state)
868 {
869 case NO_CHARGER:
870 charger_none();
871 break;
872
873 case CHARGER_PLUGGED:
874 charger_plugged();
875 break;
876
877 case CHARGER:
878 charger_control();
879 break;
880
881 case CHARGER_UNPLUGGED:
882 charger_unplugged();
883 break;
884 } /* switch */
885
886 if (charger_close)
887 {
888 if (charge_state != CHARGE_STATE_DISABLED)
889 {
890 /* Disable starts while shutting down */
891 charge_state = CHARGE_STATE_DISABLED;
892 stop_charger();
893 }
894
895 charger_close = false;
896 return;
897 }
898
899 if (charger_setting != 0)
900 {
901 if ((mc13783_read(MC13783_CHARGER) & (MC13783_ICHRG | MC13783_VCHRG)) !=
902 charger_setting)
903 {
904 /* The hardware setting doesn't match. It could have turned the
905 * charger off in a race of plugging/unplugging or the setting
906 * was changed in one of the calls. */
907 adjust_charger_current();
908 }
909 }
910}
911
912void charging_algorithm_big_step(void)
913{
914 /* Sleep for one minute */
915 power_thread_sleep(HZ*60);
916}
917
918/* Disable the charger and prepare for poweroff - called off-thread so we
919 * signal the charging thread to prepare to quit. */
920void charging_algorithm_close(void)
921{
922 charger_close = true;
923
924 /* Power management thread will set it false again */
925 while (charger_close)
926 sleep(HZ/10);
927}
928
929#ifdef BOOTLOADER
930void sys_poweroff(void)
931{
126} 932}
933#endif /* BOOTLOADER */