Index: firmware/export/config-c200.h =================================================================== --- firmware/export/config-c200.h (revision 18308) +++ firmware/export/config-c200.h (working copy) @@ -123,8 +123,8 @@ #define BATTERY_CAPACITY_INC 0 /* capacity increment */ #define BATTERY_TYPES_COUNT 1 /* only one type */ -/* Hardware controlled charging? FIXME */ -#define CONFIG_CHARGING CHARGING_SIMPLE +/* Hardware controlled charging with monitoring of charge status */ +#define CONFIG_CHARGING CHARGING_MONITOR /* define this if the unit can be powered or charged via USB */ #define HAVE_USB_POWER Index: firmware/export/config-e200.h =================================================================== --- firmware/export/config-e200.h (revision 18308) +++ firmware/export/config-e200.h (working copy) @@ -120,8 +120,8 @@ #define BATTERY_CAPACITY_INC 0 /* capacity increment */ #define BATTERY_TYPES_COUNT 1 /* only one type */ -/* Hardware controlled charging? FIXME */ -#define CONFIG_CHARGING CHARGING_SIMPLE +/* Hardware controlled charging with monitoring of charge status */ +#define CONFIG_CHARGING CHARGING_MONITOR /* define this if the unit can be powered or charged via USB */ #define HAVE_USB_POWER Index: firmware/target/arm/sandisk/power-c200_e200.c =================================================================== --- firmware/target/arm/sandisk/power-c200_e200.c (revision 18308) +++ firmware/target/arm/sandisk/power-c200_e200.c (working copy) @@ -27,8 +27,47 @@ #include "as3514.h" #include "power.h" +/* mA, 50 - 400 in steps of 50 */ +#define CHARGE_CURRENT 300 +#if (CHARGE_CURRENT < 50) || (CHARGE_CURRENT > 400) +#error "Charging current out of range" +#endif + +/* mV, 3900 - 4250 in steps of 50 */ +#define CHARGE_VOLTAGE 4200 +#if (CHARGE_VOLTAGE < 3900) || (CHARGE_VOLTAGE > 4200) +#error "Charging voltage out of range" +#endif + +/* charger status bits in AS3514_IRQ_ENRD0 */ +#define CHG_tmphigh (1<<7) +#define CHG_endofch (1<<6) +#define CHG_status (1<<5) +#define CHG_changed (1<<4) + +/* charger control bits in AS3514_CHARGER */ +#define CHG_I (1<<4) +#define CHG_V (1<<1) +#define CHG_OFF (1<<0) + +/* mutex to protect the state machine against concurrent access */ +static struct mutex fsm_mutex; + +/* charger states */ +static enum charger_state { + STATE_UNPLUGGED, + STATE_CHARGING, + STATE_CHARGED +} charger_state; + + void power_init(void) { + /* initialise charger state machine */ + mutex_init(&fsm_mutex); + charger_state = STATE_UNPLUGGED; + /* enable charger status bits */ + pp_i2c_send(AS3514_I2C_ADDR, AS3514_IRQ_ENRD0, 0xFF); } void power_off(void) @@ -53,17 +92,80 @@ } } -bool charger_inserted(void) -{ + +static enum charger_state charging_statemachine(void) +{ + unsigned char status; + + mutex_lock(&fsm_mutex); + + /* check presence of charger first by checking GPIOs */ #ifdef SANSA_E200 - if(GPIOB_INPUT_VAL & 0x10) + if (!(GPIOB_INPUT_VAL & 0x10)) #else /* SANSA_C200 */ - if(GPIOH_INPUT_VAL & 0x2) + if (!(GPIOH_INPUT_VAL & 0x2)) #endif - return true; - return false; + { + /* GPIO indicates charger is not plugged, keep state machine in reset */ + charger_state = STATE_UNPLUGGED; + } + else + { + status = i2c_readbyte(AS3514_I2C_ADDR, AS3514_IRQ_ENRD0); + + switch (charger_state) { + + case STATE_UNPLUGGED: + if (status & CHG_status) + { + /* first turn off charger (prevents missed end-of-charge) */ + pp_i2c_send(AS3514_I2C_ADDR, AS3514_CHARGER, CHG_OFF); + /* configure maximum charge current and voltage */ + pp_i2c_send(AS3514_I2C_ADDR, AS3514_CHARGER, + (((CHARGE_CURRENT - 50) / 50) * CHG_I) | + (((CHARGE_VOLTAGE - 3900) / 50) * CHG_V)); + /* read status to clear initial end-of-charge indication */ + i2c_readbyte(AS3514_I2C_ADDR, AS3514_IRQ_ENRD0); + + charger_state = STATE_CHARGING; + } + break; + + case STATE_CHARGING: + if (status & (CHG_endofch | CHG_tmphigh)) + { + /* charging done or battery too hot, stop charging */ + pp_i2c_send(AS3514_I2C_ADDR, AS3514_CHARGER, CHG_OFF); + + charger_state = STATE_CHARGED; + } + break; + + case STATE_CHARGED: + /* do nothing, wait for unplug */ + break; + + default: + /* this should never happen... */ + charger_state = STATE_UNPLUGGED; + break; + } + } + mutex_unlock(&fsm_mutex); + return charger_state; } +bool charger_inserted(void) +{ + return (charging_statemachine() != STATE_UNPLUGGED); +} + +bool charging_state(void) +{ + return (charging_statemachine() == STATE_CHARGING); +} + + void ide_power_enable(bool on) { (void)on; Index: firmware/target/arm/sandisk/sansa-e200/powermgmt-e200.c =================================================================== --- firmware/target/arm/sandisk/sansa-e200/powermgmt-e200.c (revision 18308) +++ firmware/target/arm/sandisk/sansa-e200/powermgmt-e200.c (working copy) @@ -55,6 +55,10 @@ /* Returns battery voltage from ADC [millivolts] */ unsigned int battery_adc_voltage(void) { - return (adc_read(ADC_UNREG_POWER) * BATTERY_SCALE_FACTOR) >> 10; + /* on e200 ADC_RTCSUP seems to represent battery voltage better than + ADC_BVDD during charging (ADC_BVDD is way too high) and appears the same + in normal use. + */ + return (adc_read(ADC_RTCSUP) * BATTERY_SCALE_FACTOR) >> 10; }