diff options
author | Michael Sevakis <jethead71@rockbox.org> | 2010-04-09 01:21:53 +0000 |
---|---|---|
committer | Michael Sevakis <jethead71@rockbox.org> | 2010-04-09 01:21:53 +0000 |
commit | 7abf2b53a462612808d46d6d77a7f35261a0e5a3 (patch) | |
tree | 241304f7cd2b5d1c2a9e091fe56a33d2d2f8e816 /firmware/target/arm/imx31/gigabeat-s/powermgmt-gigabeat-s.c | |
parent | 43304b87b0662d1619ac60e5297a1694aa580310 (diff) | |
download | rockbox-7abf2b53a462612808d46d6d77a7f35261a0e5a3.tar.gz rockbox-7abf2b53a462612808d46d6d77a7f35261a0e5a3.zip |
Gigabeat S/i.MX31: Sort files in the /target tree into things that are SoC-generic (into /imx31) and player-specific (into /gigabeat-s, based upon current appearances). Move i2s clock init into the appropriate file. Housekeeping only-- no functional changes.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@25547 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware/target/arm/imx31/gigabeat-s/powermgmt-gigabeat-s.c')
-rw-r--r-- | firmware/target/arm/imx31/gigabeat-s/powermgmt-gigabeat-s.c | 940 |
1 files changed, 940 insertions, 0 deletions
diff --git a/firmware/target/arm/imx31/gigabeat-s/powermgmt-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/powermgmt-gigabeat-s.c new file mode 100644 index 0000000000..34abf04940 --- /dev/null +++ b/firmware/target/arm/imx31/gigabeat-s/powermgmt-gigabeat-s.c | |||
@@ -0,0 +1,940 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (c) 2008 by Michael Sevakis | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #include <stdlib.h> | ||
22 | #include "config.h" | ||
23 | #include "system.h" | ||
24 | #include "thread.h" | ||
25 | #include "mc13783.h" | ||
26 | #include "adc.h" | ||
27 | #include "powermgmt.h" | ||
28 | #include "power.h" | ||
29 | #include "power-gigabeat-s.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 | }; | ||
55 | |||
56 | /* Returns battery voltage from ADC [millivolts] */ | ||
57 | unsigned int battery_adc_voltage(void) | ||
58 | { | ||
59 | /* ADC reading 0-1023 = 2400mV-4700mV */ | ||
60 | return ((adc_read(ADC_BATTERY) * 2303) >> 10) + 2400; | ||
61 | } | ||
62 | |||
63 | /* Returns the application supply voltage from ADC [millvolts] */ | ||
64 | unsigned int application_supply_adc_voltage(void) | ||
65 | { | ||
66 | return ((adc_read(ADC_APPLICATION_SUPPLY) * 2303) >> 10) + 2400; | ||
67 | } | ||
68 | |||
69 | unsigned int chrgraw_adc_voltage(void) | ||
70 | { | ||
71 | return (adc_read(ADC_CHARGER_VOLTAGE) * 23023) >> 10; | ||
72 | } | ||
73 | |||
74 | /* Returns battery charge current from ADC [milliamps] */ | ||
75 | int battery_adc_charge_current(void) | ||
76 | { | ||
77 | /* Positive reading = charger to battery | ||
78 | * Negative reading = battery to charger terminal | ||
79 | * ADC reading -512-511 = -2875mA-2875mA */ | ||
80 | unsigned int value = adc_read(ADC_CHARGER_CURRENT); | ||
81 | int I; | ||
82 | |||
83 | if (value == ADC_READ_ERROR) | ||
84 | return INT_MIN; | ||
85 | |||
86 | I = ((((int32_t)value << 22) >> 22) * 2881) >> 9; | ||
87 | return ILEVEL_ADJUST_IN(I); | ||
88 | } | ||
89 | |||
90 | /* Estimate power dissipation in the charge path regulator in mW. */ | ||
91 | unsigned int cccv_regulator_dissipation(void) | ||
92 | { | ||
93 | /* BATTISNS is shorted to BATT so we don't need to use the | ||
94 | * battery current reading. */ | ||
95 | int chrgraw = (adc_read(ADC_CHARGER_VOLTAGE) * 230225) >> 10; | ||
96 | int batt = ((adc_read(ADC_BATTERY) * 23023) >> 10) + 24000; | ||
97 | int ichrgsn = adc_read(ADC_CHARGER_CURRENT); | ||
98 | ichrgsn = ((((int32_t)ichrgsn << 22) >> 22) * 2881) >> 9; | ||
99 | ichrgsn = abs(ichrgsn); | ||
100 | |||
101 | return (chrgraw - ichrgsn - batt)*ILEVEL_ADJUST_IN(ichrgsn) / 10000; | ||
102 | } | ||
103 | |||
104 | /* Returns battery temperature from ADC [deg-C] */ | ||
105 | int battery_adc_temp(void) | ||
106 | { | ||
107 | /* E[volts] = value * 2.3V / 1023 | ||
108 | * R[ohms] = E/I = E[volts] / 0.00002[A] (Thermistor bias current source) | ||
109 | * | ||
110 | * Steinhart-Hart thermistor equation: | ||
111 | * [A + B*ln(R) + D*ln^3(R)] = 1 / T[°K] | ||
112 | * | ||
113 | * Coeffients that fit experimental data (one thermistor so far, one run): | ||
114 | * A = 0.0013002631685462800 | ||
115 | * B = 0.0002000841932612330 | ||
116 | * D = 0.0000000640446750919 | ||
117 | */ | ||
118 | static const unsigned short ntc_table[] = | ||
119 | { | ||
120 | #if 0 /* These have degree deltas > 1 (except the final two) so leave them | ||
121 | * out. 70 deg C upper limit is quite sufficient. */ | ||
122 | 0, /* INF */ 1, /* 171 */ 2, /* 145 */ 3, /* 130 */ | ||
123 | 4, /* 121 */ 5, /* 114 */ 6, /* 108 */ 7, /* 104 */ | ||
124 | 8, /* 100 */ 9, /* 96 */ 10, /* 93 */ 11, /* 91 */ | ||
125 | 12, /* 88 */ 13, /* 86 */ 14, /* 84 */ 15, /* 82 */ | ||
126 | 16, /* 81 */ 17, /* 79 */ 18, /* 78 */ 19, /* 76 */ | ||
127 | 20, /* 75 */ 21, /* 74 */ 22, /* 72 */ 23, /* 71 */ | ||
128 | #endif | ||
129 | 24, /* 70 */ 25, /* 69 */ 26, /* 68 */ 27, /* 67 */ | ||
130 | 28, /* 66 */ 30, /* 65 */ 31, /* 64 */ 32, /* 63 */ | ||
131 | 33, /* 62 */ 35, /* 61 */ 36, /* 60 */ 38, /* 59 */ | ||
132 | 39, /* 58 */ 41, /* 57 */ 43, /* 56 */ 45, /* 55 */ | ||
133 | 47, /* 54 */ 49, /* 53 */ 51, /* 52 */ 53, /* 51 */ | ||
134 | 56, /* 50 */ 58, /* 49 */ 61, /* 48 */ 63, /* 47 */ | ||
135 | 66, /* 46 */ 69, /* 45 */ 73, /* 44 */ 76, /* 43 */ | ||
136 | 80, /* 42 */ 83, /* 41 */ 87, /* 40 */ 92, /* 39 */ | ||
137 | 96, /* 38 */ 101, /* 37 */ 106, /* 36 */ 111, /* 35 */ | ||
138 | 116, /* 34 */ 122, /* 33 */ 128, /* 32 */ 135, /* 31 */ | ||
139 | 142, /* 30 */ 149, /* 29 */ 156, /* 28 */ 164, /* 27 */ | ||
140 | 173, /* 26 */ 182, /* 25 */ 192, /* 24 */ 202, /* 23 */ | ||
141 | 212, /* 22 */ 224, /* 21 */ 236, /* 20 */ 249, /* 19 */ | ||
142 | 262, /* 18 */ 277, /* 17 */ 292, /* 16 */ 308, /* 15 */ | ||
143 | 325, /* 14 */ 344, /* 13 */ 363, /* 12 */ 384, /* 11 */ | ||
144 | 406, /* 10 */ 429, /* 9 */ 454, /* 8 */ 480, /* 7 */ | ||
145 | 509, /* 6 */ 539, /* 5 */ 571, /* 4 */ 605, /* 3 */ | ||
146 | 641, /* 2 */ 680, /* 1 */ 722, /* 0 */ 766, /* -1 */ | ||
147 | 813, /* -2 */ 864, /* -3 */ 918, /* -4 */ 976, /* -5 */ | ||
148 | }; | ||
149 | |||
150 | unsigned int value = adc_read(ADC_BATTERY_TEMP); | ||
151 | |||
152 | if (value > 0) | ||
153 | { | ||
154 | unsigned i; | ||
155 | |||
156 | for (i = 1; i < ARRAYLEN(ntc_table); i++) | ||
157 | { | ||
158 | if (ntc_table[i] > value) | ||
159 | break; | ||
160 | } | ||
161 | |||
162 | return 71 - i; | ||
163 | } | ||
164 | |||
165 | return INT_MIN; | ||
166 | } | ||
167 | |||
168 | /** Charger control **/ | ||
169 | |||
170 | /* All code has a preference for the main charger being connected over | ||
171 | * USB. USB is considered in the algorithm only if it is the sole source. */ | ||
172 | static uint32_t int_sense0 = 0; /* Interrupt Sense 0 bits */ | ||
173 | static unsigned int last_inputs = POWER_INPUT_NONE; /* Detect input changes */ | ||
174 | static int charger_total_timer = 0; /* Total allowed charging time */ | ||
175 | static int icharger_ave = 0; /* Filtered charging current */ | ||
176 | static bool charger_close = false; /* Shutdown notification */ | ||
177 | static bool service_wdt = true; /* Service the watchdog timer, if things | ||
178 | go very wrong, cease and shut down. */ | ||
179 | static uint32_t charger_setting = 0; /* Current ICHRG and VCHRG regulator | ||
180 | * setting (register bits) */ | ||
181 | #define CHARGER_ADJUST ((uint32_t)-1)/* Force change in regulator setting */ | ||
182 | static int autorecharge_counter = 0 ; /* Battery < threshold debounce */ | ||
183 | static int chgcurr_timer = 0; /* Countdown to CHGCURR error */ | ||
184 | #define AUTORECHARGE_COUNTDOWN (10*2) /* 10s debounce */ | ||
185 | #define WATCHDOG_TIMEOUT (10*2) /* If not serviced, poweroff in 10s */ | ||
186 | #define CHGCURR_TIMEOUT (4*2) /* 4s debounce */ | ||
187 | |||
188 | /* Temperature monitoring */ | ||
189 | static enum | ||
190 | { | ||
191 | TEMP_STATE_NORMAL = 0, /* Within range */ | ||
192 | TEMP_STATE_WAIT = 1, /* Went out of range, wait to come back */ | ||
193 | TEMP_LOW_LIMIT = 0, /* Min temp */ | ||
194 | TEMP_HIGH_LIMIT = 1, /* Max temp */ | ||
195 | } temp_state = TEMP_STATE_NORMAL; | ||
196 | |||
197 | /* Set power thread priority for charging mode or not */ | ||
198 | static inline void charging_set_thread_priority(bool charging) | ||
199 | { | ||
200 | #ifdef HAVE_PRIORITY_SCHEDULING | ||
201 | thread_set_priority(THREAD_ID_CURRENT, | ||
202 | charging ? PRIORITY_REALTIME : PRIORITY_SYSTEM); | ||
203 | #endif | ||
204 | (void)charging; | ||
205 | } | ||
206 | |||
207 | /* Update filtered charger current - exponential moving average */ | ||
208 | static bool charger_current_filter_step(void) | ||
209 | { | ||
210 | int value = battery_adc_charge_current(); | ||
211 | |||
212 | if (value == ADC_READ_ERROR) | ||
213 | return false; | ||
214 | |||
215 | icharger_ave += value - (icharger_ave / ICHARGER_AVE_SAMPLES); | ||
216 | return true; | ||
217 | } | ||
218 | |||
219 | /* Return true if the main charger is connected. */ | ||
220 | static bool main_charger_connected(void) | ||
221 | { | ||
222 | return (last_inputs & | ||
223 | POWER_INPUT_MAIN_CHARGER & | ||
224 | POWER_INPUT_CHARGER) != 0; | ||
225 | } | ||
226 | |||
227 | /* Return the voltage level which should automatically trigger | ||
228 | * another recharge cycle based upon which power source is available. | ||
229 | * Assumes at least one is. */ | ||
230 | static unsigned int auto_recharge_voltage(void) | ||
231 | { | ||
232 | if (main_charger_connected()) | ||
233 | return BATT_VAUTO_RECHARGE; | ||
234 | else | ||
235 | return BATT_USB_VAUTO_RECHARGE; | ||
236 | } | ||
237 | |||
238 | /* Return greater of supply (BP) or filtered battery voltage. */ | ||
239 | unsigned int input_millivolts(void) | ||
240 | { | ||
241 | unsigned int app_millivolts = application_supply_adc_voltage(); | ||
242 | unsigned int bat_millivolts = battery_voltage(); | ||
243 | |||
244 | return MAX(app_millivolts, bat_millivolts); | ||
245 | } | ||
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 | reset_battery_filter(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 = ((last_inputs & 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 = (last_inputs & 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 | if ((charger_setting & MC13783_VCHRG) > BATTERY_VCHARGING || | ||
383 | (charger_setting & MC13783_ICHRG) > BATTERY_IFAST) | ||
384 | { | ||
385 | /* Table is corrupted somehow. We shouldn't run at all. | ||
386 | * | ||
387 | * Explanation: On two occasions, even though this driver monitors | ||
388 | * the regulator register settings on each step and | ||
389 | * ensures that only valid table indexes are used, | ||
390 | * the current and voltage seem to be misregulated, | ||
391 | * resulting in excessively high battery voltage that | ||
392 | * will trip the battery protection. After careful | ||
393 | * review it seems that two possibilities exist: | ||
394 | * This code or data got trashed at some point or | ||
395 | * there really is a hardware bug of some sort. So | ||
396 | * far the cause is unknown. Voltage is also | ||
397 | * monitored in the CHARGING case for that reason. | ||
398 | * The solution for data or code corruption is to | ||
399 | * just panic and refuse to run the device. The | ||
400 | * solution for overvoltage due to hardware bug is to | ||
401 | * disable the charging. The action taken will reveal | ||
402 | * the true cause, thus _who_ is responsible. | ||
403 | * "Burning lithium is baaaad", so sayeth The Council | ||
404 | * of Seven Ascended Masters. */ | ||
405 | charge_state = CHARGE_STATE_DISABLED; | ||
406 | service_wdt = false; | ||
407 | } | ||
408 | else | ||
409 | { | ||
410 | /* Turn on 5K pulldown. */ | ||
411 | i = mc13783_set(MC13783_CHARGER, MC13783_CHRGRAWPDEN); | ||
412 | |||
413 | if (i != MC13783_DATA_ERROR) | ||
414 | { | ||
415 | charging_set_thread_priority(true); | ||
416 | |||
417 | /* Turn regulator logically ON. Hardware may still override. | ||
418 | */ | ||
419 | i = mc13783_write_masked(MC13783_CHARGER, charger_setting, | ||
420 | MC13783_ICHRG | MC13783_VCHRG); | ||
421 | |||
422 | if (i != MC13783_DATA_ERROR) | ||
423 | { | ||
424 | int icharger; | ||
425 | |||
426 | /* Enable charge current conversion */ | ||
427 | adc_enable_channel(ADC_CHARGER_CURRENT, true); | ||
428 | |||
429 | /* Charge path regulator turn on takes ~100ms max. */ | ||
430 | sleep(HZ/10); | ||
431 | |||
432 | icharger = stat_battery_reading(ADC_CHARGER_CURRENT); | ||
433 | |||
434 | if (icharger != INT_MIN) | ||
435 | { | ||
436 | icharger_ave = icharger * ICHARGER_AVE_SAMPLES; | ||
437 | |||
438 | if (update_filtered_battery_voltage()) | ||
439 | return true; | ||
440 | } | ||
441 | } | ||
442 | } | ||
443 | |||
444 | /* Force regulator OFF. */ | ||
445 | charge_state = CHARGE_STATE_ERROR; | ||
446 | } | ||
447 | } | ||
448 | |||
449 | /* Turn regulator OFF. */ | ||
450 | icharger_ave = 0; | ||
451 | i = mc13783_write_masked(MC13783_CHARGER, | ||
452 | MC13783_ICHRG_0MA | MC13783_VCHRG_4_050V, | ||
453 | MC13783_ICHRG | MC13783_VCHRG | | ||
454 | MC13783_CHRGRAWPDEN); | ||
455 | |||
456 | if (MC13783_DATA_ERROR == i) | ||
457 | { | ||
458 | /* Failed. Force poweroff by not servicing the watchdog. */ | ||
459 | service_wdt = false; | ||
460 | } | ||
461 | else if (0 == charger_setting) | ||
462 | { | ||
463 | /* Here because OFF was requested state */ | ||
464 | success = true; | ||
465 | } | ||
466 | |||
467 | charger_setting = 0; | ||
468 | |||
469 | adc_enable_channel(ADC_CHARGER_CURRENT, false); | ||
470 | update_filtered_battery_voltage(); | ||
471 | charging_set_thread_priority(false); | ||
472 | |||
473 | return success; | ||
474 | } | ||
475 | |||
476 | /* Stop the charger - if USB only then the regulator will not really be | ||
477 | * turned off. ERROR or DISABLED will turn it off however. */ | ||
478 | static void stop_charger(void) | ||
479 | { | ||
480 | charger_total_timer = 0; | ||
481 | |||
482 | if (charge_state > DISCHARGING) | ||
483 | charge_state = DISCHARGING; | ||
484 | |||
485 | adjust_charger_current(); | ||
486 | } | ||
487 | |||
488 | /* Return OK if it is acceptable to start the regulator. */ | ||
489 | static bool charging_ok(void) | ||
490 | { | ||
491 | bool ok = charge_state >= DISCHARGING; /* Not an error condition? */ | ||
492 | |||
493 | if (ok) | ||
494 | { | ||
495 | /* Is the battery even connected? */ | ||
496 | ok = (last_inputs & POWER_INPUT_BATTERY) != 0; | ||
497 | } | ||
498 | |||
499 | if (ok) | ||
500 | { | ||
501 | /* No tolerance for any over/under temp - wait for it to | ||
502 | * come back into safe range. */ | ||
503 | static const signed char temp_ranges[2][2] = | ||
504 | { | ||
505 | /* Temperature range before beginning charging */ | ||
506 | { BATTERY_CHARGE_MIN, | ||
507 | BATTERY_CHARGE_MAX }, | ||
508 | /* Temperature range after out-of-range detected - | ||
509 | charging will self-resume */ | ||
510 | { BATTERY_CHARGE_RESTART_MIN, | ||
511 | BATTERY_CHARGE_RESTART_MAX }, | ||
512 | }; | ||
513 | |||
514 | int temp = battery_adc_temp(); | ||
515 | const signed char *range = temp_ranges[temp_state]; | ||
516 | |||
517 | ok = temp >= range[TEMP_LOW_LIMIT] && | ||
518 | temp <= range[TEMP_HIGH_LIMIT]; | ||
519 | |||
520 | switch (temp_state) | ||
521 | { | ||
522 | case TEMP_STATE_NORMAL: | ||
523 | if (!ok) | ||
524 | temp_state = TEMP_STATE_WAIT; | ||
525 | break; | ||
526 | |||
527 | case TEMP_STATE_WAIT: | ||
528 | if (ok) | ||
529 | temp_state = TEMP_STATE_NORMAL; | ||
530 | break; | ||
531 | |||
532 | default: | ||
533 | break; | ||
534 | } | ||
535 | } | ||
536 | |||
537 | if (ok) | ||
538 | { | ||
539 | /* Any events that should stop the regulator? */ | ||
540 | |||
541 | /* Overvoltage at CHRGRAW? */ | ||
542 | ok = (int_sense0 & MC13783_CHGOVS) == 0; | ||
543 | |||
544 | if (ok) | ||
545 | { | ||
546 | /* CHGCURR sensed? */ | ||
547 | ok = (int_sense0 & MC13783_CHGCURRS) != 0; | ||
548 | |||
549 | if (!ok) | ||
550 | { | ||
551 | /* Debounce transient states */ | ||
552 | if (chgcurr_timer > 0) | ||
553 | { | ||
554 | chgcurr_timer--; | ||
555 | ok = true; | ||
556 | } | ||
557 | } | ||
558 | else | ||
559 | { | ||
560 | chgcurr_timer = CHGCURR_TIMEOUT; | ||
561 | } | ||
562 | } | ||
563 | |||
564 | /* Charger may need to be reinserted */ | ||
565 | if (!ok) | ||
566 | charge_state = CHARGE_STATE_ERROR; | ||
567 | } | ||
568 | |||
569 | if (charger_setting != 0) | ||
570 | { | ||
571 | if (ok) | ||
572 | { | ||
573 | /* Protect against any conceivable overcharge/voltage condition | ||
574 | * before hardware protection must intervene. Disable charger | ||
575 | * until reboot. */ | ||
576 | ok = battery_voltage() < BATT_TOO_HIGH; | ||
577 | if (!ok) | ||
578 | charge_state = CHARGE_STATE_DISABLED; | ||
579 | } | ||
580 | |||
581 | if (ok) | ||
582 | { | ||
583 | /* Watch to not overheat FET (nothing should go over about 1012.7mW). | ||
584 | * Trying a higher voltage AC adapter can work (up to 6.90V) but | ||
585 | * we'll just reject that. Reducing current for adapters that bring | ||
586 | * CHRGRAW to > 4.900V is another possible action. */ | ||
587 | ok = cccv_regulator_dissipation() < 1150; | ||
588 | if (!ok) | ||
589 | charge_state = CHARGE_STATE_ERROR; | ||
590 | } | ||
591 | |||
592 | if (!ok) | ||
593 | { | ||
594 | int state = charge_state; | ||
595 | |||
596 | if (state > DISCHARGING) | ||
597 | state = DISCHARGING; | ||
598 | |||
599 | /* Force off for all states including maintaining the battery level | ||
600 | * on USB. */ | ||
601 | charge_state = CHARGE_STATE_ERROR; | ||
602 | stop_charger(); | ||
603 | charge_state = state; | ||
604 | } | ||
605 | } | ||
606 | |||
607 | return ok; | ||
608 | } | ||
609 | |||
610 | void powermgmt_init_target(void) | ||
611 | { | ||
612 | #ifdef IMX31_ALLOW_CHARGING | ||
613 | const uint32_t regval_w = | ||
614 | MC13783_VCHRG_4_050V | MC13783_ICHRG_0MA | | ||
615 | MC13783_ICHRGTR_0MA | MC13783_OVCTRL_6_90V; | ||
616 | |||
617 | /* Use watchdog to shut system down if we lose control of the charging | ||
618 | * hardware. */ | ||
619 | watchdog_init(WATCHDOG_TIMEOUT); | ||
620 | |||
621 | mc13783_write(MC13783_CHARGER, regval_w); | ||
622 | |||
623 | if (mc13783_read(MC13783_CHARGER) == regval_w) | ||
624 | { | ||
625 | /* Divide CHRGRAW input by 10 */ | ||
626 | mc13783_clear(MC13783_ADC0, MC13783_CHRGRAWDIV); | ||
627 | /* Turn off BATTDETB. It's worthless on MESx0V since the battery | ||
628 | * isn't removable (nor the thermistor). */ | ||
629 | mc13783_clear(MC13783_POWER_CONTROL0, MC13783_BATTDETEN); | ||
630 | } | ||
631 | else | ||
632 | { | ||
633 | /* Register has the wrong value - set error condition and disable | ||
634 | * since something is wrong. */ | ||
635 | charge_state = CHARGE_STATE_DISABLED; | ||
636 | stop_charger(); | ||
637 | } | ||
638 | #else | ||
639 | /* Disable charger use */ | ||
640 | charge_state = CHARGE_STATE_DISABLED; | ||
641 | #endif | ||
642 | } | ||
643 | |||
644 | /* Returns true if the unit is charging the batteries. */ | ||
645 | bool charging_state(void) | ||
646 | { | ||
647 | switch (charge_state) | ||
648 | { | ||
649 | case TRICKLE: | ||
650 | case TOPOFF: | ||
651 | case CHARGING: | ||
652 | return true; | ||
653 | default: | ||
654 | return false; | ||
655 | } | ||
656 | } | ||
657 | |||
658 | /* Filtered battery charge current */ | ||
659 | int battery_charge_current(void) | ||
660 | { | ||
661 | return icharger_ave / ICHARGER_AVE_SAMPLES; | ||
662 | } | ||
663 | |||
664 | static void charger_plugged(void) | ||
665 | { | ||
666 | adc_enable_channel(ADC_BATTERY_TEMP, true); | ||
667 | autorecharge_counter = -1; | ||
668 | } | ||
669 | |||
670 | static void charger_unplugged(void) | ||
671 | { | ||
672 | /* Charger pulled - turn off current sources (though hardware | ||
673 | * will have done that anyway). */ | ||
674 | if (charge_state > CHARGE_STATE_DISABLED) | ||
675 | { | ||
676 | /* Reset state and clear any error. If disabled, the charger | ||
677 | * will not have been started or will have been stopped already. */ | ||
678 | stop_charger(); | ||
679 | charge_state = DISCHARGING; | ||
680 | } | ||
681 | |||
682 | /* Might need to reevaluate these bits in charger_none. */ | ||
683 | last_inputs &= ~(POWER_INPUT | POWER_INPUT_CHARGER); | ||
684 | temp_state = TEMP_STATE_NORMAL; | ||
685 | autorecharge_counter = 0; | ||
686 | chgcurr_timer = 0; | ||
687 | |||
688 | adc_enable_channel(ADC_BATTERY_TEMP, false); | ||
689 | } | ||
690 | |||
691 | static void charger_none(void) | ||
692 | { | ||
693 | unsigned int pwr = power_thread_inputs; | ||
694 | |||
695 | if (last_inputs != pwr) | ||
696 | { | ||
697 | last_inputs = pwr; | ||
698 | |||
699 | if (charge_state == CHARGE_STATE_DISABLED) | ||
700 | return; | ||
701 | |||
702 | if ((pwr & (POWER_INPUT | POWER_INPUT_CHARGER)) == POWER_INPUT_USB) | ||
703 | { | ||
704 | /* USB connected but not configured. Maintain battery to the | ||
705 | * greatest degree possible. It probably won't be enough but the | ||
706 | * discharge won't be so severe. */ | ||
707 | charger_plugged(); | ||
708 | charger_setting = CHARGER_ADJUST; | ||
709 | } | ||
710 | else | ||
711 | { | ||
712 | charger_unplugged(); | ||
713 | last_inputs = pwr; /* Restore status */ | ||
714 | } | ||
715 | } | ||
716 | else if (charger_setting != 0) | ||
717 | { | ||
718 | /* Maintaining - keep filter going and check charge state */ | ||
719 | int_sense0 = mc13783_read(MC13783_INTERRUPT_SENSE0); | ||
720 | |||
721 | if (!charger_current_filter_step()) | ||
722 | { | ||
723 | /* Failed to read current */ | ||
724 | charge_state = CHARGE_STATE_ERROR; | ||
725 | } | ||
726 | |||
727 | charging_ok(); | ||
728 | } | ||
729 | } | ||
730 | |||
731 | static void charger_control(void) | ||
732 | { | ||
733 | unsigned int pwr = power_thread_inputs; | ||
734 | |||
735 | if (last_inputs != pwr) | ||
736 | { | ||
737 | unsigned int changed = last_inputs ^ pwr; | ||
738 | |||
739 | last_inputs = pwr; | ||
740 | |||
741 | if (charger_setting != 0) | ||
742 | charger_setting = CHARGER_ADJUST; | ||
743 | |||
744 | if (charge_state == DISCHARGING) | ||
745 | { | ||
746 | if (main_charger_connected()) | ||
747 | { | ||
748 | /* If main is connected, ignore USB plugs. */ | ||
749 | if (changed & POWER_INPUT_MAIN_CHARGER) | ||
750 | { | ||
751 | /* Main charger plugged - try charge */ | ||
752 | autorecharge_counter = -1; | ||
753 | } | ||
754 | } | ||
755 | else if (pwr & POWER_INPUT_USB_CHARGER | ||
756 | & POWER_INPUT_CHARGER) | ||
757 | { | ||
758 | /* USB power only */ | ||
759 | if (changed & POWER_INPUT_USB_CHARGER) | ||
760 | { | ||
761 | /* USB charger plugged - try charge */ | ||
762 | autorecharge_counter = -1; | ||
763 | } | ||
764 | else if (changed & POWER_INPUT_MAIN_CHARGER) | ||
765 | { | ||
766 | /* Main charger pulled - go to battery maintenence. */ | ||
767 | charger_setting = CHARGER_ADJUST; | ||
768 | } | ||
769 | } | ||
770 | } | ||
771 | } | ||
772 | |||
773 | if (charger_setting != 0 && !charger_current_filter_step()) | ||
774 | { | ||
775 | /* Failed to read current */ | ||
776 | charge_state = CHARGE_STATE_ERROR; | ||
777 | } | ||
778 | |||
779 | int_sense0 = mc13783_read(MC13783_INTERRUPT_SENSE0); | ||
780 | |||
781 | if (!charging_ok()) | ||
782 | return; | ||
783 | |||
784 | switch (charge_state) | ||
785 | { | ||
786 | case DISCHARGING: | ||
787 | { | ||
788 | /* Battery voltage may have dropped and a charge cycle should | ||
789 | * start again. Debounced. */ | ||
790 | if (autorecharge_counter < 0 && | ||
791 | battery_adc_voltage() < BATT_FULL_VOLTAGE) | ||
792 | { | ||
793 | /* Try starting a cycle now if battery isn't already topped | ||
794 | * off to allow user to ensure the battery is full. */ | ||
795 | } | ||
796 | else if (battery_voltage() > auto_recharge_voltage()) | ||
797 | { | ||
798 | /* Still above threshold - reset counter */ | ||
799 | autorecharge_counter = AUTORECHARGE_COUNTDOWN; | ||
800 | break; | ||
801 | } | ||
802 | else if (autorecharge_counter > 0) | ||
803 | { | ||
804 | /* Coundown to restart */ | ||
805 | autorecharge_counter--; | ||
806 | break; | ||
807 | } | ||
808 | |||
809 | autorecharge_counter = 0; | ||
810 | |||
811 | charging_set_thread_priority(true); | ||
812 | |||
813 | if (stat_battery_reading(ADC_BATTERY) < BATT_VTRICKLE_CHARGE) | ||
814 | { | ||
815 | /* Battery is deeply discharged - precharge at lower current. */ | ||
816 | charge_state = TRICKLE; | ||
817 | } | ||
818 | else | ||
819 | { | ||
820 | /* Ok for fast charge */ | ||
821 | charge_state = CHARGING; | ||
822 | } | ||
823 | |||
824 | charger_setting = CHARGER_ADJUST; | ||
825 | charger_total_timer = CHARGER_TOTAL_TIMER*60*2; | ||
826 | break; | ||
827 | } /* DISCHARGING: */ | ||
828 | |||
829 | case TRICKLE: /* Very low - precharge */ | ||
830 | { | ||
831 | if (battery_voltage() <= BATT_VTRICKLE_CHARGE) | ||
832 | break; | ||
833 | |||
834 | /* Switch to normal charge mode. */ | ||
835 | charge_state = CHARGING; | ||
836 | charger_setting = CHARGER_ADJUST; | ||
837 | break; | ||
838 | } /* TRICKLE: */ | ||
839 | |||
840 | case CHARGING: /* Constant-current stage */ | ||
841 | case TOPOFF: /* Constant-voltage stage */ | ||
842 | { | ||
843 | /* Reg. mode is more informative than an operational necessity. */ | ||
844 | charge_state = (int_sense0 & MC13783_CCCVS) ? TOPOFF : CHARGING; | ||
845 | |||
846 | if (main_charger_connected()) | ||
847 | { | ||
848 | /* Monitor and stop if current drops below threshold. */ | ||
849 | if (battery_charge_current() > BATTERY_ICHARGE_COMPLETE) | ||
850 | break; | ||
851 | } | ||
852 | else | ||
853 | { | ||
854 | /* Accurate I-level can't be determined since device also | ||
855 | * powers through the I sense. This simply stops the reporting | ||
856 | * of charging but the regulator remains on. */ | ||
857 | if (battery_voltage() <= BATT_USB_VSTOP) | ||
858 | break; | ||
859 | } | ||
860 | |||
861 | stop_charger(); | ||
862 | break; | ||
863 | } /* CHARGING: TOPOFF: */ | ||
864 | |||
865 | default: | ||
866 | break; | ||
867 | } /* switch */ | ||
868 | |||
869 | /* Check if charger timer expired and stop it if so. */ | ||
870 | if (charger_total_timer > 0 && --charger_total_timer == 0) | ||
871 | { | ||
872 | charge_state = CHARGE_STATE_ERROR; | ||
873 | stop_charger(); /* Time ran out - error */ | ||
874 | } | ||
875 | } | ||
876 | |||
877 | /* Main charging algorithm - called from powermgmt.c */ | ||
878 | void charging_algorithm_step(void) | ||
879 | { | ||
880 | #ifdef IMX31_ALLOW_CHARGING | ||
881 | if (service_wdt) | ||
882 | watchdog_service(); | ||
883 | #endif | ||
884 | |||
885 | /* Switch by input state */ | ||
886 | switch (charger_input_state) | ||
887 | { | ||
888 | case NO_CHARGER: | ||
889 | charger_none(); | ||
890 | break; | ||
891 | |||
892 | case CHARGER_PLUGGED: | ||
893 | charger_plugged(); | ||
894 | break; | ||
895 | |||
896 | case CHARGER: | ||
897 | charger_control(); | ||
898 | break; | ||
899 | |||
900 | case CHARGER_UNPLUGGED: | ||
901 | charger_unplugged(); | ||
902 | break; | ||
903 | } /* switch */ | ||
904 | |||
905 | if (charger_close) | ||
906 | { | ||
907 | if (charge_state != CHARGE_STATE_DISABLED) | ||
908 | { | ||
909 | /* Disable starts while shutting down */ | ||
910 | charge_state = CHARGE_STATE_DISABLED; | ||
911 | stop_charger(); | ||
912 | } | ||
913 | |||
914 | charger_close = false; | ||
915 | return; | ||
916 | } | ||
917 | |||
918 | if (charger_setting != 0) | ||
919 | { | ||
920 | if ((mc13783_read(MC13783_CHARGER) & (MC13783_ICHRG | MC13783_VCHRG)) != | ||
921 | charger_setting) | ||
922 | { | ||
923 | /* The hardware setting doesn't match. It could have turned the | ||
924 | * charger off in a race of plugging/unplugging or the setting | ||
925 | * was changed in one of the calls. */ | ||
926 | adjust_charger_current(); | ||
927 | } | ||
928 | } | ||
929 | } | ||
930 | |||
931 | /* Disable the charger and prepare for poweroff - called off-thread so we | ||
932 | * signal the charging thread to prepare to quit. */ | ||
933 | void charging_algorithm_close(void) | ||
934 | { | ||
935 | charger_close = true; | ||
936 | |||
937 | /* Power management thread will set it false again */ | ||
938 | while (charger_close) | ||
939 | sleep(HZ/10); | ||
940 | } | ||