diff options
Diffstat (limited to 'firmware/target/arm/imx31/gigabeat-s/powermgmt-imx31.c')
-rw-r--r-- | firmware/target/arm/imx31/gigabeat-s/powermgmt-imx31.c | 883 |
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! */ | ||
32 | const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT] = | ||
33 | { | ||
34 | 3450 | ||
35 | }; | ||
36 | |||
37 | const unsigned short battery_level_shutoff[BATTERY_TYPES_COUNT] = | ||
38 | { | ||
39 | 3400 | ||
40 | }; | ||
41 | |||
42 | /* voltages (millivolt) of 0%, 10%, ... 100% when charging disabled */ | ||
43 | const 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 */ | ||
50 | const 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 | |||
60 | const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT] = | ||
61 | { | ||
62 | 3450 | ||
63 | }; | ||
64 | |||
65 | const unsigned short battery_level_shutoff[BATTERY_TYPES_COUNT] = | ||
66 | { | ||
67 | 3400 | ||
68 | }; | ||
69 | |||
70 | /* voltages (millivolt) of 0%, 10%, ... 100% when charging disabled */ | ||
71 | const 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 */ | ||
78 | const 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] */ |
85 | unsigned int battery_adc_voltage(void) | 87 | unsigned 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] */ | ||
94 | unsigned int application_supply_adc_voltage(void) | ||
95 | { | ||
96 | return ((adc_read(ADC_APPLICATION_SUPPLY) * 2303) >> 10) + 2400; | ||
97 | } | ||
98 | |||
99 | unsigned 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] */ |
92 | int battery_adc_charge_current(void) | 105 | int 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. */ | ||
121 | unsigned 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] */ |
102 | unsigned int battery_adc_temp(void) | 135 | int 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. */ | ||
170 | static uint32_t int_sense0 = 0; /* Interrupt Sense 0 bits */ | ||
171 | static unsigned int power_status = POWER_INPUT_NONE; /* Detect input changes */ | ||
172 | static int charger_total_timer = 0; /* Total allowed charging time */ | ||
173 | static int icharger_ave = 0; /* Filtered charging current */ | ||
174 | static bool charger_close = false; /* Shutdown notification */ | ||
175 | static bool service_wdt = true; /* Service the watchdog timer, if things | ||
176 | go very wrong, cease and shut down. */ | ||
177 | static 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 */ | ||
180 | static int autorecharge_counter = 0 ; /* Battery < threshold debounce */ | ||
181 | static 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 */ | ||
187 | static 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 */ | ||
196 | static 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 */ | ||
206 | static 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. */ | ||
218 | static 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. */ | ||
228 | static 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. */ | ||
238 | static 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. */ | ||
248 | static 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. */ | ||
289 | static 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. */ | ||
304 | static 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. */ | ||
443 | static 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. */ | ||
454 | static 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 | |||
560 | void 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. */ | ||
595 | int 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. */ | ||
609 | bool 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 */ | ||
623 | int battery_charge_current(void) | ||
624 | { | ||
625 | return icharger_ave / ICHARGER_AVE_SAMPLES; | ||
626 | } | ||
627 | |||
628 | bool 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 | |||
637 | bool 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 | |||
646 | static void charger_plugged(void) | ||
647 | { | ||
648 | adc_enable_channel(ADC_BATTERY_TEMP, true); | ||
649 | autorecharge_counter = -1; | ||
650 | } | ||
651 | |||
652 | static 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 | |||
673 | static 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 | |||
717 | static 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 */ | ||
861 | void 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 | |||
912 | void 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. */ | ||
920 | void 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 | ||
930 | void sys_poweroff(void) | ||
931 | { | ||
126 | } | 932 | } |
933 | #endif /* BOOTLOADER */ | ||