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 0db0e5a..beee39a 100644
--- a/apps/debug_menu.c
+++ b/apps/debug_menu.c
@@ -1649,15 +1649,87 @@ static bool view_battery(void)
charger_inserted() ? "present" : "absent");
lcd_puts(0, 3, buf);
#if defined TOSHIBA_GIGABEAT_S
+ int line = 4;
+ unsigned int st;
+
+ static const unsigned char * const chrgstate_strings[] =
+ {
+ "Disabled",
+ "Error",
+ "Discharging",
+ "Precharge",
+ "Constant Voltage",
+ "Constant Current",
+ "<unknown>",
+ };
+
+ st = power_input_status() &
+ (POWER_INPUT_CHARGER | POWER_INPUT_BATTERY);
+ snprintf(buf, 30, "%s%s",
+ (st & POWER_INPUT_MAIN_CHARGER) ? " Main" : "",
+ (st & POWER_INPUT_USB_CHARGER) ? " USB" : "");
+ lcd_puts(0, line++, buf);
+
+ snprintf(buf, 30, "IUSB Max: %d", usb_allowed_current());
+ lcd_puts(0, line++, buf);
+
+ y = ARRAYLEN(chrgstate_strings) - 1;
+
+ switch (charge_state)
+ {
+ case CHARGE_STATE_DISABLED: y--;
+ case CHARGE_STATE_ERROR: y--;
+ case DISCHARGING: y--;
+ case TRICKLE: y--;
+ case TOPOFF: y--;
+ case CHARGING: y--;
+ default:;
+ }
+
+ snprintf(buf, 30, "State: %s", chrgstate_strings[y]);
+ lcd_puts(0, line++, buf);
+
+ snprintf(buf, 30, "Battery Switch: %s",
+ (st & POWER_INPUT_BATTERY) ? "On" : "Off");
+ lcd_puts(0, line++, buf);
+
+ y = chrgraw_adc_voltage();
+ snprintf(buf, 30, "CHRGRAW: %d.%03d V",
+ y / 1000, y % 1000);
+ lcd_puts(0, line++, buf);
+
+ y = application_supply_adc_voltage();
+ snprintf(buf, 30, "BP : %d.%03d V",
+ y / 1000, y % 1000);
+ lcd_puts(0, line++, buf);
+
y = battery_adc_charge_current();
- x = y < 0 ? '-' : ' ';
- y = abs(y);
- snprintf(buf, 30, "I Charge:%c%d.%03d A", (char)x, y / 1000, y % 1000);
- lcd_puts(0, 4, buf);
+ if (y < 0) x = '-', y = -y;
+ else x = ' ';
+ snprintf(buf, 30, "CHRGISN:%c%d mA", x, y);
+ lcd_puts(0, line++, buf);
+
+ y = cccv_regulator_dissipation();
+ snprintf(buf, 30, "P CCCV : %d mW", y);
+ lcd_puts(0, line++, buf);
+
+ y = battery_charge_current();
+ if (y < 0) x = '-', y = -y;
+ else x = ' ';
+ snprintf(buf, 30, "I Charge:%c%d mA", x, y);
+ lcd_puts(0, line++, buf);
y = battery_adc_temp();
- snprintf(buf, 30, "T Battery: %dC (%dF)", y, (9*y + 160) / 5);
- lcd_puts(0, 5, buf);
+
+ if (y != INT_MIN) {
+ snprintf(buf, 30, "T Battery: %dC (%dF)", y,
+ (9*y + 160) / 5);
+ } else {
+ /* Conversion disabled */
+ snprintf(buf, 30, "T Battery: ?");
+ }
+
+ lcd_puts(0, line++, buf);
#endif /* defined TOSHIBA_GIGABEAT_S */
#endif /* defined IPOD_NANO || defined IPOD_VIDEO */
#endif /* CONFIG_CHARGING != CHARGING_CONTROL */
diff --git a/firmware/export/config-gigabeat-s.h b/firmware/export/config-gigabeat-s.h
index 54a3de2..b4c3026 100644
--- a/firmware/export/config-gigabeat-s.h
+++ b/firmware/export/config-gigabeat-s.h
@@ -2,7 +2,6 @@
* This config file is for toshiba Gigabeat S
*/
-#define NO_LOW_BATTERY_SHUTDOWN
#define TARGET_TREE /* this target is using the target tree system */
#define TOSHIBA_GIGABEAT_S 1
@@ -132,8 +131,10 @@
#define BATTERY_CAPACITY_INC 25 /* capacity increment */
#define BATTERY_TYPES_COUNT 1 /* only one type */
-/* Hardware controlled charging with monitoring */
-#define CONFIG_CHARGING CHARGING_MONITOR
+/* TODO: have a proper status displayed in the bootloader and have it
+ * work! */
+/* Charing implemented in a target-specific algorithm */
+#define CONFIG_CHARGING CHARGING_TARGET
/* define this if the hardware can be powered off while charging */
#define HAVE_POWEROFF_WHILE_CHARGING
@@ -146,8 +147,9 @@
#define CPU_FREQ 264000000 /* Set by retailOS loader */
/* define this if the unit can be powered or charged via USB */
-//#define HAVE_USB_POWER /* Disable for now */
-//#define HAVE_USB_CHARGING_ENABLE
+#define HAVE_USB_POWER
+#define USBPOWER_BUTTON BUTTON_MENU
+#define USBPOWER_BTN_IGNORE BUTTON_POWER
/* define this if the unit has a battery switch or battery can be removed
* when running */
diff --git a/firmware/export/config.h b/firmware/export/config.h
index 75aa76a..d484805 100644
--- a/firmware/export/config.h
+++ b/firmware/export/config.h
@@ -117,6 +117,8 @@
#define CHARGING_SIMPLE 1 /* Simple, hardware controlled charging */
#define CHARGING_MONITOR 2 /* Hardware controlled charging with monitoring */
#define CHARGING_CONTROL 3 /* Software controlled charging */
+#define CHARGING_TARGET 4 /* Anything the target implements that is not
+ a generic implementation */
/* CONFIG_LCD */
#define LCD_SSD1815 1 /* as used by Archos Recorders and Ondios */
diff --git a/firmware/export/powermgmt.h b/firmware/export/powermgmt.h
index 70c4b70..c333795 100644
--- a/firmware/export/powermgmt.h
+++ b/firmware/export/powermgmt.h
@@ -30,17 +30,26 @@
#define CHARGE_END_LONGD 50 /* stop when N minutes have passed with
* avg delta being < -0.02 V */
-#if CONFIG_CHARGING >= CHARGING_MONITOR
typedef enum { /* sorted by increasing charging current */
+#if CONFIG_CHARGING >= CHARGING_MONITOR
+ CHARGE_STATE_DISABLED = -2, /* Disable charger use */
+ CHARGE_STATE_ERROR = -1, /* Some error occurred that should not allow
+ further attempts without user intervention */
+#endif
DISCHARGING = 0,
+#if CONFIG_CHARGING >= CHARGING_MONITOR
TRICKLE, /* Can occur for CONFIG_CHARGING >= CHARGING_MONITOR */
+ /* For LiIon, the low-current precharge mode if battery
+ was very low */
TOPOFF, /* Can occur for CONFIG_CHARGING == CHARGING_CONTROL */
- CHARGING /* Can occur for all CONFIG_CHARGING options */
+ /* For LiIon, constant voltage phase */
+ CHARGING, /* Can occur for all CONFIG_CHARGING options */
+ /* For LiIon, the constant current phase */
+#endif
} charge_state_type;
/* tells what the charger is doing */
extern charge_state_type charge_state;
-#endif /* CONFIG_CHARGING >= CHARGING_MONITOR */
#ifdef CONFIG_CHARGING
/*
@@ -48,10 +57,10 @@ extern charge_state_type charge_state;
* one time through the power loop when the charger has been plugged in.
*/
typedef enum {
- NO_CHARGER,
- CHARGER_UNPLUGGED, /* transient state */
- CHARGER_PLUGGED, /* transient state */
- CHARGER
+ NO_CHARGER = 0, /* No charger is present */
+ CHARGER_UNPLUGGED, /* Transitional state during CHARGER=>NO_CHARGER */
+ CHARGER_PLUGGED, /* Transitional state during NO_CHARGER=>CHARGER */
+ CHARGER /* Charger is present */
} charger_input_state_type;
/* tells the state of the charge input */
@@ -154,6 +163,11 @@ extern int trickle_sec; /* trickle charge: How many seconds per minute
# define MAX_CHG_V 10250 /* anything over 10.25v gives CURRENT_MAX_CHG */
#endif /* not ONDIO */
+#if CONFIG_CHARGING == CHARGING_TARGET
+/* Include target-specific definitions */
+#include "powermgmt-target.h"
+#endif
+
extern unsigned short power_history[POWER_HISTORY_LEN];
extern const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT];
extern const unsigned short battery_level_shutoff[BATTERY_TYPES_COUNT];
@@ -165,6 +179,12 @@ extern const unsigned short percent_to_volt_charge[11];
/* Start up power management thread */
void powermgmt_init(void);
+/* Do target portion of init (for CHARGING_TARGET) - called on power thread */
+void powermgmt_init_target(void);
+
+/* Handle frequent tasks and call charging_algorithm_small_step */
+void power_thread_sleep(int ticks);
+
#endif /* SIMULATOR */
/* Returns battery statust */
@@ -173,12 +193,20 @@ int battery_time(void); /* minutes */
unsigned int battery_adc_voltage(void); /* voltage from ADC in millivolts */
unsigned int battery_voltage(void); /* filtered batt. voltage in millivolts */
+/* Set the filtered battery voltage (to adjust it before beginning a charge
+ cycle for instance where old, loaded readings will likely be invalid). */
+void set_filtered_battery_voltage(int millivolts);
+
/* read unfiltered battery info */
void battery_read_info(int *voltage, int *level);
/* Tells if the battery level is safe for disk writes */
bool battery_level_safe(void);
+#ifdef TARGET_POWERMGMT_FILTER_CHARGE_STATE
+int powermgmt_filter_charge_state(void);
+#endif
+
void set_poweroff_timeout(int timeout);
void set_battery_capacity(int capacity); /* set local battery capacity value */
void set_battery_type(int type); /* set local battery type */
@@ -190,7 +218,11 @@ void reset_poweroff_timer(void);
void cancel_shutdown(void);
void shutdown_hw(void);
void sys_poweroff(void);
+/* Returns true if the system should force shutdown for some reason -
+ * eg. low battery */
+bool query_force_shutdown(void);
#ifdef HAVE_ACCESSORY_SUPPLY
void accessory_supply_set(bool);
#endif
-#endif
+
+#endif /* _POWERMGMT_H_ */
diff --git a/firmware/export/usb.h b/firmware/export/usb.h
index 00517b2..c8bbf28 100644
--- a/firmware/export/usb.h
+++ b/firmware/export/usb.h
@@ -54,9 +54,6 @@ enum {
#elif CONFIG_KEYPAD == GIGABEAT_PAD
#define USBPOWER_BUTTON BUTTON_MENU
#define USBPOWER_BTN_IGNORE BUTTON_POWER
-#elif CONFIG_KEYPAD == GIGABEAT_S_PAD
-#define USBPOWER_BUTTON BUTTON_MENU
-#define USBPOWER_BTN_IGNORE BUTTON_BACK
#elif (CONFIG_KEYPAD == IRIVER_H10_PAD) || \
(CONFIG_KEYPAD == MROBE100_PAD)
#define USBPOWER_BUTTON BUTTON_RIGHT
diff --git a/firmware/powermgmt.c b/firmware/powermgmt.c
index a1f7ed9..00b7b2f 100644
--- a/firmware/powermgmt.c
+++ b/firmware/powermgmt.c
@@ -76,7 +76,7 @@ static int wrcount = 0;
static int shutdown_timeout = 0;
#if CONFIG_CHARGING >= CHARGING_MONITOR
-charge_state_type charge_state; /* charging mode */
+charge_state_type charge_state = DISCHARGING; /* charging mode */
#endif
static void send_battery_level_event(void);
@@ -204,8 +204,6 @@ void accessory_supply_set(bool enable)
#else /* not SIMULATOR ******************************************************/
-static void power_thread_sleep(int ticks);
-
/*
* Average battery voltage and charger voltage, filtered via a digital
* exponential filter (aka. exponential moving average, scaled):
@@ -241,6 +239,19 @@ static int voltage_to_battery_level(int battery_millivolts);
static void battery_status_update(void);
static int runcurrent(void);
+#ifndef TARGET_POWERMGMT_FILTER_CHARGE_STATE
+static inline int powermgmt_filter_charge_state(void)
+{
+#if CONFIG_CHARGING >= CHARGING_MONITOR
+ /* No adjustment of state */
+ return charge_state;
+#else
+ /* Always discharging */
+ return DISCHARGING;
+#endif
+}
+#endif /* TARGET_POWERMGMT_FILTER_CHARGE_STATE */
+
void battery_read_info(int *voltage, int *level)
{
int millivolts = battery_adc_voltage();
@@ -285,6 +296,10 @@ int battery_time(void)
/* Returns battery level in percent */
int battery_level(void)
{
+#ifdef HAVE_BATTERY_SWITCH
+ if ((power_input_status() & POWER_INPUT_BATTERY) == 0)
+ return -1;
+#endif
return battery_percent;
}
@@ -294,11 +309,13 @@ unsigned int battery_voltage(void)
return battery_millivolts;
}
+#ifndef TARGET_BATTERY_LEVEL_SAFE
/* Tells if the battery level is safe for disk writes */
bool battery_level_safe(void)
{
return battery_millivolts > battery_level_dangerous[battery_type];
}
+#endif
void set_poweroff_timeout(int timeout)
{
@@ -349,26 +366,24 @@ static int voltage_to_percent(int voltage, const short* table)
* when battery capacity / type settings are changed */
static int voltage_to_battery_level(int battery_millivolts)
{
+ const int state = powermgmt_filter_charge_state();
int level;
-#if CONFIG_CHARGING >= CHARGING_MONITOR
- if (charge_state == DISCHARGING) {
+ if (state == DISCHARGING) {
level = voltage_to_percent(battery_millivolts,
percent_to_volt_discharge[battery_type]);
}
- else if (charge_state == CHARGING) {
+#if CONFIG_CHARGING >= CHARGING_MONITOR
+ else if (state == CHARGING) {
/* battery level is defined to be < 100% until charging is finished */
level = MIN(voltage_to_percent(battery_millivolts,
percent_to_volt_charge), 99);
}
- else { /* in topoff/trickle charge, battery is by definition 100% full */
+ else {
+ /* in topoff/trickle charge, battery is by definition 100% full */
level = 100;
}
-#else
- /* always use the discharge table */
- level = voltage_to_percent(battery_millivolts,
- percent_to_volt_discharge[battery_type]);
-#endif /* CONFIG_CHARGING ... */
+#endif
return level;
}
@@ -381,7 +396,7 @@ static void battery_status_update(void)
/* discharging: remaining running time */
/* charging: remaining charging time */
#if CONFIG_CHARGING >= CHARGING_MONITOR
- if (charge_state == CHARGING) {
+ if (powermgmt_filter_charge_state() == CHARGING) {
powermgmt_est_runningtime_min = (100 - level) * battery_capacity * 60
/ 100 / (CURRENT_MAX_CHG - runcurrent());
}
@@ -431,15 +446,10 @@ static void handle_auto_poweroff(void)
}
#endif
-#ifndef NO_LOW_BATTERY_SHUTDOWN
- /* switch off unit if battery level is too low for reliable operation */
- if(battery_millivolts < battery_level_shutoff[battery_type]) {
- if(!shutdown_timeout) {
- backlight_on();
- sys_poweroff();
- }
+ if( !shutdown_timeout && query_force_shutdown()) {
+ backlight_on();
+ sys_poweroff();
}
-#endif
if(timeout &&
#if CONFIG_TUNER && !defined(BOOTLOADER)
@@ -546,6 +556,18 @@ static void power_thread_rtc_process(void)
}
#endif
+#ifndef TARGET_QUERY_FORCE_SHUTDOWN
+bool query_force_shutdown(void)
+{
+#ifndef NO_LOW_BATTERY_SHUTDOWN
+ /* switch off unit if battery level is too low for reliable operation */
+ return battery_millivolts < battery_level_shutoff[battery_type];
+#else
+ return false;
+#endif
+}
+#endif /* TARGET_QUERY_FORCE_SHUTDOWN */
+
/*
* This power thread maintains a history of battery voltage
* and implements a charging algorithm.
@@ -896,6 +918,18 @@ static inline void charging_algorithm_close(void)
}
#endif
}
+#elif CONFIG_CHARGING == CHARGING_TARGET
+extern void charging_algorithm_big_step(void);
+extern void charging_algorithm_small_step(void);
+extern void charging_algorithm_close(void);
+
+void set_filtered_battery_voltage(int millivolts)
+{
+ avgbat = millivolts * BATT_AVE_SAMPLES;
+ battery_millivolts = millivolts;
+ battery_status_update();
+}
+
#else
#define BATT_AVE_SAMPLES 128 /* slw filter constant for all others */
@@ -961,12 +995,12 @@ bool power_input_present(void)
* While we are waiting for the time to expire, we average the battery
* voltages.
*/
-static void power_thread_sleep(int ticks)
+void power_thread_sleep(int ticks)
{
- int small_ticks;
-
- while (ticks > 0) {
+ long tick_return = current_tick + ticks;
+ do
+ {
#if CONFIG_CHARGING
/*
* Detect charger plugged/unplugged transitions. On a plugged or
@@ -979,7 +1013,8 @@ static void power_thread_sleep(int ticks)
case NO_CHARGER:
case CHARGER_UNPLUGGED:
charger_input_state = CHARGER_PLUGGED;
- return;
+ tick_return = current_tick;
+ goto do_small_step; /* Algorithm should see transition */
case CHARGER_PLUGGED:
queue_broadcast(SYS_CHARGER_CONNECTED, 0);
last_sent_battery_level = 0;
@@ -1000,19 +1035,23 @@ static void power_thread_sleep(int ticks)
case CHARGER_PLUGGED:
case CHARGER:
charger_input_state = CHARGER_UNPLUGGED;
- return;
+ tick_return = current_tick;
+ goto do_small_step; /* Algorithm should see transition */
}
}
#endif /* CONFIG_CHARGING */
- small_ticks = MIN(HZ/2, ticks);
- sleep(small_ticks);
- ticks -= small_ticks;
+ ticks = tick_return - current_tick;
+
+ if (ticks > 0) {
+ ticks = MIN(HZ/2, ticks);
+ sleep(ticks);
+ }
/* If the power off timeout expires, the main thread has failed
to shut down the system, and we need to force a power off */
if(shutdown_timeout) {
- shutdown_timeout -= small_ticks;
+ shutdown_timeout -= MAX(ticks, 1);
if(shutdown_timeout <= 0)
power_off();
}
@@ -1024,9 +1063,13 @@ static void power_thread_sleep(int ticks)
/*
* Do a digital exponential filter. We don't sample the battery if
* the disk is spinning unless we are in USB mode (the disk will most
- * likely always be spinning in USB mode).
+ * likely always be spinning in USB mode) or charging.
*/
- if (!storage_disk_is_active() || usb_inserted()) {
+ if (!storage_disk_is_active() || usb_inserted()
+#if CONFIG_CHARGING >= CHARGING_MONITOR
+ || charger_input_state == CHARGER
+#endif
+ ) {
avgbat += battery_adc_voltage() - (avgbat / BATT_AVE_SAMPLES);
/*
* battery_millivolts is the millivolt-scaled filtered battery value.
@@ -1047,17 +1090,20 @@ static void power_thread_sleep(int ticks)
/* update battery status every time an update is available */
battery_status_update();
-#ifndef NO_LOW_BATTERY_SHUTDOWN
- if (!shutdown_timeout &&
- (battery_millivolts < battery_level_shutoff[battery_type]))
+ if (!shutdown_timeout && query_force_shutdown()) {
sys_poweroff();
- else
-#endif
+ }
+ else {
avgbat += battery_millivolts - (avgbat / BATT_AVE_SAMPLES);
+ }
}
+#if CONFIG_CHARGING
+ do_small_step:
+#endif
charging_algorithm_small_step();
}
+ while (TIME_BEFORE(current_tick, tick_return));
}
static void power_thread(void)
@@ -1074,7 +1120,7 @@ static void power_thread(void)
#ifdef HAVE_DISK_STORAGE /* this adjustment is only needed for HD based */
/* The battery voltage is usually a little lower directly after
turning on, because the disk was used heavily. Raise it by 5% */
-#ifdef HAVE_CHARGING
+#if CONFIG_CHARGING
if(!charger_inserted()) /* only if charger not connected */
#endif
avgbat += (percent_to_volt_discharge[battery_type][6] -
@@ -1095,6 +1141,10 @@ static void power_thread(void)
battery_percent += (battery_percent < 100);
}
+#if CONFIG_CHARGING == CHARGING_TARGET
+ powermgmt_init_target();
+#endif
+
while (1)
{
/* rotate the power history */
@@ -1121,6 +1171,7 @@ void powermgmt_init(void)
#endif /* SIMULATOR */
+#ifndef BOOTLOADER
void sys_poweroff(void)
{
#ifndef BOOTLOADER
@@ -1142,6 +1193,7 @@ void sys_poweroff(void)
queue_broadcast(SYS_POWEROFF, 0);
#endif /* BOOTLOADER */
}
+#endif
void cancel_shutdown(void)
{
diff --git a/firmware/target/arm/imx31/debug-imx31.c b/firmware/target/arm/imx31/debug-imx31.c
index df2489e..b72390c 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)
MC13783_INTERRUPT_SENSE0,
MC13783_INTERRUPT_STATUS1,
MC13783_INTERRUPT_SENSE1,
+ MC13783_CHARGER,
MC13783_RTC_TIME,
MC13783_RTC_ALARM,
MC13783_RTC_DAY,
@@ -151,6 +152,7 @@ bool __dbg_ports(void)
"Int Sense0",
"Int Stat1 ",
"Int Sense1",
+ "Charger ",
"RTC Time ",
"RTC Alarm ",
"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 85ef15b..3c66c42 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)
return (channel & 4) ? MC13783_ADD2r(data) : MC13783_ADD1r(data);
}
+bool adc_enable_channel(int channel, bool enable)
+{
+ uint32_t bit, mask;
+
+ switch (channel)
+ {
+ case ADC_CHARGER_CURRENT:
+ mask = MC13783_CHRGICON;
+ break;
+
+ case ADC_BATTERY_TEMP:
+ mask = MC13783_RTHEN;
+ break;
+
+ default:
+ return false;
+ }
+
+ bit = enable ? mask : 0;
+
+ return mc13783_write_masked(MC13783_ADC0, bit, mask)
+ != MC13783_DATA_ERROR;
+}
+
/* Called by mc13783 interrupt thread when conversion is complete */
void adc_done(void)
{
@@ -98,9 +122,9 @@ void adc_init(void)
/* Init so first reads get data */
last_adc_read[0] = last_adc_read[1] = current_tick-1;
- /* Enable increment-by-read, thermistor, charge current */
- mc13783_write(MC13783_ADC0, MC13783_ADINC2 | MC13783_ADINC1 |
- MC13783_RTHEN | MC13783_CHRGICON);
+ /* Enable increment-by-read, turn off extra conversions. */
+ mc13783_write(MC13783_ADC0, MC13783_ADINC2 | MC13783_ADINC1);
+
/* Enable ADC, set multi-channel mode */
mc13783_write(MC13783_ADC1, MC13783_ADEN);
diff --git a/firmware/target/arm/imx31/gigabeat-s/adc-target.h b/firmware/target/arm/imx31/gigabeat-s/adc-target.h
index efa665d..00027e0 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 @@
#define ADC_READ_ERROR 0xFFFF
void adc_done(void);
+/* Enable conversion of specified channel (if switchoff is possible) */
+bool adc_enable_channel(int channel, bool enable);
+
+/* Implemented in powermgmt-imx31.c */
int battery_adc_charge_current(void);
-unsigned int battery_adc_temp(void);
+int battery_adc_temp(void);
+unsigned int application_supply_adc_voltage(void);
+unsigned int chrgraw_adc_voltage(void);
+unsigned int cccv_regulator_dissipation(void);
#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 e623811..fc9ad71 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 @@
#include "button-target.h"
#include "usb-target.h"
#include "power-imx31.h"
+#include "powermgmt-target.h"
/* Gigabeat S definitions for static MC13783 event registration */
@@ -45,26 +46,26 @@ static const struct mc13783_event mc13783_events[] =
.mask = MC13783_ONOFD1M,
.callback = button_power_event,
},
-#ifdef HAVE_HEADPHONE_DETECTION
- [MC13783_ONOFD2_EVENT] = /* Headphone jack */
- {
- .set = MC13783_EVENT_SET1,
- .mask = MC13783_ONOFD2M,
- .callback = headphone_detect_event,
- },
-#endif
- [MC13783_CHGDET_EVENT] = /* Charger detection */
+ [MC13783_SE1_EVENT] = /* Main charger detection */
{
.set = MC13783_EVENT_SET0,
- .mask = MC13783_CHGDETM,
- .callback = charger_detect_event,
+ .mask = MC13783_SE1M,
+ .callback = charger_main_detect_event,
},
- [MC13783_USB4V4_EVENT] = /* USB insertion */
+ [MC13783_USB_EVENT] = /* USB insertion/USB charger detection */
{
.set = MC13783_EVENT_SET0,
.mask = MC13783_USBM,
.callback = usb_connect_event,
},
+#ifdef HAVE_HEADPHONE_DETECTION
+ [MC13783_ONOFD2_EVENT] = /* Headphone jack */
+ {
+ .set = MC13783_EVENT_SET1,
+ .mask = MC13783_ONOFD2M,
+ .callback = headphone_detect_event,
+ },
+#endif
};
const 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 26ab3c6..2ae3be1 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
#ifdef HAVE_HEADPHONE_DETECTION
MC13783_ONOFD2_EVENT, /* Headphone jack */
#endif
- MC13783_CHGDET_EVENT, /* Charger detection */
- MC13783_USB4V4_EVENT, /* USB insertion */
+ MC13783_SE1_EVENT, /* Main charger detection */
+ MC13783_USB_EVENT, /* USB insertion */
};
#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 17008ce..39724c7 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 @@
****************************************************************************/
#include "config.h"
#include "system.h"
+#include "usb.h"
+#include "usb_core.h"
#include "power.h"
#include "power-imx31.h"
#include "backlight.h"
@@ -29,35 +31,47 @@
#include "i2c-imx31.h"
extern struct i2c_node si4700_i2c_node;
+static unsigned int power_status = POWER_INPUT_NONE;
-static bool charger_detect = false;
-
-/* This is called from the mc13783 interrupt thread */
-void charger_detect_event(void)
-{
- charger_detect =
- mc13783_read(MC13783_INTERRUPT_SENSE0) & MC13783_CHGDETS;
-}
-
+/* Detect which power sources are present. */
unsigned int power_input_status(void)
{
- unsigned int status = POWER_INPUT_NONE;
+ unsigned int status = power_status;
- if ((GPIO3_DR & (1 << 20)) != 0)
+ if (GPIO3_DR & (1 << 20))
status |= POWER_INPUT_BATTERY;
- if (charger_detect)
- status |= POWER_INPUT_MAIN_CHARGER;
+ if (usb_allowed_current() < 500)
+ {
+ /* ACK that USB is connected but NOT chargeable */
+ status &= ~(POWER_INPUT_USB_CHARGER & POWER_INPUT_CHARGER);
+ }
return status;
}
-/* Returns true if the unit is charging the batteries. */
-bool charging_state(void)
+/* Detect changes in presence of the AC adaptor. */
+void charger_main_detect_event(void)
{
- return false;
+ if (mc13783_read(MC13783_INTERRUPT_SENSE0) & MC13783_SE1S)
+ power_status |= POWER_INPUT_MAIN_CHARGER;
+ else
+ power_status &= ~POWER_INPUT_MAIN_CHARGER;
}
+/* Detect changes in USB bus power. Called from usb connect event handler. */
+void charger_usb_detect_event(int status)
+{
+ /* USB plugged does not imply charging is possible or even
+ * powering the device to maintain the battery. */
+ if (status == USB_INSERTED)
+ power_status |= POWER_INPUT_USB_CHARGER;
+ else
+ power_status &= ~POWER_INPUT_USB_CHARGER;
+}
+
+/* charging_state is implemented in powermgmt-imx31.c */
+
void ide_power_enable(bool on)
{
if (!on)
@@ -129,9 +143,8 @@ void power_off(void)
void power_init(void)
{
/* Poll initial state */
- charger_detect_event();
+ charger_main_detect_event();
/* Enable detect event */
- mc13783_enable_event(MC13783_CHGDET_EVENT);
+ mc13783_enable_event(MC13783_SE1_EVENT);
}
-
diff --git a/firmware/target/arm/imx31/gigabeat-s/power-imx31.h b/firmware/target/arm/imx31/gigabeat-s/power-imx31.h
index 86ba4ac..9294de1 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 @@
#ifndef POWER_IMX31_H
#define POWER_IMX31_H
-void charger_detect_event(void);
+void charger_main_detect_event(void);
+void charger_usb_detect_event(int status);
#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 796c781..c44e7cc 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 @@
* \/ \/ \/ \/ \/
* $Id$
*
- * Copyright (C) 2002 by Heikki Hannikainen, Uwe Freese
- * Revisions copyright (C) 2005 by Gerald Van Baren
+ * Copyright (c) 2008 by Michael Sevakis
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -19,12 +18,40 @@
* KIND, either express or implied.
*
****************************************************************************/
-
-/* FIXME: This is just the Gigabeat F/X file with a different name... */
-
+#include <stdlib.h>
#include "config.h"
+#include "system.h"
+#include "thread.h"
+#include "mc13783.h"
#include "adc.h"
#include "powermgmt.h"
+#include "power.h"
+#include "power-imx31.h"
+
+/* TODO: Battery tests to get the right values! */
+const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT] =
+{
+ 3450
+};
+
+const unsigned short battery_level_shutoff[BATTERY_TYPES_COUNT] =
+{
+ 3400
+};
+
+/* voltages (millivolt) of 0%, 10%, ... 100% when charging disabled */
+const unsigned short percent_to_volt_discharge[BATTERY_TYPES_COUNT][11] =
+{
+ /* Toshiba Gigabeat Li Ion 830mAH figured from discharge curve */
+ { 3480, 3550, 3590, 3610, 3630, 3650, 3700, 3760, 3800, 3910, 3990 },
+};
+
+/* voltages (millivolt) of 0%, 10%, ... 100% when charging enabled */
+const unsigned short percent_to_volt_charge[11] =
+{
+ /* Toshiba Gigabeat Li Ion 830mAH */
+ 3480, 3550, 3590, 3610, 3630, 3650, 3700, 3760, 3800, 3910, 3990
+};
/**
* Fixed-point natural log
@@ -56,31 +83,6 @@ static long flog(int x)
return y;
}
-
-const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT] =
-{
- 3450
-};
-
-const unsigned short battery_level_shutoff[BATTERY_TYPES_COUNT] =
-{
- 3400
-};
-
-/* voltages (millivolt) of 0%, 10%, ... 100% when charging disabled */
-const unsigned short percent_to_volt_discharge[BATTERY_TYPES_COUNT][11] =
-{
- /* Toshiba Gigabeat Li Ion 830mAH figured from discharge curve */
- { 3480, 3550, 3590, 3610, 3630, 3650, 3700, 3760, 3800, 3910, 3990 },
-};
-
-/* voltages (millivolt) of 0%, 10%, ... 100% when charging enabled */
-const unsigned short percent_to_volt_charge[11] =
-{
- /* Toshiba Gigabeat Li Ion 830mAH */
- 3480, 3550, 3590, 3610, 3630, 3650, 3700, 3760, 3800, 3910, 3990
-};
-
/* Returns battery voltage from ADC [millivolts] */
unsigned int battery_adc_voltage(void)
{
@@ -88,6 +90,17 @@ unsigned int battery_adc_voltage(void)
return ((adc_read(ADC_BATTERY) * 2303) >> 10) + 2400;
}
+/* Returns the application supply voltage from ADC [millvolts] */
+unsigned int application_supply_adc_voltage(void)
+{
+ return ((adc_read(ADC_APPLICATION_SUPPLY) * 2303) >> 10) + 2400;
+}
+
+unsigned int chrgraw_adc_voltage(void)
+{
+ return (adc_read(ADC_CHARGER_VOLTAGE) * 23023) >> 10;
+}
+
/* Returns battery charge current from ADC [milliamps] */
int battery_adc_charge_current(void)
{
@@ -95,11 +108,31 @@ int battery_adc_charge_current(void)
* Negative reading = battery to charger terminal
* ADC reading -512-511 = -2875mA-2875mA */
unsigned int value = adc_read(ADC_CHARGER_CURRENT);
- return (((int)value << 22) >> 22) * 2881 >> 9;
+ int I;
+
+ if (value == ADC_READ_ERROR)
+ return INT_MIN;
+
+ I = ((((int32_t)value << 22) >> 22) * 2881) >> 9;
+ return ILEVEL_ADJUST_IN(I);
+}
+
+/* Estimate power dissipation in the charge path regulator in mW. */
+unsigned int cccv_regulator_dissipation(void)
+{
+ /* BATTISNS is shorted to BATT so we don't need to use the
+ * battery current reading. */
+ int chrgraw = (adc_read(ADC_CHARGER_VOLTAGE) * 230225) >> 10;
+ int batt = ((adc_read(ADC_BATTERY) * 23023) >> 10) + 24000;
+ int ichrgsn = adc_read(ADC_CHARGER_CURRENT);
+ ichrgsn = ((((int32_t)ichrgsn << 22) >> 22) * 2881) >> 9;
+ ichrgsn = abs(ichrgsn);
+
+ return (chrgraw - ichrgsn - batt)*ILEVEL_ADJUST_IN(ichrgsn) / 10000;
}
/* Returns battery temperature from ADC [deg-C] */
-unsigned int battery_adc_temp(void)
+int battery_adc_temp(void)
{
unsigned int value = adc_read(ADC_BATTERY_TEMP);
/* E[volts] = value * 2.3V / 1023
@@ -117,10 +150,784 @@ unsigned int battery_adc_temp(void)
* Fixed-point output matches the floating-point version for each ADC
* value.
*/
- int R = 2070000 * value;
- long long ln = flog(R) + 83196;
- long long t0 = 425890304133ll;
- long long t1 = 1000000*ln;
- long long t3 = ln*ln*ln / 13418057;
- return ((32754211579494400ll / (t0 + t1 + t3)) - 27315) / 100;
+ if (value > 0)
+ {
+ int R = 2070000 * value;
+ long long ln = flog(R) + 83196;
+ long long t0 = 425890304133ll;
+ long long t1 = 1000000*ln;
+ long long t3 = ln*ln*ln / 13418057;
+ return ((32754211579494400ll / (t0 + t1 + t3)) - 27315) / 100;
+ }
+
+ return INT_MIN;
+}
+
+/** Charger control **/
+
+/* All code has a preference for the main charger being connected over
+ * USB. USB is considered in the algorithm only if it is the sole source. */
+static uint32_t int_sense0 = 0; /* Interrupt Sense 0 bits */
+static unsigned int power_status = POWER_INPUT_NONE; /* Detect input changes */
+static int charger_total_timer = 0; /* Total allowed charging time */
+static int icharger_ave = 0; /* Filtered charging current */
+static bool charger_close = false; /* Shutdown notification */
+static bool service_wdt = true; /* Service the watchdog timer, if things
+ go very wrong, cease and shut down. */
+static uint32_t charger_setting = 0; /* Current ICHRG and VCHRG regulator
+ * setting (register bits) */
+#define CHARGER_ADJUST ((uint32_t)-1)/* Force change in regulator setting */
+static int autorecharge_counter = 0 ; /* Battery < threshold debounce */
+static int chgcurr_timer = 0; /* Countdown to CHGCURR error */
+#define AUTORECHARGE_COUNTDOWN (10*2) /* 10s debounce */
+#define WATCHDOG_TIMEOUT (10*2) /* If not serviced, poweroff in 10s */
+#define CHGCURR_TIMEOUT (2*2) /* 2s debounce */
+
+/* Temperature monitoring */
+static enum
+{
+ TEMP_STATE_NORMAL = 0, /* Within range */
+ TEMP_STATE_WAIT = 1, /* Went out of range, wait to come back */
+ TEMP_LOW_LIMIT = 0, /* Min temp */
+ TEMP_HIGH_LIMIT = 1, /* Max temp */
+} temp_state = TEMP_STATE_NORMAL;
+
+/* Set power thread priority for charging mode or not */
+static inline void charging_set_thread_priority(bool charging)
+{
+#ifdef HAVE_PRIORITY_SCHEDULING
+ thread_set_priority(THREAD_ID_CURRENT,
+ charging ? PRIORITY_REALTIME : PRIORITY_SYSTEM);
+#endif
+ (void)charging;
+}
+
+/* Update filtered charger current - exponential moving average */
+static bool charger_current_filter_step(void)
+{
+ int value = battery_adc_charge_current();
+
+ if (value == ADC_READ_ERROR)
+ return false;
+
+ icharger_ave += value - (icharger_ave / ICHARGER_AVE_SAMPLES);
+ return true;
+}
+
+/* Return true if the main charger is connected. */
+static bool main_charger_connected(void)
+{
+ return (power_status &
+ POWER_INPUT_MAIN_CHARGER &
+ POWER_INPUT_CHARGER) != 0;
+}
+
+/* Return the voltage level which should automatically trigger
+ * another recharge cycle based upon which power source is available.
+ * Assumes at least one is. */
+static unsigned int auto_recharge_voltage(void)
+{
+ if (main_charger_connected())
+ return BATT_VAUTO_RECHARGE;
+ else
+ return BATT_USB_VAUTO_RECHARGE;
+}
+
+#ifndef NO_LOW_BATTERY_SHUTDOWN
+/* Return greater of supply (BP) or filtered battery voltage. */
+static unsigned int input_millivolts(void)
+{
+ unsigned int app_millivolts = application_supply_adc_voltage();
+ unsigned int bat_millivolts = battery_voltage();
+
+ return MAX(app_millivolts, bat_millivolts);
+}
+#endif
+
+/* Get smoothed readings for initializing filtered data. */
+static int stat_battery_reading(int type)
+{
+ int high = INT_MIN, low = INT_MAX;
+ int value = 0;
+ int i;
+
+ for (i = 0; i < 7; i++)
+ {
+ int reading = ADC_READ_ERROR;
+
+ sleep(2); /* Get unique readings */
+
+ switch (type)
+ {
+ case ADC_BATTERY:
+ reading = battery_adc_voltage();
+ break;
+
+ case ADC_CHARGER_CURRENT:
+ reading = battery_adc_charge_current();
+ break;
+ }
+
+ if (reading == ADC_READ_ERROR)
+ return INT_MIN;
+
+ if (reading > high)
+ high = reading;
+
+ if (reading < low)
+ low = reading;
+
+ value += reading;
+ }
+
+ /* Discard extremes */
+ return (value - high - low) / 5;
+}
+
+/* Update filtered battery voltage instead of waiting for filter
+ * decay. */
+static bool update_filtered_battery_voltage(void)
+{
+ int millivolts = stat_battery_reading(ADC_BATTERY);
+
+ if (millivolts != INT_MIN)
+ {
+ set_filtered_battery_voltage(millivolts);
+ return true;
+ }
+
+ return false;
+}
+
+/* Sets the charge current limit based upon state. charge_state should be
+ * set before calling. */
+static bool adjust_charger_current(void)
+{
+ static const uint8_t charger_bits[][2] =
+ {
+ [DISCHARGING] =
+ {
+ /* These are actually zeros but reflect this setting */
+ MC13783_ICHRG_0MA | MC13783_VCHRG_4_050V,
+ MC13783_ICHRG_0MA | MC13783_VCHRG_4_050V,
+ },
+ /* Main(+USB): Charge slowly from the adapter until voltage is
+ * sufficient for normal charging.
+ *
+ * USB: The truth is that things will probably not make it this far.
+ * Cover the case, just in case the disk isn't used and it is
+ * manageable. */
+ [TRICKLE] =
+ {
+ BATTERY_ITRICKLE | BATTERY_VCHARGING,
+ BATTERY_ITRICKLE_USB | BATTERY_VCHARGING
+ },
+ [TOPOFF] =
+ {
+ BATTERY_IFAST | BATTERY_VCHARGING,
+ BATTERY_IFAST_USB | BATTERY_VCHARGING
+ },
+ [CHARGING] =
+ {
+ BATTERY_IFAST | BATTERY_VCHARGING,
+ BATTERY_IFAST_USB | BATTERY_VCHARGING
+ },
+ /* Must maintain battery when on USB power only - utterly nasty
+ * but true and something retailos does (it will even end up charging
+ * the battery but not reporting that it is doing so).
+ * Float lower than MAX - could end up slightly discharging after
+ * a full charge but this is safer than maxing it out. */
+ [CHARGING+1] =
+ {
+ BATTERY_IFLOAT_USB | BATTERY_VFLOAT_USB,
+ BATTERY_IMAINTAIN_USB | BATTERY_VMAINTAIN_USB
+ },
+#if 0
+ /* Slower settings to so that the linear regulator doesn't dissipate
+ * an excessive amount of power when coming out of precharge state. */
+ [CHARGING+2] =
+ {
+ BATTERY_ISLOW | BATTERY_VCHARGING,
+ BATTERY_ISLOW_USB | BATTEYR_VCHARGING
+ },
+#endif
+ };
+
+ bool success = false;
+ int usb_select;
+ uint32_t i;
+
+ usb_select = ((power_status & POWER_INPUT) == POWER_INPUT_USB)
+ ? 1 : 0;
+
+ if (charge_state == DISCHARGING && usb_select == 1)
+ {
+ /* USB-only, DISCHARGING, = maintaining battery */
+ int select = (power_status & POWER_INPUT_CHARGER) ? 0 : 1;
+ charger_setting = charger_bits[CHARGING+1][select];
+ }
+ else
+ {
+ /* Take very good care not to write garbage. */
+ int state = charge_state;
+
+ if (state < DISCHARGING || state > CHARGING)
+ state = DISCHARGING;
+
+ charger_setting = charger_bits[state][usb_select];
+ }
+
+ if (charger_setting != 0)
+ {
+ charging_set_thread_priority(true);
+
+ /* Turn regulator logically ON. Hardware may still override. */
+ i = mc13783_write_masked(MC13783_CHARGER,
+ charger_setting | MC13783_CHRGRAWPDEN,
+ MC13783_ICHRG | MC13783_VCHRG |
+ MC13783_CHRGRAWPDEN);
+
+ if (i != MC13783_DATA_ERROR)
+ {
+ int icharger;
+
+ /* Enable charge current conversion */
+ adc_enable_channel(ADC_CHARGER_CURRENT, true);
+
+ /* Charge path regulator turn on takes ~100ms max. */
+ sleep(HZ/10);
+
+ icharger = stat_battery_reading(ADC_CHARGER_CURRENT);
+
+ if (icharger != INT_MIN)
+ {
+ icharger_ave = icharger * ICHARGER_AVE_SAMPLES;
+
+ if (update_filtered_battery_voltage())
+ return true;
+ }
+ }
+
+ /* Force regulator OFF. */
+ charge_state = CHARGE_STATE_ERROR;
+ }
+
+ /* Turn regulator OFF. */
+ icharger_ave = 0;
+ i = mc13783_write_masked(MC13783_CHARGER, charger_bits[0][0],
+ MC13783_ICHRG | MC13783_VCHRG |
+ MC13783_CHRGRAWPDEN);
+
+ if (MC13783_DATA_ERROR == i)
+ {
+ /* Failed. Force poweroff by not servicing the watchdog. */
+ service_wdt = false;
+ }
+ else if (0 == charger_setting)
+ {
+ /* Here because OFF was requested state */
+ success = true;
+ }
+
+ charger_setting = 0;
+
+ adc_enable_channel(ADC_CHARGER_CURRENT, false);
+ update_filtered_battery_voltage();
+ charging_set_thread_priority(false);
+
+ return success;
+}
+
+/* Stop the charger - if USB only then the regulator will not really be
+ * turned off. ERROR or DISABLED will turn it off however. */
+static void stop_charger(void)
+{
+ charger_total_timer = 0;
+
+ if (charge_state > DISCHARGING)
+ charge_state = DISCHARGING;
+
+ adjust_charger_current();
+}
+
+/* Return OK if it is acceptable to start the regulator. */
+static bool charging_ok(void)
+{
+ bool ok = charge_state >= DISCHARGING; /* Not an error condition? */
+
+ if (ok)
+ {
+ /* Is the battery even connected? */
+ ok = (power_status & POWER_INPUT_BATTERY) != 0;
+ }
+
+ if (ok)
+ {
+ /* No tolerance for any over/under temp - wait for it to
+ * come back into safe range. */
+ static const signed char temp_ranges[2][2] =
+ {
+ { 0, 45 }, /* Temperature range before beginning charging */
+ { 5, 40 }, /* Temperature range after out-of-range detected */
+ };
+
+ int temp = battery_adc_temp();
+ const signed char *range = temp_ranges[temp_state];
+
+ ok = temp >= range[TEMP_LOW_LIMIT] &&
+ temp <= range[TEMP_HIGH_LIMIT];
+
+ switch (temp_state)
+ {
+ case TEMP_STATE_NORMAL:
+ if (!ok)
+ temp_state = TEMP_STATE_WAIT;
+ break;
+
+ case TEMP_STATE_WAIT:
+ if (ok)
+ temp_state = TEMP_STATE_NORMAL;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (ok)
+ {
+ /* Any events that should stop the regulator? */
+
+ /* Overvoltage at CHRGRAW? */
+ ok = (int_sense0 & MC13783_CHGOVS) == 0;
+
+ if (ok)
+ {
+ /* CHGCURR sensed? */
+ ok = (int_sense0 & MC13783_CHGCURRS) != 0;
+
+ if (!ok)
+ {
+ /* Debounce transient states */
+ if (chgcurr_timer > 0)
+ {
+ chgcurr_timer--;
+ ok = true;
+ }
+ }
+ else
+ {
+ chgcurr_timer = CHGCURR_TIMEOUT;
+ }
+ }
+
+ /* Charger may need to be reinserted */
+ if (!ok)
+ charge_state = CHARGE_STATE_ERROR;
+ }
+
+ if (charger_setting != 0)
+ {
+ if (ok)
+ {
+ /* Watch to not overheat FET (nothing should go over about 1012.7mW).
+ * Trying a higher voltage AC adapter can work (up to 6.90V) but
+ * we'll just reject that. Reducing current for adapters that bring
+ * CHRGRAW to > 4.900V is another possible action. */
+ ok = cccv_regulator_dissipation() < 1150;
+ if (!ok)
+ charge_state = CHARGE_STATE_ERROR;
+ }
+
+ if (!ok)
+ {
+ int state = charge_state;
+
+ if (state > DISCHARGING)
+ state = DISCHARGING;
+
+ /* Force off for all states including maintaining the battery level
+ * on USB. */
+ charge_state = CHARGE_STATE_ERROR;
+ stop_charger();
+ charge_state = state;
+ }
+ }
+
+ return ok;
+}
+
+void powermgmt_init_target(void)
+{
+#ifdef IMX31_ALLOW_CHARGING
+ const uint32_t regval_w =
+ MC13783_VCHRG_4_050V | MC13783_ICHRG_0MA |
+ MC13783_ICHRGTR_0MA | MC13783_OVCTRL_6_90V;
+
+ /* Use watchdog to shut system down if we lose control of the charging
+ * hardware. */
+ watchdog_init(WATCHDOG_TIMEOUT);
+
+ mc13783_write(MC13783_CHARGER, regval_w);
+
+ if (mc13783_read(MC13783_CHARGER) == regval_w)
+ {
+ /* Divide CHRGRAW input by 10 */
+ mc13783_clear(MC13783_ADC0, MC13783_CHRGRAWDIV);
+ /* Turn off BATTDETB. It's worthless on MESx0V since the battery
+ * isn't removable (nor the thermistor). */
+ mc13783_clear(MC13783_POWER_CONTROL0, MC13783_BATTDETEN);
+ }
+ else
+ {
+ /* Register has the wrong value - set error condition and disable
+ * since something is wrong. */
+ charge_state = CHARGE_STATE_DISABLED;
+ stop_charger();
+ }
+#else
+ /* Disable charger use */
+ charge_state = CHARGE_STATE_DISABLED;
+#endif
+}
+
+/* Returns CHARGING or DISCHARGING since that's all we really do. */
+int powermgmt_filter_charge_state(void)
+{
+ switch(charge_state)
+ {
+ case TRICKLE:
+ case TOPOFF:
+ case CHARGING:
+ return CHARGING;
+ default:
+ return DISCHARGING;
+ }
+}
+
+/* Returns true if the unit is charging the batteries. */
+bool charging_state(void)
+{
+ switch (charge_state)
+ {
+ case TRICKLE:
+ case TOPOFF:
+ case CHARGING:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/* Filtered battery charge current */
+int battery_charge_current(void)
+{
+ return icharger_ave / ICHARGER_AVE_SAMPLES;
+}
+
+bool query_force_shutdown(void)
+{
+#ifndef NO_LOW_BATTERY_SHUTDOWN
+ return input_millivolts() < battery_level_shutoff[0];
+#else
+ return false;
+#endif
+}
+
+bool battery_level_safe(void)
+{
+#ifndef NO_LOW_BATTERY_SHUTDOWN
+ return input_millivolts() > battery_level_dangerous[0];
+#else
+ return true;
+#endif
+}
+
+static void charger_plugged(void)
+{
+ adc_enable_channel(ADC_BATTERY_TEMP, true);
+ autorecharge_counter = -1;
+}
+
+static void charger_unplugged(void)
+{
+ /* Charger pulled - turn off current sources (though hardware
+ * will have done that anyway). */
+ if (charge_state > CHARGE_STATE_DISABLED)
+ {
+ /* Reset state and clear any error. If disabled, the charger
+ * will not have been started or will have been stopped already. */
+ stop_charger();
+ charge_state = DISCHARGING;
+ }
+
+ /* Might need to reevaluate these bits in charger_none. */
+ power_status &= ~(POWER_INPUT | POWER_INPUT_CHARGER);
+ temp_state = TEMP_STATE_NORMAL;
+ autorecharge_counter = 0;
+ chgcurr_timer = 0;
+
+ adc_enable_channel(ADC_BATTERY_TEMP, false);
+}
+
+static void charger_none(void)
+{
+ unsigned int pwr = power_input_status();
+
+ if (power_status != pwr)
+ {
+ /* If battery switch state changed, reset filter. */
+ if ((power_status ^ pwr) & POWER_INPUT_BATTERY)
+ update_filtered_battery_voltage();
+
+ power_status = pwr;
+
+ if (charge_state == CHARGE_STATE_DISABLED)
+ return;
+
+ if ((pwr & (POWER_INPUT | POWER_INPUT_CHARGER)) == POWER_INPUT_USB)
+ {
+ /* USB connected but not configured. Maintain battery to the
+ * greatest degree possible. It probably won't be enough but the
+ * discharge won't be so severe. */
+ charger_plugged();
+ charger_setting = CHARGER_ADJUST;
+ }
+ else
+ {
+ charger_unplugged();
+ power_status = pwr; /* Restore status */
+ }
+ }
+ else if (charger_setting != 0)
+ {
+ /* Maintaining - keep filter going and check charge state */
+ int_sense0 = mc13783_read(MC13783_INTERRUPT_SENSE0);
+
+ if (!charger_current_filter_step())
+ {
+ /* Failed to read current */
+ charge_state = CHARGE_STATE_ERROR;
+ }
+
+ charging_ok();
+ }
+}
+
+static void charger_control(void)
+{
+ unsigned int pwr = power_input_status();
+
+ if (power_status != pwr)
+ {
+ unsigned int changed = power_status ^ pwr;
+
+ power_status = pwr;
+
+ /* If battery switch state changed, reset filter. */
+ if (changed & POWER_INPUT_BATTERY)
+ update_filtered_battery_voltage();
+
+ if (charger_setting != 0)
+ charger_setting = CHARGER_ADJUST;
+
+ if (charge_state == DISCHARGING)
+ {
+ if (main_charger_connected())
+ {
+ /* If main is connected, ignore USB plugs. */
+ if (changed & POWER_INPUT_MAIN_CHARGER)
+ {
+ /* Main charger plugged - try charge */
+ autorecharge_counter = -1;
+ }
+ }
+ else if (pwr & POWER_INPUT_USB_CHARGER
+ & POWER_INPUT_CHARGER)
+ {
+ if (changed & POWER_INPUT_USB_CHARGER)
+ {
+ /* USB charger plugged - try charge */
+ autorecharge_counter = -1;
+ }
+ }
+ }
+ }
+
+ if (charger_setting != 0 && !charger_current_filter_step())
+ {
+ /* Failed to read current */
+ charge_state = CHARGE_STATE_ERROR;
+ }
+
+ int_sense0 = mc13783_read(MC13783_INTERRUPT_SENSE0);
+
+ if (!charging_ok())
+ return;
+
+ switch (charge_state)
+ {
+ case DISCHARGING:
+ {
+ /* Battery voltage may have dropped and a charge cycle should
+ * start again. Debounced. */
+ if (autorecharge_counter < 0)
+ {
+ /* Try starting a cycle now regardless of battery level to
+ * allow user to ensure the battery is topped off. It
+ * will soon turn off if already full. */
+ autorecharge_counter = 0;
+ }
+ else if (battery_voltage() > auto_recharge_voltage())
+ {
+ /* Still above threshold - reset counter */
+ autorecharge_counter = AUTORECHARGE_COUNTDOWN;
+ break;
+ }
+ else if (autorecharge_counter > 0)
+ {
+ /* Coundown to restart */
+ autorecharge_counter--;
+ break;
+ }
+
+ charging_set_thread_priority(true);
+
+ if (stat_battery_reading(ADC_BATTERY) < BATT_VTRICKLE_CHARGE)
+ {
+ /* Battery is deeply discharged - precharge at lower current. */
+ charge_state = TRICKLE;
+ }
+ else
+ {
+ /* Ok for fast charge */
+ charge_state = CHARGING;
+ }
+
+ charger_setting = CHARGER_ADJUST;
+ charger_total_timer = CHARGER_TOTAL_TIMER*60*2;
+ break;
+ } /* DISCHARGING: */
+
+ case TRICKLE: /* Very low - precharge */
+ {
+ if (battery_voltage() <= BATT_VTRICKLE_CHARGE)
+ break;
+
+ /* Switch to normal charge mode. */
+ charge_state = CHARGING;
+ charger_setting = CHARGER_ADJUST;
+ break;
+ } /* TRICKLE: */
+
+ case CHARGING: /* Constant-current stage */
+ case TOPOFF: /* Constant-voltage stage */
+ {
+ /* Reg. mode is more informative than an operational necessity. */
+ charge_state = (int_sense0 & MC13783_CCCVS) ? TOPOFF : CHARGING;
+
+ if (main_charger_connected())
+ {
+ /* Monitor and stop if current drops below threshold. */
+ if (battery_charge_current() > BATTERY_ICHARGE_COMPLETE)
+ break;
+ }
+ else
+ {
+ /* Accurate I-level can't be determined since device also
+ * powers through the I sense. This simply stops the reporting
+ * of charging but the regulator remains on. */
+ if (battery_voltage() <= BATT_USB_VSTOP)
+ break;
+ }
+
+ stop_charger();
+ break;
+ } /* CHARGING: TOPOFF: */
+
+ default:
+ break;
+ } /* switch */
+
+ /* Check if charger timer expired and stop it if so. */
+ if (charger_total_timer > 0 && --charger_total_timer == 0)
+ {
+ charge_state = CHARGE_STATE_ERROR;
+ stop_charger(); /* Time ran out - error */
+ }
+}
+
+/* Main charging algorithm - called from powermgmt.c */
+void charging_algorithm_small_step(void)
+{
+ if (service_wdt)
+ watchdog_service();
+
+ /* Switch by input state */
+ switch (charger_input_state)
+ {
+ case NO_CHARGER:
+ charger_none();
+ break;
+
+ case CHARGER_PLUGGED:
+ charger_plugged();
+ break;
+
+ case CHARGER:
+ charger_control();
+ break;
+
+ case CHARGER_UNPLUGGED:
+ charger_unplugged();
+ break;
+ } /* switch */
+
+ if (charger_close)
+ {
+ if (charge_state != CHARGE_STATE_DISABLED)
+ {
+ /* Disable starts while shutting down */
+ charge_state = CHARGE_STATE_DISABLED;
+ stop_charger();
+ }
+
+ charger_close = false;
+ return;
+ }
+
+ if (charger_setting != 0)
+ {
+ if ((mc13783_read(MC13783_CHARGER) & (MC13783_ICHRG | MC13783_VCHRG)) !=
+ charger_setting)
+ {
+ /* The hardware setting doesn't match. It could have turned the
+ * charger off in a race of plugging/unplugging or the setting
+ * was changed in one of the calls. */
+ adjust_charger_current();
+ }
+ }
+}
+
+void charging_algorithm_big_step(void)
+{
+ /* Sleep for one minute */
+ power_thread_sleep(HZ*60);
+}
+
+/* Disable the charger and prepare for poweroff - called off-thread so we
+ * signal the charging thread to prepare to quit. */
+void charging_algorithm_close(void)
+{
+ charger_close = true;
+
+ /* Power management thread will set it false again */
+ while (charger_close)
+ sleep(HZ/10);
+}
+
+#ifdef BOOTLOADER
+void sys_poweroff(void)
+{
}
+#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 0000000..8ad4af8
--- /dev/null
+++ b/firmware/target/arm/imx31/gigabeat-s/powermgmt-target.h
@@ -0,0 +1,114 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2008 by Michael Sevakis
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#ifndef POWERMGMT_TARGET_H
+#define POWERMGMT_TARGET_H
+
+/* Can't just run this code willy-nilly. Do not allow charger engagement
+ * without carefully verifying compatibility.
+ *
+ * Things to check:
+ * 1) Charge path configuration for the PMIC.
+ * 2) Correct thermistor reading
+ * 3) Accurate voltage readings
+ * 4) Accurate current sense for the charge path as the sense resistor may
+ * deviate from the 0.1 ohms assumed by the charge path regulator.
+ */
+#ifdef TOSHIBA_GIGABEAT_S
+/*
+ * Gigabeat S qualifications:
+ * 1) Setup for dual-supply mode with separate inputs and providing USB
+ * charging capability through external components.
+ * 2) Curve obtained experimentally - extreme deviation from "optimized"
+ * characteristics.
+ * 3) Verified at battery terminals - no deviation from datasheet formula.
+ * 4) 0.316 ohms <=?? - verified by comparitive current readings on device
+ * with ammeter readings and measurement of on-board components.
+ */
+#ifndef BOOTLOADER
+#define IMX31_ALLOW_CHARGING
+#endif
+
+#else
+#warning This iMX31 target requires validation of charging algorithm - charging disabled
+#endif
+
+#define BATT_VTRICKLE_CHARGE 2900 /* Must charge slowly */
+#define BATT_VSLOW_CHARGE 3500 /* Lower-current charge mode below
+ * this level */
+#define BATT_VAUTO_RECHARGE 4100 /* When to begin another cycle */
+#define BATT_USB_VAUTO_RECHARGE 4000 /* When to cycle with USB only */
+#define BATT_USB_VSTOP 4140 /* When to "stop" when USB only */
+#define BATT_TOO_LOW 2400 /* No battery? Short? Can't
+ read below 2400mV. */
+#define CHARGER_TOTAL_TIMER 300 /* minutes */
+
+/* .316 ohms is closest standard value as measured in 1% tolerance - adjust
+ * relative to .100 ohm which is what the PMIC is "tuned" for. */
+#define ILEVEL_ADJUST_IN(I) (100*(I) / 316)
+#define ILEVEL_ADJUST_OUT(I) (316*(I) / 100)
+
+/* Relative draw to battery capacity - adjusted for sense resistor */
+#define BATTERY_ICHARGE_COMPLETE (505*9/100) /* 9% of nominal max output */
+/* All charging modes use 4.200V for regulator */
+#define BATTERY_VCHARGING MC13783_VCHRG_4_200V
+/* Slow charging - MAIN - Still below 3.5V (avoid excessive reg. dissipation) */
+/* #define BATTERY_ISLOW */
+/* Fast charging - MAIN */
+#define BATTERY_IFAST MC13783_ICHRG_1596MA /* 505mA */
+/* Trickle charging low battery - MAIN (~10% Imax) */
+#define BATTERY_ITRICKLE MC13783_ICHRG_177MA /* 56mA */
+/* Slow charging - USB - Still below 3.5V (avoid excessive reg. dissipation) */
+/* #define BATTERY_ISLOW_USB */
+/* Fast charging - USB */
+#define BATTERY_IFAST_USB MC13783_ICHRG_1152MA /* 365mA */
+/* Trickle charging low battery - USB (Ibat = Icccv - Idevice) */
+#define BATTERY_ITRICKLE_USB MC13783_ICHRG_532MA /* 168mA */
+/* Maintain charge - USB 500mA */
+#define BATTERY_IFLOAT_USB MC13783_ICHRG_1152MA /* 365mA */
+#define BATTERY_VFLOAT_USB MC13783_VCHRG_4_150V
+/* Maintain charge - USB 100mA */
+#define BATTERY_IMAINTAIN_USB MC13783_ICHRG_266MA /* 84mA */
+#define BATTERY_VMAINTAIN_USB MC13783_VCHRG_4_150V
+
+/* Battery filter lengths in samples */
+#define BATT_AVE_SAMPLES 32
+#define ICHARGER_AVE_SAMPLES 32
+
+/* Provide filtered charge current */
+int battery_charge_current(void);
+
+#ifndef SIMULATOR
+/* Indicate various functions that require implementation at the target level.
+ * This is because the battery could be low or the battery switch is off but
+ * with the main charger attached which implies safe power for anything. The
+ * true battery reading is always reported for voltage readings and not the
+ * value at the application supply. */
+#define TARGET_QUERY_FORCE_SHUTDOWN
+
+/* For this the application supply is read out if the charger is attached or
+ * the battery read if not (completely hardware selected at BP). */
+#define TARGET_BATTERY_LEVEL_SAFE
+
+/* The state should be adjusted to CHARGING or DISCHARGING */
+#define TARGET_POWERMGMT_FILTER_CHARGE_STATE
+#endif /* SIMULATOR */
+
+#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 6d4797e..7d778fb 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 @@
#include "clkctl-imx31.h"
#include "mc13783.h"
+/* Initialize the watchdog timer */
+void watchdog_init(unsigned int half_seconds)
+{
+ uint16_t wcr = WDOG_WCR_WTw(half_seconds) | /* Timeout */
+ WDOG_WCR_WOE | /* WDOG output enabled */
+ WDOG_WCR_WDA | /* WDOG assertion - no effect */
+ WDOG_WCR_SRS | /* System reset - no effect */
+ WDOG_WCR_WRE; /* Generate a WDOG signal */
+
+ imx31_clkctl_module_clock_gating(CG_WDOG, CGM_ON_RUN_WAIT);
+
+ WDOG_WCR = wcr;
+ WDOG_WSR = 0x5555;
+ WDOG_WCR = wcr | WDOG_WCR_WDE; /* Enable timer - hardware does
+ not allow a disable now */
+ WDOG_WSR = 0xaaaa;
+}
+
+/* Service the watchdog timer */
+void watchdog_service(void)
+{
+ WDOG_WSR = 0x5555;
+ WDOG_WSR = 0xaaaa;
+}
+
int system_memory_guard(int newmode)
{
(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 31f1342..fbf7b23 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)
}
#endif
+/* Service the watchdog timer - serviced from the power thread every minute */
+void watchdog_init(unsigned int half_seconds);
+void watchdog_service(void);
+
+/* Prepare for transition to firmware */
void system_prepare_fw_start(void);
void tick_stop(void);
void 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 382bc32..a731486 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 @@
#include "usb_drv.h"
#include "usb-target.h"
#include "clkctl-imx31.h"
+#include "power-imx31.h"
#include "mc13783.h"
static int usb_status = USB_EXTRACTED;
@@ -53,8 +54,11 @@ static void enable_transceiver(bool enable)
void usb_connect_event(void)
{
- uint32_t status = mc13783_read(MC13783_INTERRUPT_SENSE0);
- usb_status = (status & MC13783_USB4V4S) ? USB_INSERTED : USB_EXTRACTED;
+ uint32_t status = mc13783_read(MC13783_INTERRUPT_SENSE0);
+ usb_status = (status & MC13783_USB4V4S) ?
+ USB_INSERTED : USB_EXTRACTED;
+ /* Notify power that USB charging is potentially available */
+ charger_usb_detect_event(usb_status);
}
int usb_detect(void)
@@ -81,7 +85,7 @@ void usb_init_device(void)
usb_connect_event();
/* Enable PMIC event */
- mc13783_enable_event(MC13783_USB4V4_EVENT);
+ mc13783_enable_event(MC13783_USB_EVENT);
}
void usb_enable(bool on)