summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apps/debug_menu.c84
-rw-r--r--firmware/export/config-gigabeat-s.h12
-rw-r--r--firmware/export/config.h2
-rw-r--r--firmware/export/powermgmt.h48
-rw-r--r--firmware/export/usb.h3
-rw-r--r--firmware/powermgmt.c130
-rw-r--r--firmware/target/arm/imx31/debug-imx31.c2
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/adc-imx31.c30
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/adc-target.h9
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/mc13783-gigabeat-s.c25
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/mc13783-target.h4
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/power-imx31.c51
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/power-imx31.h3
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/powermgmt-imx31.c883
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/powermgmt-target.h114
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/system-imx31.c25
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/system-target.h5
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/usb-imx31.c10
18 files changed, 1300 insertions, 140 deletions
diff --git a/apps/debug_menu.c b/apps/debug_menu.c
index 0db0e5a471..beee39a09b 100644
--- a/apps/debug_menu.c
+++ b/apps/debug_menu.c
@@ -1649,15 +1649,87 @@ static bool view_battery(void)
1649 charger_inserted() ? "present" : "absent"); 1649 charger_inserted() ? "present" : "absent");
1650 lcd_puts(0, 3, buf); 1650 lcd_puts(0, 3, buf);
1651#if defined TOSHIBA_GIGABEAT_S 1651#if defined TOSHIBA_GIGABEAT_S
1652 int line = 4;
1653 unsigned int st;
1654
1655 static const unsigned char * const chrgstate_strings[] =
1656 {
1657 "Disabled",
1658 "Error",
1659 "Discharging",
1660 "Precharge",
1661 "Constant Voltage",
1662 "Constant Current",
1663 "<unknown>",
1664 };
1665
1666 st = power_input_status() &
1667 (POWER_INPUT_CHARGER | POWER_INPUT_BATTERY);
1668 snprintf(buf, 30, "%s%s",
1669 (st & POWER_INPUT_MAIN_CHARGER) ? " Main" : "",
1670 (st & POWER_INPUT_USB_CHARGER) ? " USB" : "");
1671 lcd_puts(0, line++, buf);
1672
1673 snprintf(buf, 30, "IUSB Max: %d", usb_allowed_current());
1674 lcd_puts(0, line++, buf);
1675
1676 y = ARRAYLEN(chrgstate_strings) - 1;
1677
1678 switch (charge_state)
1679 {
1680 case CHARGE_STATE_DISABLED: y--;
1681 case CHARGE_STATE_ERROR: y--;
1682 case DISCHARGING: y--;
1683 case TRICKLE: y--;
1684 case TOPOFF: y--;
1685 case CHARGING: y--;
1686 default:;
1687 }
1688
1689 snprintf(buf, 30, "State: %s", chrgstate_strings[y]);
1690 lcd_puts(0, line++, buf);
1691
1692 snprintf(buf, 30, "Battery Switch: %s",
1693 (st & POWER_INPUT_BATTERY) ? "On" : "Off");
1694 lcd_puts(0, line++, buf);
1695
1696 y = chrgraw_adc_voltage();
1697 snprintf(buf, 30, "CHRGRAW: %d.%03d V",
1698 y / 1000, y % 1000);
1699 lcd_puts(0, line++, buf);
1700
1701 y = application_supply_adc_voltage();
1702 snprintf(buf, 30, "BP : %d.%03d V",
1703 y / 1000, y % 1000);
1704 lcd_puts(0, line++, buf);
1705
1652 y = battery_adc_charge_current(); 1706 y = battery_adc_charge_current();
1653 x = y < 0 ? '-' : ' '; 1707 if (y < 0) x = '-', y = -y;
1654 y = abs(y); 1708 else x = ' ';
1655 snprintf(buf, 30, "I Charge:%c%d.%03d A", (char)x, y / 1000, y % 1000); 1709 snprintf(buf, 30, "CHRGISN:%c%d mA", x, y);
1656 lcd_puts(0, 4, buf); 1710 lcd_puts(0, line++, buf);
1711
1712 y = cccv_regulator_dissipation();
1713 snprintf(buf, 30, "P CCCV : %d mW", y);
1714 lcd_puts(0, line++, buf);
1715
1716 y = battery_charge_current();
1717 if (y < 0) x = '-', y = -y;
1718 else x = ' ';
1719 snprintf(buf, 30, "I Charge:%c%d mA", x, y);
1720 lcd_puts(0, line++, buf);
1657 1721
1658 y = battery_adc_temp(); 1722 y = battery_adc_temp();
1659 snprintf(buf, 30, "T Battery: %dC (%dF)", y, (9*y + 160) / 5); 1723
1660 lcd_puts(0, 5, buf); 1724 if (y != INT_MIN) {
1725 snprintf(buf, 30, "T Battery: %dC (%dF)", y,
1726 (9*y + 160) / 5);
1727 } else {
1728 /* Conversion disabled */
1729 snprintf(buf, 30, "T Battery: ?");
1730 }
1731
1732 lcd_puts(0, line++, buf);
1661#endif /* defined TOSHIBA_GIGABEAT_S */ 1733#endif /* defined TOSHIBA_GIGABEAT_S */
1662#endif /* defined IPOD_NANO || defined IPOD_VIDEO */ 1734#endif /* defined IPOD_NANO || defined IPOD_VIDEO */
1663#endif /* CONFIG_CHARGING != CHARGING_CONTROL */ 1735#endif /* CONFIG_CHARGING != CHARGING_CONTROL */
diff --git a/firmware/export/config-gigabeat-s.h b/firmware/export/config-gigabeat-s.h
index 54a3de2d19..b4c30268ba 100644
--- a/firmware/export/config-gigabeat-s.h
+++ b/firmware/export/config-gigabeat-s.h
@@ -2,7 +2,6 @@
2 * This config file is for toshiba Gigabeat S 2 * This config file is for toshiba Gigabeat S
3 */ 3 */
4 4
5#define NO_LOW_BATTERY_SHUTDOWN
6#define TARGET_TREE /* this target is using the target tree system */ 5#define TARGET_TREE /* this target is using the target tree system */
7 6
8#define TOSHIBA_GIGABEAT_S 1 7#define TOSHIBA_GIGABEAT_S 1
@@ -132,8 +131,10 @@
132#define BATTERY_CAPACITY_INC 25 /* capacity increment */ 131#define BATTERY_CAPACITY_INC 25 /* capacity increment */
133#define BATTERY_TYPES_COUNT 1 /* only one type */ 132#define BATTERY_TYPES_COUNT 1 /* only one type */
134 133
135/* Hardware controlled charging with monitoring */ 134/* TODO: have a proper status displayed in the bootloader and have it
136#define CONFIG_CHARGING CHARGING_MONITOR 135 * work! */
136/* Charing implemented in a target-specific algorithm */
137#define CONFIG_CHARGING CHARGING_TARGET
137 138
138/* define this if the hardware can be powered off while charging */ 139/* define this if the hardware can be powered off while charging */
139#define HAVE_POWEROFF_WHILE_CHARGING 140#define HAVE_POWEROFF_WHILE_CHARGING
@@ -146,8 +147,9 @@
146#define CPU_FREQ 264000000 /* Set by retailOS loader */ 147#define CPU_FREQ 264000000 /* Set by retailOS loader */
147 148
148/* define this if the unit can be powered or charged via USB */ 149/* define this if the unit can be powered or charged via USB */
149//#define HAVE_USB_POWER /* Disable for now */ 150#define HAVE_USB_POWER
150//#define HAVE_USB_CHARGING_ENABLE 151#define USBPOWER_BUTTON BUTTON_MENU
152#define USBPOWER_BTN_IGNORE BUTTON_POWER
151 153
152/* define this if the unit has a battery switch or battery can be removed 154/* define this if the unit has a battery switch or battery can be removed
153 * when running */ 155 * when running */
diff --git a/firmware/export/config.h b/firmware/export/config.h
index 75aa76a898..d484805532 100644
--- a/firmware/export/config.h
+++ b/firmware/export/config.h
@@ -117,6 +117,8 @@
117#define CHARGING_SIMPLE 1 /* Simple, hardware controlled charging */ 117#define CHARGING_SIMPLE 1 /* Simple, hardware controlled charging */
118#define CHARGING_MONITOR 2 /* Hardware controlled charging with monitoring */ 118#define CHARGING_MONITOR 2 /* Hardware controlled charging with monitoring */
119#define CHARGING_CONTROL 3 /* Software controlled charging */ 119#define CHARGING_CONTROL 3 /* Software controlled charging */
120#define CHARGING_TARGET 4 /* Anything the target implements that is not
121 a generic implementation */
120 122
121/* CONFIG_LCD */ 123/* CONFIG_LCD */
122#define LCD_SSD1815 1 /* as used by Archos Recorders and Ondios */ 124#define LCD_SSD1815 1 /* as used by Archos Recorders and Ondios */
diff --git a/firmware/export/powermgmt.h b/firmware/export/powermgmt.h
index 70c4b70d7a..c333795ad7 100644
--- a/firmware/export/powermgmt.h
+++ b/firmware/export/powermgmt.h
@@ -30,17 +30,26 @@
30#define CHARGE_END_LONGD 50 /* stop when N minutes have passed with 30#define CHARGE_END_LONGD 50 /* stop when N minutes have passed with
31 * avg delta being < -0.02 V */ 31 * avg delta being < -0.02 V */
32 32
33#if CONFIG_CHARGING >= CHARGING_MONITOR
34typedef enum { /* sorted by increasing charging current */ 33typedef enum { /* sorted by increasing charging current */
34#if CONFIG_CHARGING >= CHARGING_MONITOR
35 CHARGE_STATE_DISABLED = -2, /* Disable charger use */
36 CHARGE_STATE_ERROR = -1, /* Some error occurred that should not allow
37 further attempts without user intervention */
38#endif
35 DISCHARGING = 0, 39 DISCHARGING = 0,
40#if CONFIG_CHARGING >= CHARGING_MONITOR
36 TRICKLE, /* Can occur for CONFIG_CHARGING >= CHARGING_MONITOR */ 41 TRICKLE, /* Can occur for CONFIG_CHARGING >= CHARGING_MONITOR */
42 /* For LiIon, the low-current precharge mode if battery
43 was very low */
37 TOPOFF, /* Can occur for CONFIG_CHARGING == CHARGING_CONTROL */ 44 TOPOFF, /* Can occur for CONFIG_CHARGING == CHARGING_CONTROL */
38 CHARGING /* Can occur for all CONFIG_CHARGING options */ 45 /* For LiIon, constant voltage phase */
46 CHARGING, /* Can occur for all CONFIG_CHARGING options */
47 /* For LiIon, the constant current phase */
48#endif
39} charge_state_type; 49} charge_state_type;
40 50
41/* tells what the charger is doing */ 51/* tells what the charger is doing */
42extern charge_state_type charge_state; 52extern charge_state_type charge_state;
43#endif /* CONFIG_CHARGING >= CHARGING_MONITOR */
44 53
45#ifdef CONFIG_CHARGING 54#ifdef CONFIG_CHARGING
46/* 55/*
@@ -48,10 +57,10 @@ extern charge_state_type charge_state;
48 * one time through the power loop when the charger has been plugged in. 57 * one time through the power loop when the charger has been plugged in.
49 */ 58 */
50typedef enum { 59typedef enum {
51 NO_CHARGER, 60 NO_CHARGER = 0, /* No charger is present */
52 CHARGER_UNPLUGGED, /* transient state */ 61 CHARGER_UNPLUGGED, /* Transitional state during CHARGER=>NO_CHARGER */
53 CHARGER_PLUGGED, /* transient state */ 62 CHARGER_PLUGGED, /* Transitional state during NO_CHARGER=>CHARGER */
54 CHARGER 63 CHARGER /* Charger is present */
55} charger_input_state_type; 64} charger_input_state_type;
56 65
57/* tells the state of the charge input */ 66/* tells the state of the charge input */
@@ -154,6 +163,11 @@ extern int trickle_sec; /* trickle charge: How many seconds per minute
154# define MAX_CHG_V 10250 /* anything over 10.25v gives CURRENT_MAX_CHG */ 163# define MAX_CHG_V 10250 /* anything over 10.25v gives CURRENT_MAX_CHG */
155#endif /* not ONDIO */ 164#endif /* not ONDIO */
156 165
166#if CONFIG_CHARGING == CHARGING_TARGET
167/* Include target-specific definitions */
168#include "powermgmt-target.h"
169#endif
170
157extern unsigned short power_history[POWER_HISTORY_LEN]; 171extern unsigned short power_history[POWER_HISTORY_LEN];
158extern const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT]; 172extern const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT];
159extern const unsigned short battery_level_shutoff[BATTERY_TYPES_COUNT]; 173extern const unsigned short battery_level_shutoff[BATTERY_TYPES_COUNT];
@@ -165,6 +179,12 @@ extern const unsigned short percent_to_volt_charge[11];
165/* Start up power management thread */ 179/* Start up power management thread */
166void powermgmt_init(void); 180void powermgmt_init(void);
167 181
182/* Do target portion of init (for CHARGING_TARGET) - called on power thread */
183void powermgmt_init_target(void);
184
185/* Handle frequent tasks and call charging_algorithm_small_step */
186void power_thread_sleep(int ticks);
187
168#endif /* SIMULATOR */ 188#endif /* SIMULATOR */
169 189
170/* Returns battery statust */ 190/* Returns battery statust */
@@ -173,12 +193,20 @@ int battery_time(void); /* minutes */
173unsigned int battery_adc_voltage(void); /* voltage from ADC in millivolts */ 193unsigned int battery_adc_voltage(void); /* voltage from ADC in millivolts */
174unsigned int battery_voltage(void); /* filtered batt. voltage in millivolts */ 194unsigned int battery_voltage(void); /* filtered batt. voltage in millivolts */
175 195
196/* Set the filtered battery voltage (to adjust it before beginning a charge
197 cycle for instance where old, loaded readings will likely be invalid). */
198void set_filtered_battery_voltage(int millivolts);
199
176/* read unfiltered battery info */ 200/* read unfiltered battery info */
177void battery_read_info(int *voltage, int *level); 201void battery_read_info(int *voltage, int *level);
178 202
179/* Tells if the battery level is safe for disk writes */ 203/* Tells if the battery level is safe for disk writes */
180bool battery_level_safe(void); 204bool battery_level_safe(void);
181 205
206#ifdef TARGET_POWERMGMT_FILTER_CHARGE_STATE
207int powermgmt_filter_charge_state(void);
208#endif
209
182void set_poweroff_timeout(int timeout); 210void set_poweroff_timeout(int timeout);
183void set_battery_capacity(int capacity); /* set local battery capacity value */ 211void set_battery_capacity(int capacity); /* set local battery capacity value */
184void set_battery_type(int type); /* set local battery type */ 212void set_battery_type(int type); /* set local battery type */
@@ -190,7 +218,11 @@ void reset_poweroff_timer(void);
190void cancel_shutdown(void); 218void cancel_shutdown(void);
191void shutdown_hw(void); 219void shutdown_hw(void);
192void sys_poweroff(void); 220void sys_poweroff(void);
221/* Returns true if the system should force shutdown for some reason -
222 * eg. low battery */
223bool query_force_shutdown(void);
193#ifdef HAVE_ACCESSORY_SUPPLY 224#ifdef HAVE_ACCESSORY_SUPPLY
194void accessory_supply_set(bool); 225void accessory_supply_set(bool);
195#endif 226#endif
196#endif 227
228#endif /* _POWERMGMT_H_ */
diff --git a/firmware/export/usb.h b/firmware/export/usb.h
index 00517b2475..c8bbf28267 100644
--- a/firmware/export/usb.h
+++ b/firmware/export/usb.h
@@ -54,9 +54,6 @@ enum {
54#elif CONFIG_KEYPAD == GIGABEAT_PAD 54#elif CONFIG_KEYPAD == GIGABEAT_PAD
55#define USBPOWER_BUTTON BUTTON_MENU 55#define USBPOWER_BUTTON BUTTON_MENU
56#define USBPOWER_BTN_IGNORE BUTTON_POWER 56#define USBPOWER_BTN_IGNORE BUTTON_POWER
57#elif CONFIG_KEYPAD == GIGABEAT_S_PAD
58#define USBPOWER_BUTTON BUTTON_MENU
59#define USBPOWER_BTN_IGNORE BUTTON_BACK
60#elif (CONFIG_KEYPAD == IRIVER_H10_PAD) || \ 57#elif (CONFIG_KEYPAD == IRIVER_H10_PAD) || \
61 (CONFIG_KEYPAD == MROBE100_PAD) 58 (CONFIG_KEYPAD == MROBE100_PAD)
62#define USBPOWER_BUTTON BUTTON_RIGHT 59#define USBPOWER_BUTTON BUTTON_RIGHT
diff --git a/firmware/powermgmt.c b/firmware/powermgmt.c
index a1f7ed9836..00b7b2fd4f 100644
--- a/firmware/powermgmt.c
+++ b/firmware/powermgmt.c
@@ -76,7 +76,7 @@ static int wrcount = 0;
76 76
77static int shutdown_timeout = 0; 77static int shutdown_timeout = 0;
78#if CONFIG_CHARGING >= CHARGING_MONITOR 78#if CONFIG_CHARGING >= CHARGING_MONITOR
79charge_state_type charge_state; /* charging mode */ 79charge_state_type charge_state = DISCHARGING; /* charging mode */
80#endif 80#endif
81 81
82static void send_battery_level_event(void); 82static void send_battery_level_event(void);
@@ -204,8 +204,6 @@ void accessory_supply_set(bool enable)
204 204
205#else /* not SIMULATOR ******************************************************/ 205#else /* not SIMULATOR ******************************************************/
206 206
207static void power_thread_sleep(int ticks);
208
209/* 207/*
210 * Average battery voltage and charger voltage, filtered via a digital 208 * Average battery voltage and charger voltage, filtered via a digital
211 * exponential filter (aka. exponential moving average, scaled): 209 * exponential filter (aka. exponential moving average, scaled):
@@ -241,6 +239,19 @@ static int voltage_to_battery_level(int battery_millivolts);
241static void battery_status_update(void); 239static void battery_status_update(void);
242static int runcurrent(void); 240static int runcurrent(void);
243 241
242#ifndef TARGET_POWERMGMT_FILTER_CHARGE_STATE
243static inline int powermgmt_filter_charge_state(void)
244{
245#if CONFIG_CHARGING >= CHARGING_MONITOR
246 /* No adjustment of state */
247 return charge_state;
248#else
249 /* Always discharging */
250 return DISCHARGING;
251#endif
252}
253#endif /* TARGET_POWERMGMT_FILTER_CHARGE_STATE */
254
244void battery_read_info(int *voltage, int *level) 255void battery_read_info(int *voltage, int *level)
245{ 256{
246 int millivolts = battery_adc_voltage(); 257 int millivolts = battery_adc_voltage();
@@ -285,6 +296,10 @@ int battery_time(void)
285/* Returns battery level in percent */ 296/* Returns battery level in percent */
286int battery_level(void) 297int battery_level(void)
287{ 298{
299#ifdef HAVE_BATTERY_SWITCH
300 if ((power_input_status() & POWER_INPUT_BATTERY) == 0)
301 return -1;
302#endif
288 return battery_percent; 303 return battery_percent;
289} 304}
290 305
@@ -294,11 +309,13 @@ unsigned int battery_voltage(void)
294 return battery_millivolts; 309 return battery_millivolts;
295} 310}
296 311
312#ifndef TARGET_BATTERY_LEVEL_SAFE
297/* Tells if the battery level is safe for disk writes */ 313/* Tells if the battery level is safe for disk writes */
298bool battery_level_safe(void) 314bool battery_level_safe(void)
299{ 315{
300 return battery_millivolts > battery_level_dangerous[battery_type]; 316 return battery_millivolts > battery_level_dangerous[battery_type];
301} 317}
318#endif
302 319
303void set_poweroff_timeout(int timeout) 320void set_poweroff_timeout(int timeout)
304{ 321{
@@ -349,26 +366,24 @@ static int voltage_to_percent(int voltage, const short* table)
349 * when battery capacity / type settings are changed */ 366 * when battery capacity / type settings are changed */
350static int voltage_to_battery_level(int battery_millivolts) 367static int voltage_to_battery_level(int battery_millivolts)
351{ 368{
369 const int state = powermgmt_filter_charge_state();
352 int level; 370 int level;
353 371
354#if CONFIG_CHARGING >= CHARGING_MONITOR 372 if (state == DISCHARGING) {
355 if (charge_state == DISCHARGING) {
356 level = voltage_to_percent(battery_millivolts, 373 level = voltage_to_percent(battery_millivolts,
357 percent_to_volt_discharge[battery_type]); 374 percent_to_volt_discharge[battery_type]);
358 } 375 }
359 else if (charge_state == CHARGING) { 376#if CONFIG_CHARGING >= CHARGING_MONITOR
377 else if (state == CHARGING) {
360 /* battery level is defined to be < 100% until charging is finished */ 378 /* battery level is defined to be < 100% until charging is finished */
361 level = MIN(voltage_to_percent(battery_millivolts, 379 level = MIN(voltage_to_percent(battery_millivolts,
362 percent_to_volt_charge), 99); 380 percent_to_volt_charge), 99);
363 } 381 }
364 else { /* in topoff/trickle charge, battery is by definition 100% full */ 382 else {
383 /* in topoff/trickle charge, battery is by definition 100% full */
365 level = 100; 384 level = 100;
366 } 385 }
367#else 386#endif
368 /* always use the discharge table */
369 level = voltage_to_percent(battery_millivolts,
370 percent_to_volt_discharge[battery_type]);
371#endif /* CONFIG_CHARGING ... */
372 387
373 return level; 388 return level;
374} 389}
@@ -381,7 +396,7 @@ static void battery_status_update(void)
381 /* discharging: remaining running time */ 396 /* discharging: remaining running time */
382 /* charging: remaining charging time */ 397 /* charging: remaining charging time */
383#if CONFIG_CHARGING >= CHARGING_MONITOR 398#if CONFIG_CHARGING >= CHARGING_MONITOR
384 if (charge_state == CHARGING) { 399 if (powermgmt_filter_charge_state() == CHARGING) {
385 powermgmt_est_runningtime_min = (100 - level) * battery_capacity * 60 400 powermgmt_est_runningtime_min = (100 - level) * battery_capacity * 60
386 / 100 / (CURRENT_MAX_CHG - runcurrent()); 401 / 100 / (CURRENT_MAX_CHG - runcurrent());
387 } 402 }
@@ -431,15 +446,10 @@ static void handle_auto_poweroff(void)
431 } 446 }
432#endif 447#endif
433 448
434#ifndef NO_LOW_BATTERY_SHUTDOWN 449 if( !shutdown_timeout && query_force_shutdown()) {
435 /* switch off unit if battery level is too low for reliable operation */ 450 backlight_on();
436 if(battery_millivolts < battery_level_shutoff[battery_type]) { 451 sys_poweroff();
437 if(!shutdown_timeout) {
438 backlight_on();
439 sys_poweroff();
440 }
441 } 452 }
442#endif
443 453
444 if(timeout && 454 if(timeout &&
445#if CONFIG_TUNER && !defined(BOOTLOADER) 455#if CONFIG_TUNER && !defined(BOOTLOADER)
@@ -546,6 +556,18 @@ static void power_thread_rtc_process(void)
546} 556}
547#endif 557#endif
548 558
559#ifndef TARGET_QUERY_FORCE_SHUTDOWN
560bool query_force_shutdown(void)
561{
562#ifndef NO_LOW_BATTERY_SHUTDOWN
563 /* switch off unit if battery level is too low for reliable operation */
564 return battery_millivolts < battery_level_shutoff[battery_type];
565#else
566 return false;
567#endif
568}
569#endif /* TARGET_QUERY_FORCE_SHUTDOWN */
570
549/* 571/*
550 * This power thread maintains a history of battery voltage 572 * This power thread maintains a history of battery voltage
551 * and implements a charging algorithm. 573 * and implements a charging algorithm.
@@ -896,6 +918,18 @@ static inline void charging_algorithm_close(void)
896 } 918 }
897#endif 919#endif
898} 920}
921#elif CONFIG_CHARGING == CHARGING_TARGET
922extern void charging_algorithm_big_step(void);
923extern void charging_algorithm_small_step(void);
924extern void charging_algorithm_close(void);
925
926void set_filtered_battery_voltage(int millivolts)
927{
928 avgbat = millivolts * BATT_AVE_SAMPLES;
929 battery_millivolts = millivolts;
930 battery_status_update();
931}
932
899#else 933#else
900#define BATT_AVE_SAMPLES 128 /* slw filter constant for all others */ 934#define BATT_AVE_SAMPLES 128 /* slw filter constant for all others */
901 935
@@ -961,12 +995,12 @@ bool power_input_present(void)
961 * While we are waiting for the time to expire, we average the battery 995 * While we are waiting for the time to expire, we average the battery
962 * voltages. 996 * voltages.
963 */ 997 */
964static void power_thread_sleep(int ticks) 998void power_thread_sleep(int ticks)
965{ 999{
966 int small_ticks; 1000 long tick_return = current_tick + ticks;
967
968 while (ticks > 0) {
969 1001
1002 do
1003 {
970#if CONFIG_CHARGING 1004#if CONFIG_CHARGING
971 /* 1005 /*
972 * Detect charger plugged/unplugged transitions. On a plugged or 1006 * Detect charger plugged/unplugged transitions. On a plugged or
@@ -979,7 +1013,8 @@ static void power_thread_sleep(int ticks)
979 case NO_CHARGER: 1013 case NO_CHARGER:
980 case CHARGER_UNPLUGGED: 1014 case CHARGER_UNPLUGGED:
981 charger_input_state = CHARGER_PLUGGED; 1015 charger_input_state = CHARGER_PLUGGED;
982 return; 1016 tick_return = current_tick;
1017 goto do_small_step; /* Algorithm should see transition */
983 case CHARGER_PLUGGED: 1018 case CHARGER_PLUGGED:
984 queue_broadcast(SYS_CHARGER_CONNECTED, 0); 1019 queue_broadcast(SYS_CHARGER_CONNECTED, 0);
985 last_sent_battery_level = 0; 1020 last_sent_battery_level = 0;
@@ -1000,19 +1035,23 @@ static void power_thread_sleep(int ticks)
1000 case CHARGER_PLUGGED: 1035 case CHARGER_PLUGGED:
1001 case CHARGER: 1036 case CHARGER:
1002 charger_input_state = CHARGER_UNPLUGGED; 1037 charger_input_state = CHARGER_UNPLUGGED;
1003 return; 1038 tick_return = current_tick;
1039 goto do_small_step; /* Algorithm should see transition */
1004 } 1040 }
1005 } 1041 }
1006#endif /* CONFIG_CHARGING */ 1042#endif /* CONFIG_CHARGING */
1007 1043
1008 small_ticks = MIN(HZ/2, ticks); 1044 ticks = tick_return - current_tick;
1009 sleep(small_ticks); 1045
1010 ticks -= small_ticks; 1046 if (ticks > 0) {
1047 ticks = MIN(HZ/2, ticks);
1048 sleep(ticks);
1049 }
1011 1050
1012 /* If the power off timeout expires, the main thread has failed 1051 /* If the power off timeout expires, the main thread has failed
1013 to shut down the system, and we need to force a power off */ 1052 to shut down the system, and we need to force a power off */
1014 if(shutdown_timeout) { 1053 if(shutdown_timeout) {
1015 shutdown_timeout -= small_ticks; 1054 shutdown_timeout -= MAX(ticks, 1);
1016 if(shutdown_timeout <= 0) 1055 if(shutdown_timeout <= 0)
1017 power_off(); 1056 power_off();
1018 } 1057 }
@@ -1024,9 +1063,13 @@ static void power_thread_sleep(int ticks)
1024 /* 1063 /*
1025 * Do a digital exponential filter. We don't sample the battery if 1064 * Do a digital exponential filter. We don't sample the battery if
1026 * the disk is spinning unless we are in USB mode (the disk will most 1065 * the disk is spinning unless we are in USB mode (the disk will most
1027 * likely always be spinning in USB mode). 1066 * likely always be spinning in USB mode) or charging.
1028 */ 1067 */
1029 if (!storage_disk_is_active() || usb_inserted()) { 1068 if (!storage_disk_is_active() || usb_inserted()
1069#if CONFIG_CHARGING >= CHARGING_MONITOR
1070 || charger_input_state == CHARGER
1071#endif
1072 ) {
1030 avgbat += battery_adc_voltage() - (avgbat / BATT_AVE_SAMPLES); 1073 avgbat += battery_adc_voltage() - (avgbat / BATT_AVE_SAMPLES);
1031 /* 1074 /*
1032 * battery_millivolts is the millivolt-scaled filtered battery value. 1075 * battery_millivolts is the millivolt-scaled filtered battery value.
@@ -1047,17 +1090,20 @@ static void power_thread_sleep(int ticks)
1047 /* update battery status every time an update is available */ 1090 /* update battery status every time an update is available */
1048 battery_status_update(); 1091 battery_status_update();
1049 1092
1050#ifndef NO_LOW_BATTERY_SHUTDOWN 1093 if (!shutdown_timeout && query_force_shutdown()) {
1051 if (!shutdown_timeout &&
1052 (battery_millivolts < battery_level_shutoff[battery_type]))
1053 sys_poweroff(); 1094 sys_poweroff();
1054 else 1095 }
1055#endif 1096 else {
1056 avgbat += battery_millivolts - (avgbat / BATT_AVE_SAMPLES); 1097 avgbat += battery_millivolts - (avgbat / BATT_AVE_SAMPLES);
1098 }
1057 } 1099 }
1058 1100
1101#if CONFIG_CHARGING
1102 do_small_step:
1103#endif
1059 charging_algorithm_small_step(); 1104 charging_algorithm_small_step();
1060 } 1105 }
1106 while (TIME_BEFORE(current_tick, tick_return));
1061} 1107}
1062 1108
1063static void power_thread(void) 1109static void power_thread(void)
@@ -1074,7 +1120,7 @@ static void power_thread(void)
1074#ifdef HAVE_DISK_STORAGE /* this adjustment is only needed for HD based */ 1120#ifdef HAVE_DISK_STORAGE /* this adjustment is only needed for HD based */
1075 /* The battery voltage is usually a little lower directly after 1121 /* The battery voltage is usually a little lower directly after
1076 turning on, because the disk was used heavily. Raise it by 5% */ 1122 turning on, because the disk was used heavily. Raise it by 5% */
1077#ifdef HAVE_CHARGING 1123#if CONFIG_CHARGING
1078 if(!charger_inserted()) /* only if charger not connected */ 1124 if(!charger_inserted()) /* only if charger not connected */
1079#endif 1125#endif
1080 avgbat += (percent_to_volt_discharge[battery_type][6] - 1126 avgbat += (percent_to_volt_discharge[battery_type][6] -
@@ -1095,6 +1141,10 @@ static void power_thread(void)
1095 battery_percent += (battery_percent < 100); 1141 battery_percent += (battery_percent < 100);
1096 } 1142 }
1097 1143
1144#if CONFIG_CHARGING == CHARGING_TARGET
1145 powermgmt_init_target();
1146#endif
1147
1098 while (1) 1148 while (1)
1099 { 1149 {
1100 /* rotate the power history */ 1150 /* rotate the power history */
@@ -1121,6 +1171,7 @@ void powermgmt_init(void)
1121 1171
1122#endif /* SIMULATOR */ 1172#endif /* SIMULATOR */
1123 1173
1174#ifndef BOOTLOADER
1124void sys_poweroff(void) 1175void sys_poweroff(void)
1125{ 1176{
1126#ifndef BOOTLOADER 1177#ifndef BOOTLOADER
@@ -1142,6 +1193,7 @@ void sys_poweroff(void)
1142 queue_broadcast(SYS_POWEROFF, 0); 1193 queue_broadcast(SYS_POWEROFF, 0);
1143#endif /* BOOTLOADER */ 1194#endif /* BOOTLOADER */
1144} 1195}
1196#endif
1145 1197
1146void cancel_shutdown(void) 1198void cancel_shutdown(void)
1147{ 1199{
diff --git a/firmware/target/arm/imx31/debug-imx31.c b/firmware/target/arm/imx31/debug-imx31.c
index df2489eafb..b72390cb63 100644
--- a/firmware/target/arm/imx31/debug-imx31.c
+++ b/firmware/target/arm/imx31/debug-imx31.c
@@ -139,6 +139,7 @@ bool __dbg_ports(void)
139 MC13783_INTERRUPT_SENSE0, 139 MC13783_INTERRUPT_SENSE0,
140 MC13783_INTERRUPT_STATUS1, 140 MC13783_INTERRUPT_STATUS1,
141 MC13783_INTERRUPT_SENSE1, 141 MC13783_INTERRUPT_SENSE1,
142 MC13783_CHARGER,
142 MC13783_RTC_TIME, 143 MC13783_RTC_TIME,
143 MC13783_RTC_ALARM, 144 MC13783_RTC_ALARM,
144 MC13783_RTC_DAY, 145 MC13783_RTC_DAY,
@@ -151,6 +152,7 @@ bool __dbg_ports(void)
151 "Int Sense0", 152 "Int Sense0",
152 "Int Stat1 ", 153 "Int Stat1 ",
153 "Int Sense1", 154 "Int Sense1",
155 "Charger ",
154 "RTC Time ", 156 "RTC Time ",
155 "RTC Alarm ", 157 "RTC Alarm ",
156 "RTC Day ", 158 "RTC Day ",
diff --git a/firmware/target/arm/imx31/gigabeat-s/adc-imx31.c b/firmware/target/arm/imx31/gigabeat-s/adc-imx31.c
index 85ef15b9b4..3c66c42adc 100644
--- a/firmware/target/arm/imx31/gigabeat-s/adc-imx31.c
+++ b/firmware/target/arm/imx31/gigabeat-s/adc-imx31.c
@@ -84,6 +84,30 @@ unsigned short adc_read(int channel)
84 return (channel & 4) ? MC13783_ADD2r(data) : MC13783_ADD1r(data); 84 return (channel & 4) ? MC13783_ADD2r(data) : MC13783_ADD1r(data);
85} 85}
86 86
87bool adc_enable_channel(int channel, bool enable)
88{
89 uint32_t bit, mask;
90
91 switch (channel)
92 {
93 case ADC_CHARGER_CURRENT:
94 mask = MC13783_CHRGICON;
95 break;
96
97 case ADC_BATTERY_TEMP:
98 mask = MC13783_RTHEN;
99 break;
100
101 default:
102 return false;
103 }
104
105 bit = enable ? mask : 0;
106
107 return mc13783_write_masked(MC13783_ADC0, bit, mask)
108 != MC13783_DATA_ERROR;
109}
110
87/* Called by mc13783 interrupt thread when conversion is complete */ 111/* Called by mc13783 interrupt thread when conversion is complete */
88void adc_done(void) 112void adc_done(void)
89{ 113{
@@ -98,9 +122,9 @@ void adc_init(void)
98 /* Init so first reads get data */ 122 /* Init so first reads get data */
99 last_adc_read[0] = last_adc_read[1] = current_tick-1; 123 last_adc_read[0] = last_adc_read[1] = current_tick-1;
100 124
101 /* Enable increment-by-read, thermistor, charge current */ 125 /* Enable increment-by-read, turn off extra conversions. */
102 mc13783_write(MC13783_ADC0, MC13783_ADINC2 | MC13783_ADINC1 | 126 mc13783_write(MC13783_ADC0, MC13783_ADINC2 | MC13783_ADINC1);
103 MC13783_RTHEN | MC13783_CHRGICON); 127
104 /* Enable ADC, set multi-channel mode */ 128 /* Enable ADC, set multi-channel mode */
105 mc13783_write(MC13783_ADC1, MC13783_ADEN); 129 mc13783_write(MC13783_ADC1, MC13783_ADEN);
106 130
diff --git a/firmware/target/arm/imx31/gigabeat-s/adc-target.h b/firmware/target/arm/imx31/gigabeat-s/adc-target.h
index efa665dd98..00027e05df 100644
--- a/firmware/target/arm/imx31/gigabeat-s/adc-target.h
+++ b/firmware/target/arm/imx31/gigabeat-s/adc-target.h
@@ -47,7 +47,14 @@
47#define ADC_READ_ERROR 0xFFFF 47#define ADC_READ_ERROR 0xFFFF
48 48
49void adc_done(void); 49void adc_done(void);
50/* Enable conversion of specified channel (if switchoff is possible) */
51bool adc_enable_channel(int channel, bool enable);
52
53/* Implemented in powermgmt-imx31.c */
50int battery_adc_charge_current(void); 54int battery_adc_charge_current(void);
51unsigned int battery_adc_temp(void); 55int battery_adc_temp(void);
56unsigned int application_supply_adc_voltage(void);
57unsigned int chrgraw_adc_voltage(void);
58unsigned int cccv_regulator_dissipation(void);
52 59
53#endif 60#endif
diff --git a/firmware/target/arm/imx31/gigabeat-s/mc13783-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/mc13783-gigabeat-s.c
index e6238112d1..fc9ad719a6 100644
--- a/firmware/target/arm/imx31/gigabeat-s/mc13783-gigabeat-s.c
+++ b/firmware/target/arm/imx31/gigabeat-s/mc13783-gigabeat-s.c
@@ -28,6 +28,7 @@
28#include "button-target.h" 28#include "button-target.h"
29#include "usb-target.h" 29#include "usb-target.h"
30#include "power-imx31.h" 30#include "power-imx31.h"
31#include "powermgmt-target.h"
31 32
32/* Gigabeat S definitions for static MC13783 event registration */ 33/* Gigabeat S definitions for static MC13783 event registration */
33 34
@@ -45,26 +46,26 @@ static const struct mc13783_event mc13783_events[] =
45 .mask = MC13783_ONOFD1M, 46 .mask = MC13783_ONOFD1M,
46 .callback = button_power_event, 47 .callback = button_power_event,
47 }, 48 },
48#ifdef HAVE_HEADPHONE_DETECTION 49 [MC13783_SE1_EVENT] = /* Main charger detection */
49 [MC13783_ONOFD2_EVENT] = /* Headphone jack */
50 {
51 .set = MC13783_EVENT_SET1,
52 .mask = MC13783_ONOFD2M,
53 .callback = headphone_detect_event,
54 },
55#endif
56 [MC13783_CHGDET_EVENT] = /* Charger detection */
57 { 50 {
58 .set = MC13783_EVENT_SET0, 51 .set = MC13783_EVENT_SET0,
59 .mask = MC13783_CHGDETM, 52 .mask = MC13783_SE1M,
60 .callback = charger_detect_event, 53 .callback = charger_main_detect_event,
61 }, 54 },
62 [MC13783_USB4V4_EVENT] = /* USB insertion */ 55 [MC13783_USB_EVENT] = /* USB insertion/USB charger detection */
63 { 56 {
64 .set = MC13783_EVENT_SET0, 57 .set = MC13783_EVENT_SET0,
65 .mask = MC13783_USBM, 58 .mask = MC13783_USBM,
66 .callback = usb_connect_event, 59 .callback = usb_connect_event,
67 }, 60 },
61#ifdef HAVE_HEADPHONE_DETECTION
62 [MC13783_ONOFD2_EVENT] = /* Headphone jack */
63 {
64 .set = MC13783_EVENT_SET1,
65 .mask = MC13783_ONOFD2M,
66 .callback = headphone_detect_event,
67 },
68#endif
68}; 69};
69 70
70const struct mc13783_event_list mc13783_event_list = 71const struct mc13783_event_list mc13783_event_list =
diff --git a/firmware/target/arm/imx31/gigabeat-s/mc13783-target.h b/firmware/target/arm/imx31/gigabeat-s/mc13783-target.h
index 26ab3c6e21..2ae3be1819 100644
--- a/firmware/target/arm/imx31/gigabeat-s/mc13783-target.h
+++ b/firmware/target/arm/imx31/gigabeat-s/mc13783-target.h
@@ -31,8 +31,8 @@ enum mc13783_event_ids
31#ifdef HAVE_HEADPHONE_DETECTION 31#ifdef HAVE_HEADPHONE_DETECTION
32 MC13783_ONOFD2_EVENT, /* Headphone jack */ 32 MC13783_ONOFD2_EVENT, /* Headphone jack */
33#endif 33#endif
34 MC13783_CHGDET_EVENT, /* Charger detection */ 34 MC13783_SE1_EVENT, /* Main charger detection */
35 MC13783_USB4V4_EVENT, /* USB insertion */ 35 MC13783_USB_EVENT, /* USB insertion */
36}; 36};
37 37
38#endif /* MC13783_TARGET_H */ 38#endif /* MC13783_TARGET_H */
diff --git a/firmware/target/arm/imx31/gigabeat-s/power-imx31.c b/firmware/target/arm/imx31/gigabeat-s/power-imx31.c
index 17008cec4b..39724c7b75 100644
--- a/firmware/target/arm/imx31/gigabeat-s/power-imx31.c
+++ b/firmware/target/arm/imx31/gigabeat-s/power-imx31.c
@@ -20,6 +20,8 @@
20 ****************************************************************************/ 20 ****************************************************************************/
21#include "config.h" 21#include "config.h"
22#include "system.h" 22#include "system.h"
23#include "usb.h"
24#include "usb_core.h"
23#include "power.h" 25#include "power.h"
24#include "power-imx31.h" 26#include "power-imx31.h"
25#include "backlight.h" 27#include "backlight.h"
@@ -29,35 +31,47 @@
29#include "i2c-imx31.h" 31#include "i2c-imx31.h"
30 32
31extern struct i2c_node si4700_i2c_node; 33extern struct i2c_node si4700_i2c_node;
34static unsigned int power_status = POWER_INPUT_NONE;
32 35
33static bool charger_detect = false; 36/* Detect which power sources are present. */
34
35/* This is called from the mc13783 interrupt thread */
36void charger_detect_event(void)
37{
38 charger_detect =
39 mc13783_read(MC13783_INTERRUPT_SENSE0) & MC13783_CHGDETS;
40}
41
42unsigned int power_input_status(void) 37unsigned int power_input_status(void)
43{ 38{
44 unsigned int status = POWER_INPUT_NONE; 39 unsigned int status = power_status;
45 40
46 if ((GPIO3_DR & (1 << 20)) != 0) 41 if (GPIO3_DR & (1 << 20))
47 status |= POWER_INPUT_BATTERY; 42 status |= POWER_INPUT_BATTERY;
48 43
49 if (charger_detect) 44 if (usb_allowed_current() < 500)
50 status |= POWER_INPUT_MAIN_CHARGER; 45 {
46 /* ACK that USB is connected but NOT chargeable */
47 status &= ~(POWER_INPUT_USB_CHARGER & POWER_INPUT_CHARGER);
48 }
51 49
52 return status; 50 return status;
53} 51}
54 52
55/* Returns true if the unit is charging the batteries. */ 53/* Detect changes in presence of the AC adaptor. */
56bool charging_state(void) 54void charger_main_detect_event(void)
57{ 55{
58 return false; 56 if (mc13783_read(MC13783_INTERRUPT_SENSE0) & MC13783_SE1S)
57 power_status |= POWER_INPUT_MAIN_CHARGER;
58 else
59 power_status &= ~POWER_INPUT_MAIN_CHARGER;
59} 60}
60 61
62/* Detect changes in USB bus power. Called from usb connect event handler. */
63void charger_usb_detect_event(int status)
64{
65 /* USB plugged does not imply charging is possible or even
66 * powering the device to maintain the battery. */
67 if (status == USB_INSERTED)
68 power_status |= POWER_INPUT_USB_CHARGER;
69 else
70 power_status &= ~POWER_INPUT_USB_CHARGER;
71}
72
73/* charging_state is implemented in powermgmt-imx31.c */
74
61void ide_power_enable(bool on) 75void ide_power_enable(bool on)
62{ 76{
63 if (!on) 77 if (!on)
@@ -129,9 +143,8 @@ void power_off(void)
129void power_init(void) 143void power_init(void)
130{ 144{
131 /* Poll initial state */ 145 /* Poll initial state */
132 charger_detect_event(); 146 charger_main_detect_event();
133 147
134 /* Enable detect event */ 148 /* Enable detect event */
135 mc13783_enable_event(MC13783_CHGDET_EVENT); 149 mc13783_enable_event(MC13783_SE1_EVENT);
136} 150}
137
diff --git a/firmware/target/arm/imx31/gigabeat-s/power-imx31.h b/firmware/target/arm/imx31/gigabeat-s/power-imx31.h
index 86ba4ac815..9294de102c 100644
--- a/firmware/target/arm/imx31/gigabeat-s/power-imx31.h
+++ b/firmware/target/arm/imx31/gigabeat-s/power-imx31.h
@@ -21,6 +21,7 @@
21#ifndef POWER_IMX31_H 21#ifndef POWER_IMX31_H
22#define POWER_IMX31_H 22#define POWER_IMX31_H
23 23
24void charger_detect_event(void); 24void charger_main_detect_event(void);
25void charger_usb_detect_event(int status);
25 26
26#endif /* POWER_IMX31_H */ 27#endif /* POWER_IMX31_H */
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 */
diff --git a/firmware/target/arm/imx31/gigabeat-s/powermgmt-target.h b/firmware/target/arm/imx31/gigabeat-s/powermgmt-target.h
new file mode 100644
index 0000000000..8ad4af8d18
--- /dev/null
+++ b/firmware/target/arm/imx31/gigabeat-s/powermgmt-target.h
@@ -0,0 +1,114 @@
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#ifndef POWERMGMT_TARGET_H
22#define POWERMGMT_TARGET_H
23
24/* Can't just run this code willy-nilly. Do not allow charger engagement
25 * without carefully verifying compatibility.
26 *
27 * Things to check:
28 * 1) Charge path configuration for the PMIC.
29 * 2) Correct thermistor reading
30 * 3) Accurate voltage readings
31 * 4) Accurate current sense for the charge path as the sense resistor may
32 * deviate from the 0.1 ohms assumed by the charge path regulator.
33 */
34#ifdef TOSHIBA_GIGABEAT_S
35/*
36 * Gigabeat S qualifications:
37 * 1) Setup for dual-supply mode with separate inputs and providing USB
38 * charging capability through external components.
39 * 2) Curve obtained experimentally - extreme deviation from "optimized"
40 * characteristics.
41 * 3) Verified at battery terminals - no deviation from datasheet formula.
42 * 4) 0.316 ohms <=?? - verified by comparitive current readings on device
43 * with ammeter readings and measurement of on-board components.
44 */
45#ifndef BOOTLOADER
46#define IMX31_ALLOW_CHARGING
47#endif
48
49#else
50#warning This iMX31 target requires validation of charging algorithm - charging disabled
51#endif
52
53#define BATT_VTRICKLE_CHARGE 2900 /* Must charge slowly */
54#define BATT_VSLOW_CHARGE 3500 /* Lower-current charge mode below
55 * this level */
56#define BATT_VAUTO_RECHARGE 4100 /* When to begin another cycle */
57#define BATT_USB_VAUTO_RECHARGE 4000 /* When to cycle with USB only */
58#define BATT_USB_VSTOP 4140 /* When to "stop" when USB only */
59#define BATT_TOO_LOW 2400 /* No battery? Short? Can't
60 read below 2400mV. */
61#define CHARGER_TOTAL_TIMER 300 /* minutes */
62
63/* .316 ohms is closest standard value as measured in 1% tolerance - adjust
64 * relative to .100 ohm which is what the PMIC is "tuned" for. */
65#define ILEVEL_ADJUST_IN(I) (100*(I) / 316)
66#define ILEVEL_ADJUST_OUT(I) (316*(I) / 100)
67
68/* Relative draw to battery capacity - adjusted for sense resistor */
69#define BATTERY_ICHARGE_COMPLETE (505*9/100) /* 9% of nominal max output */
70/* All charging modes use 4.200V for regulator */
71#define BATTERY_VCHARGING MC13783_VCHRG_4_200V
72/* Slow charging - MAIN - Still below 3.5V (avoid excessive reg. dissipation) */
73/* #define BATTERY_ISLOW */
74/* Fast charging - MAIN */
75#define BATTERY_IFAST MC13783_ICHRG_1596MA /* 505mA */
76/* Trickle charging low battery - MAIN (~10% Imax) */
77#define BATTERY_ITRICKLE MC13783_ICHRG_177MA /* 56mA */
78/* Slow charging - USB - Still below 3.5V (avoid excessive reg. dissipation) */
79/* #define BATTERY_ISLOW_USB */
80/* Fast charging - USB */
81#define BATTERY_IFAST_USB MC13783_ICHRG_1152MA /* 365mA */
82/* Trickle charging low battery - USB (Ibat = Icccv - Idevice) */
83#define BATTERY_ITRICKLE_USB MC13783_ICHRG_532MA /* 168mA */
84/* Maintain charge - USB 500mA */
85#define BATTERY_IFLOAT_USB MC13783_ICHRG_1152MA /* 365mA */
86#define BATTERY_VFLOAT_USB MC13783_VCHRG_4_150V
87/* Maintain charge - USB 100mA */
88#define BATTERY_IMAINTAIN_USB MC13783_ICHRG_266MA /* 84mA */
89#define BATTERY_VMAINTAIN_USB MC13783_VCHRG_4_150V
90
91/* Battery filter lengths in samples */
92#define BATT_AVE_SAMPLES 32
93#define ICHARGER_AVE_SAMPLES 32
94
95/* Provide filtered charge current */
96int battery_charge_current(void);
97
98#ifndef SIMULATOR
99/* Indicate various functions that require implementation at the target level.
100 * This is because the battery could be low or the battery switch is off but
101 * with the main charger attached which implies safe power for anything. The
102 * true battery reading is always reported for voltage readings and not the
103 * value at the application supply. */
104#define TARGET_QUERY_FORCE_SHUTDOWN
105
106/* For this the application supply is read out if the charger is attached or
107 * the battery read if not (completely hardware selected at BP). */
108#define TARGET_BATTERY_LEVEL_SAFE
109
110/* The state should be adjusted to CHARGING or DISCHARGING */
111#define TARGET_POWERMGMT_FILTER_CHARGE_STATE
112#endif /* SIMULATOR */
113
114#endif /* POWERMGMT_TARGET_H */
diff --git a/firmware/target/arm/imx31/gigabeat-s/system-imx31.c b/firmware/target/arm/imx31/gigabeat-s/system-imx31.c
index 6d4797e9df..7d778fb8fd 100644
--- a/firmware/target/arm/imx31/gigabeat-s/system-imx31.c
+++ b/firmware/target/arm/imx31/gigabeat-s/system-imx31.c
@@ -32,6 +32,31 @@
32#include "clkctl-imx31.h" 32#include "clkctl-imx31.h"
33#include "mc13783.h" 33#include "mc13783.h"
34 34
35/* Initialize the watchdog timer */
36void watchdog_init(unsigned int half_seconds)
37{
38 uint16_t wcr = WDOG_WCR_WTw(half_seconds) | /* Timeout */
39 WDOG_WCR_WOE | /* WDOG output enabled */
40 WDOG_WCR_WDA | /* WDOG assertion - no effect */
41 WDOG_WCR_SRS | /* System reset - no effect */
42 WDOG_WCR_WRE; /* Generate a WDOG signal */
43
44 imx31_clkctl_module_clock_gating(CG_WDOG, CGM_ON_RUN_WAIT);
45
46 WDOG_WCR = wcr;
47 WDOG_WSR = 0x5555;
48 WDOG_WCR = wcr | WDOG_WCR_WDE; /* Enable timer - hardware does
49 not allow a disable now */
50 WDOG_WSR = 0xaaaa;
51}
52
53/* Service the watchdog timer */
54void watchdog_service(void)
55{
56 WDOG_WSR = 0x5555;
57 WDOG_WSR = 0xaaaa;
58}
59
35int system_memory_guard(int newmode) 60int system_memory_guard(int newmode)
36{ 61{
37 (void)newmode; 62 (void)newmode;
diff --git a/firmware/target/arm/imx31/gigabeat-s/system-target.h b/firmware/target/arm/imx31/gigabeat-s/system-target.h
index 31f1342c9e..fbf7b23f3b 100644
--- a/firmware/target/arm/imx31/gigabeat-s/system-target.h
+++ b/firmware/target/arm/imx31/gigabeat-s/system-target.h
@@ -39,6 +39,11 @@ static inline void udelay(unsigned int usecs)
39} 39}
40#endif 40#endif
41 41
42/* Service the watchdog timer - serviced from the power thread every minute */
43void watchdog_init(unsigned int half_seconds);
44void watchdog_service(void);
45
46/* Prepare for transition to firmware */
42void system_prepare_fw_start(void); 47void system_prepare_fw_start(void);
43void tick_stop(void); 48void tick_stop(void);
44void kernel_device_init(void); 49void kernel_device_init(void);
diff --git a/firmware/target/arm/imx31/gigabeat-s/usb-imx31.c b/firmware/target/arm/imx31/gigabeat-s/usb-imx31.c
index 382bc326b7..a7314861c3 100644
--- a/firmware/target/arm/imx31/gigabeat-s/usb-imx31.c
+++ b/firmware/target/arm/imx31/gigabeat-s/usb-imx31.c
@@ -28,6 +28,7 @@
28#include "usb_drv.h" 28#include "usb_drv.h"
29#include "usb-target.h" 29#include "usb-target.h"
30#include "clkctl-imx31.h" 30#include "clkctl-imx31.h"
31#include "power-imx31.h"
31#include "mc13783.h" 32#include "mc13783.h"
32 33
33static int usb_status = USB_EXTRACTED; 34static int usb_status = USB_EXTRACTED;
@@ -53,8 +54,11 @@ static void enable_transceiver(bool enable)
53 54
54void usb_connect_event(void) 55void usb_connect_event(void)
55{ 56{
56 uint32_t status = mc13783_read(MC13783_INTERRUPT_SENSE0); 57 uint32_t status = mc13783_read(MC13783_INTERRUPT_SENSE0);
57 usb_status = (status & MC13783_USB4V4S) ? USB_INSERTED : USB_EXTRACTED; 58 usb_status = (status & MC13783_USB4V4S) ?
59 USB_INSERTED : USB_EXTRACTED;
60 /* Notify power that USB charging is potentially available */
61 charger_usb_detect_event(usb_status);
58} 62}
59 63
60int usb_detect(void) 64int usb_detect(void)
@@ -81,7 +85,7 @@ void usb_init_device(void)
81 usb_connect_event(); 85 usb_connect_event();
82 86
83 /* Enable PMIC event */ 87 /* Enable PMIC event */
84 mc13783_enable_event(MC13783_USB4V4_EVENT); 88 mc13783_enable_event(MC13783_USB_EVENT);
85} 89}
86 90
87void usb_enable(bool on) 91void usb_enable(bool on)