From f7f1f7023f94a0c1f00033235f0f9d9ab51a1b3f Mon Sep 17 00:00:00 2001 From: Amaury Pouly Date: Sat, 24 Dec 2011 19:20:12 +0000 Subject: imx233/fuze+: implement power management(battery charger), add powermgmt debug info, add power debug info, move target specific powermgmt code to its own files, remove auto-slow setup which was forgotten for debugging git-svn-id: svn://svn.rockbox.org/rockbox/trunk@31424 a1c6a512-1295-4272-9138-f99709370657 --- firmware/SOURCES | 1 + firmware/export/imx233.h | 1 + firmware/target/arm/imx233/debug-imx233.c | 68 ++++++++-- firmware/target/arm/imx233/power-imx233.c | 123 +++++++++++++++++- firmware/target/arm/imx233/power-imx233.h | 71 ++++++++++- firmware/target/arm/imx233/powermgmt-imx233.c | 138 +++++++++++++++++---- firmware/target/arm/imx233/powermgmt-target.h | 30 ----- .../arm/imx233/sansa-fuzeplus/powermgmt-fuzeplus.c | 46 +++++++ .../arm/imx233/sansa-fuzeplus/powermgmt-target.h | 48 +++++++ 9 files changed, 459 insertions(+), 67 deletions(-) delete mode 100644 firmware/target/arm/imx233/powermgmt-target.h create mode 100644 firmware/target/arm/imx233/sansa-fuzeplus/powermgmt-fuzeplus.c create mode 100644 firmware/target/arm/imx233/sansa-fuzeplus/powermgmt-target.h diff --git a/firmware/SOURCES b/firmware/SOURCES index f59475e27a..e80a7492b1 100644 --- a/firmware/SOURCES +++ b/firmware/SOURCES @@ -1382,6 +1382,7 @@ target/arm/imx233/sansa-fuzeplus/power-fuzeplus.c target/arm/imx233/sansa-fuzeplus/adc-fuzeplus.c #ifndef BOOTLOADER target/arm/imx233/sansa-fuzeplus/audio-fuzeplus.c +target/arm/imx233/sansa-fuzeplus/powermgmt-fuzeplus.c #endif #endif diff --git a/firmware/export/imx233.h b/firmware/export/imx233.h index 1fa16154c1..a68ca6df13 100644 --- a/firmware/export/imx233.h +++ b/firmware/export/imx233.h @@ -90,5 +90,6 @@ #define __XTRACT(reg, field) ((reg & reg##__##field##_BM) >> reg##__##field##_BP) #define __XTRACT_EX(val, field) (((val) & field##_BM) >> field##_BP) +#define __FIELD_SET(reg, field, val) reg = (reg & ~reg##__##field##_BM) | (val << reg##__##field##_BP) #endif /* __IMX233_H__ */ diff --git a/firmware/target/arm/imx233/debug-imx233.c b/firmware/target/arm/imx233/debug-imx233.c index e6b07aef58..896c5bf6c3 100644 --- a/firmware/target/arm/imx233/debug-imx233.c +++ b/firmware/target/arm/imx233/debug-imx233.c @@ -29,7 +29,7 @@ #include "adc-imx233.h" #include "power-imx233.h" #include "clkctrl-imx233.h" -#include "powermgmt.h" +#include "powermgmt-imx233.h" #include "string.h" static struct @@ -107,11 +107,22 @@ bool dbg_hw_info_power(void) lcd_clear_display(); struct imx233_power_info_t info = imx233_power_get_info(POWER_INFO_ALL); - lcd_putsf(0, 0, "VDDD: %d mV linreg: %d", info.vddd, info.vddd_linreg); - lcd_putsf(0, 1, "VDDA: %d mV linreg: %d", info.vdda, info.vdda_linreg); - lcd_putsf(0, 2, "VDDIO: %d mV", info.vddio); + lcd_putsf(0, 0, "VDDD: %d mV linreg: %d offset: %d", info.vddd, info.vddd_linreg, + info.vddd_linreg_offset); + lcd_putsf(0, 1, "VDDA: %d mV linreg: %d offset: %d", info.vdda, info.vdda_linreg, + info.vdda_linreg_offset); + lcd_putsf(0, 2, "VDDIO: %d mV offset: %d", info.vddio, info.vddio_linreg_offset); lcd_putsf(0, 3, "VDDMEM: %d mV linreg: %d", info.vddmem, info.vddmem_linreg); lcd_putsf(0, 4, "DC-DC: pll: %d freq: %d", info.dcdc_sel_pllclk, info.dcdc_freqsel); + lcd_putsf(0, 5, "charge: %d mA stop: %d mA", info.charge_current, info.stop_current); + lcd_putsf(0, 6, "charging: %d bat_adj: %d", info.charging, info.batt_adj); + lcd_putsf(0, 7, "4.2: en: %d dcdc: %d", info._4p2_enable, info._4p2_dcdc); + lcd_putsf(0, 8, "4.2: cmptrip: %d dropout: %d", info._4p2_cmptrip, info._4p2_dropout); + lcd_putsf(0, 9, "5V: pwd_4.2_charge: %d", info._5v_pwd_charge_4p2); + lcd_putsf(0, 10, "5V: chargelim: %d mA", info._5v_charge_4p2_limit); + lcd_putsf(0, 11, "5V: dcdc: %d xfer: %d", info._5v_enable_dcdc, info._5v_dcdc_xfer); + lcd_putsf(0, 12, "5V: thr: %d mV use: %d cmps: %d", info._5v_vbusvalid_thr, + info._5v_vbusvalid_detect, info._5v_vbus_cmps); lcd_update(); yield(); @@ -194,12 +205,6 @@ static struct bool dbg_hw_info_clkctrl(void) { lcd_setfont(FONT_SYSFIXED); - imx233_enable_auto_slow_monitor(AS_CPU_INSTR, true); - imx233_enable_auto_slow_monitor(AS_CPU_DATA, true); - imx233_enable_auto_slow_monitor(AS_TRAFFIC, true); - imx233_enable_auto_slow_monitor(AS_TRAFFIC_JAM, true); - imx233_enable_auto_slow_monitor(AS_APBXDMA, true); - imx233_enable_auto_slow_monitor(AS_APBHDMA, true); while(1) { @@ -269,10 +274,49 @@ bool dbg_hw_info_clkctrl(void) } } +bool dbg_hw_info_powermgmt(void) +{ + lcd_setfont(FONT_SYSFIXED); + + while(1) + { + int button = get_action(CONTEXT_STD, HZ / 10); + switch(button) + { + case ACTION_STD_NEXT: + case ACTION_STD_PREV: + case ACTION_STD_OK: + case ACTION_STD_MENU: + lcd_setfont(FONT_UI); + return true; + case ACTION_STD_CANCEL: + lcd_setfont(FONT_UI); + return false; + } + + lcd_clear_display(); + struct imx233_powermgmt_info_t info = imx233_powermgmt_get_info(); + + lcd_putsf(0, 0, "state: %s", + info.state == CHARGE_STATE_DISABLED ? "disabled" : + info.state == CHARGE_STATE_ERROR ? "error" : + info.state == DISCHARGING ? "discharging" : + info.state == TRICKLE ? "trickle" : + info.state == TOPOFF ? "topoff" : + info.state == CHARGING ? "charging" : ""); + lcd_putsf(0, 1, "charging tmo: %d", info.charging_timeout); + lcd_putsf(0, 2, "topoff tmo: %d", info.topoff_timeout); + lcd_putsf(0, 3, "4p2ilimit tmo: %d", info.incr_4p2_ilimit_timeout); + + lcd_update(); + yield(); + } +} + bool dbg_hw_info(void) { - return dbg_hw_info_clkctrl() && dbg_hw_info_dma() && dbg_hw_info_adc() && dbg_hw_info_power() && - dbg_hw_target_info(); + return dbg_hw_info_clkctrl() && dbg_hw_info_dma() && dbg_hw_info_adc() && + dbg_hw_info_power() && dbg_hw_info_powermgmt() && dbg_hw_target_info(); } bool dbg_ports(void) diff --git a/firmware/target/arm/imx233/power-imx233.c b/firmware/target/arm/imx233/power-imx233.c index 557b8521d5..ac1a407ba0 100644 --- a/firmware/target/arm/imx233/power-imx233.c +++ b/firmware/target/arm/imx233/power-imx233.c @@ -26,6 +26,43 @@ #include "system-target.h" #include "usb-target.h" +struct current_step_bit_t +{ + unsigned current; + uint32_t bit; +}; + +/* in decreasing order */ +static struct current_step_bit_t g_charger_current_bits[] = +{ + { 400, HW_POWER_CHARGE__BATTCHRG_I__400mA }, + { 200, HW_POWER_CHARGE__BATTCHRG_I__200mA }, + { 100, HW_POWER_CHARGE__BATTCHRG_I__100mA }, + { 50, HW_POWER_CHARGE__BATTCHRG_I__50mA }, + { 20, HW_POWER_CHARGE__BATTCHRG_I__20mA }, + { 10, HW_POWER_CHARGE__BATTCHRG_I__10mA } +}; + +/* in decreasing order */ +static struct current_step_bit_t g_charger_stop_current_bits[] = +{ + { 100, HW_POWER_CHARGE__STOP_ILIMIT__100mA }, + { 50, HW_POWER_CHARGE__STOP_ILIMIT__50mA }, + { 20, HW_POWER_CHARGE__STOP_ILIMIT__20mA }, + { 10, HW_POWER_CHARGE__STOP_ILIMIT__10mA } +}; + +/* in decreasing order */ +static struct current_step_bit_t g_4p2_charge_limit_bits[] = +{ + { 400, HW_POWER_5VCTRL__CHARGE_4P2_ILIMIT__400mA }, + { 200, HW_POWER_5VCTRL__CHARGE_4P2_ILIMIT__200mA }, + { 100, HW_POWER_5VCTRL__CHARGE_4P2_ILIMIT__100mA }, + { 50, HW_POWER_5VCTRL__CHARGE_4P2_ILIMIT__50mA }, + { 20, HW_POWER_5VCTRL__CHARGE_4P2_ILIMIT__20mA }, + { 10, HW_POWER_5VCTRL__CHARGE_4P2_ILIMIT__10mA } +}; + void INT_VDD5V(void) { if(HW_POWER_CTRL & HW_POWER_CTRL__VBUSVALID_IRQ) @@ -43,6 +80,12 @@ void INT_VDD5V(void) void power_init(void) { + /* setup vbusvalid parameters: set threshold to 4v and power up comparators */ + __REG_CLR(HW_POWER_5VCTRL) = HW_POWER_5VCTRL__VBUSVALID_TRSH_BM; + __REG_SET(HW_POWER_5VCTRL) = HW_POWER_5VCTRL__VBUSVALID_TRSH_4V | + HW_POWER_5VCTRL__PWRUP_VBUS_CMPS; + /* enable vbusvalid detection method for the dcdc (improves efficiency) */ + __REG_SET(HW_POWER_5VCTRL) = HW_POWER_5VCTRL__VBUSVALID_5VDETECT; /* clear vbusvalid irq and set correct polarity */ __REG_CLR(HW_POWER_CTRL) = HW_POWER_CTRL__VBUSVALID_IRQ; if(HW_POWER_STS & HW_POWER_STS__VBUSVALID) @@ -51,6 +94,11 @@ void power_init(void) __REG_SET(HW_POWER_CTRL) = HW_POWER_CTRL__POLARITY_VBUSVALID; __REG_SET(HW_POWER_CTRL) = HW_POWER_CTRL__ENIRQ_VBUS_VALID; imx233_enable_interrupt(INT_SRC_VDD5V, true); + /* setup linear regulator offsets to 25 mV below to prevent contention between + * linear regulators and DCDC */ + __FIELD_SET(HW_POWER_VDDDCTRL, LINREG_OFFSET, 2); + __FIELD_SET(HW_POWER_VDDACTRL, LINREG_OFFSET, 2); + __FIELD_SET(HW_POWER_VDDIOCTRL, LINREG_OFFSET, 2); } void power_off(void) @@ -64,12 +112,42 @@ void power_off(void) unsigned int power_input_status(void) { - return POWER_INPUT_NONE; + return usb_plugged() ? POWER_INPUT_MAIN_CHARGER : POWER_INPUT_NONE; } bool charging_state(void) { - return false; + return HW_POWER_STS & HW_POWER_STS__CHRGSTS; +} + +void imx233_power_set_charge_current(unsigned current) +{ + __REG_CLR(HW_POWER_CHARGE) = HW_POWER_CHARGE__BATTCHRG_I_BM; + /* find closest current LOWER THAN OR EQUAL TO the expected current */ + for(unsigned i = 0; i < ARRAYLEN(g_charger_current_bits); i++) + if(current >= g_charger_current_bits[i].current) + { + current -= g_charger_current_bits[i].current; + __REG_SET(HW_POWER_CHARGE) = g_charger_current_bits[i].bit; + } +} + +void imx233_power_set_stop_current(unsigned current) +{ + __REG_CLR(HW_POWER_CHARGE) = HW_POWER_CHARGE__STOP_ILIMIT_BM; + /* find closest current GREATHER THAN OR EQUAL TO the expected current */ + unsigned sum = 0; + for(unsigned i = 0; i < ARRAYLEN(g_charger_stop_current_bits); i++) + sum += g_charger_stop_current_bits[i].current; + for(unsigned i = 0; i < ARRAYLEN(g_charger_stop_current_bits); i++) + { + sum -= g_charger_stop_current_bits[i].current; + if(current > sum) + { + current -= g_charger_stop_current_bits[i].current; + __REG_SET(HW_POWER_CHARGE) = g_charger_stop_current_bits[i].bit; + } + } } struct imx233_power_info_t imx233_power_get_info(unsigned flags) @@ -91,14 +169,22 @@ struct imx233_power_info_t imx233_power_get_info(unsigned flags) { s.vddd = HW_POWER_VDDDCTRL__TRG_MIN + HW_POWER_VDDDCTRL__TRG_STEP * __XTRACT(HW_POWER_VDDDCTRL, TRG); s.vddd_linreg = HW_POWER_VDDDCTRL & HW_POWER_VDDDCTRL__ENABLE_LINREG; + s.vddd_linreg_offset = __XTRACT(HW_POWER_VDDDCTRL, LINREG_OFFSET) == 0 ? 0 : + __XTRACT(HW_POWER_VDDDCTRL, LINREG_OFFSET) == 1 ? 25 : -25; } if(flags & POWER_INFO_VDDA) { s.vdda = HW_POWER_VDDACTRL__TRG_MIN + HW_POWER_VDDACTRL__TRG_STEP * __XTRACT(HW_POWER_VDDACTRL, TRG); s.vdda_linreg = HW_POWER_VDDACTRL & HW_POWER_VDDACTRL__ENABLE_LINREG; + s.vdda_linreg_offset = __XTRACT(HW_POWER_VDDACTRL, LINREG_OFFSET) == 0 ? 0 : + __XTRACT(HW_POWER_VDDACTRL, LINREG_OFFSET) == 1 ? 25 : -25; } if(flags & POWER_INFO_VDDIO) + { s.vddio = HW_POWER_VDDIOCTRL__TRG_MIN + HW_POWER_VDDIOCTRL__TRG_STEP * __XTRACT(HW_POWER_VDDIOCTRL, TRG); + s.vddio_linreg_offset = __XTRACT(HW_POWER_VDDIOCTRL, LINREG_OFFSET) == 0 ? 0 : + __XTRACT(HW_POWER_VDDIOCTRL, LINREG_OFFSET) == 1 ? 25 : -25; + } if(flags & POWER_INFO_VDDMEM) { s.vddmem = HW_POWER_VDDMEMCTRL__TRG_MIN + HW_POWER_VDDMEMCTRL__TRG_STEP * __XTRACT(HW_POWER_VDDMEMCTRL, TRG); @@ -109,5 +195,38 @@ struct imx233_power_info_t imx233_power_get_info(unsigned flags) s.dcdc_sel_pllclk = HW_POWER_MISC & HW_POWER_MISC__SEL_PLLCLK; s.dcdc_freqsel = dcdc_freqsel[__XTRACT(HW_POWER_MISC, FREQSEL)]; } + if(flags & POWER_INFO_CHARGE) + { + for(unsigned i = 0; i < ARRAYLEN(g_charger_current_bits); i++) + if(HW_POWER_CHARGE & g_charger_current_bits[i].bit) + s.charge_current += g_charger_current_bits[i].current; + for(unsigned i = 0; i < ARRAYLEN(g_charger_stop_current_bits); i++) + if(HW_POWER_CHARGE & g_charger_stop_current_bits[i].bit) + s.stop_current += g_charger_stop_current_bits[i].current; + s.charging = HW_POWER_STS & HW_POWER_STS__CHRGSTS; + s.batt_adj = HW_POWER_BATTMONITOR & HW_POWER_BATTMONITOR__ENBATADJ; + } + if(flags & POWER_INFO_4P2) + { + s._4p2_enable = HW_POWER_DCDC4P2 & HW_POWER_DCDC4P2__ENABLE_4P2; + s._4p2_dcdc = HW_POWER_DCDC4P2 & HW_POWER_DCDC4P2__ENABLE_DCDC; + s._4p2_cmptrip = __XTRACT(HW_POWER_DCDC4P2, CMPTRIP); + s._4p2_dropout = __XTRACT(HW_POWER_DCDC4P2, DROPOUT_CTRL); + } + if(flags & POWER_INFO_5V) + { + s._5v_pwd_charge_4p2 = HW_POWER_5VCTRL & HW_POWER_5VCTRL__PWD_CHARGE_4P2; + s._5v_dcdc_xfer = HW_POWER_5VCTRL & HW_POWER_5VCTRL__DCDC_XFER; + s._5v_enable_dcdc = HW_POWER_5VCTRL & HW_POWER_5VCTRL__ENABLE_DCDC; + for(unsigned i = 0; i < ARRAYLEN(g_4p2_charge_limit_bits); i++) + if(HW_POWER_5VCTRL & g_4p2_charge_limit_bits[i].bit) + s._5v_charge_4p2_limit += g_4p2_charge_limit_bits[i].current; + s._5v_vbusvalid_detect = HW_POWER_5VCTRL & HW_POWER_5VCTRL__VBUSVALID_5VDETECT; + s._5v_vbus_cmps = HW_POWER_5VCTRL & HW_POWER_5VCTRL__PWRUP_VBUS_CMPS; + s._5v_vbusvalid_thr = + __XTRACT(HW_POWER_5VCTRL, VBUSVALID_TRSH) == 0 ? + 2900 + : 3900 + __XTRACT(HW_POWER_5VCTRL, VBUSVALID_TRSH) * 100; + } return s; } diff --git a/firmware/target/arm/imx233/power-imx233.h b/firmware/target/arm/imx233/power-imx233.h index a546b96117..368ca719da 100644 --- a/firmware/target/arm/imx233/power-imx233.h +++ b/firmware/target/arm/imx233/power-imx233.h @@ -33,25 +33,52 @@ #define HW_POWER_CTRL__POLARITY_VBUSVALID (1 << 5) #define HW_POWER_5VCTRL (*(volatile uint32_t *)(HW_POWER_BASE + 0x10)) +#define HW_POWER_5VCTRL__ENABLE_DCDC (1 << 0) +#define HW_POWER_5VCTRL__PWRUP_VBUS_CMPS (1 << 1) #define HW_POWER_5VCTRL__VBUSVALID_5VDETECT (1 << 4) +#define HW_POWER_5VCTRL__DCDC_XFER (1 << 5) #define HW_POWER_5VCTRL__VBUSVALID_TRSH_BP 8 #define HW_POWER_5VCTRL__VBUSVALID_TRSH_BM (0x7 << 8) +#define HW_POWER_5VCTRL__VBUSVALID_TRSH_2p9 (0 << 8) +#define HW_POWER_5VCTRL__VBUSVALID_TRSH_4V (1 << 8) +#define HW_POWER_5VCTRL__CHARGE_4P2_ILIMIT_BP 12 +#define HW_POWER_5VCTRL__CHARGE_4P2_ILIMIT_BM (0x3f << 12) +#define HW_POWER_5VCTRL__CHARGE_4P2_ILIMIT__10mA (1 << 12) +#define HW_POWER_5VCTRL__CHARGE_4P2_ILIMIT__20mA (1 << 13) +#define HW_POWER_5VCTRL__CHARGE_4P2_ILIMIT__50mA (1 << 14) +#define HW_POWER_5VCTRL__CHARGE_4P2_ILIMIT__100mA (1 << 15) +#define HW_POWER_5VCTRL__CHARGE_4P2_ILIMIT__200mA (1 << 16) +#define HW_POWER_5VCTRL__CHARGE_4P2_ILIMIT__400mA (1 << 17) +#define HW_POWER_5VCTRL__PWD_CHARGE_4P2 (1 << 20) #define HW_POWER_MINPWR (*(volatile uint32_t *)(HW_POWER_BASE + 0x20)) #define HW_POWER_CHARGE (*(volatile uint32_t *)(HW_POWER_BASE + 0x30)) #define HW_POWER_CHARGE__BATTCHRG_I_BP 0 #define HW_POWER_CHARGE__BATTCHRG_I_BM 0x3f +#define HW_POWER_CHARGE__BATTCHRG_I__10mA (1 << 0) +#define HW_POWER_CHARGE__BATTCHRG_I__20mA (1 << 1) +#define HW_POWER_CHARGE__BATTCHRG_I__50mA (1 << 2) +#define HW_POWER_CHARGE__BATTCHRG_I__100mA (1 << 3) +#define HW_POWER_CHARGE__BATTCHRG_I__200mA (1 << 4) +#define HW_POWER_CHARGE__BATTCHRG_I__400mA (1 << 5) #define HW_POWER_CHARGE__STOP_ILIMIT_BP 8 #define HW_POWER_CHARGE__STOP_ILIMIT_BM 0xf00 +#define HW_POWER_CHARGE__STOP_ILIMIT__10mA (1 << 8) +#define HW_POWER_CHARGE__STOP_ILIMIT__20mA (1 << 9) +#define HW_POWER_CHARGE__STOP_ILIMIT__50mA (1 << 10) +#define HW_POWER_CHARGE__STOP_ILIMIT__100mA (1 << 11) #define HW_POWER_CHARGE__PWD_BATTCHRG (1 << 16) #define HW_POWER_CHARGE__CHRG_STS_OFF (1 << 19) +#define HW_POWER_CHARGE__ENABLE_LOAD (1 << 22) #define HW_POWER_VDDDCTRL (*(volatile uint32_t *)(HW_POWER_BASE + 0x40)) #define HW_POWER_VDDDCTRL__TRG_BP 0 #define HW_POWER_VDDDCTRL__TRG_BM 0x1f #define HW_POWER_VDDDCTRL__TRG_STEP 25 /* mV */ #define HW_POWER_VDDDCTRL__TRG_MIN 800 /* mV */ +#define HW_POWER_VDDDCTRL__LINREG_OFFSET_BP 16 +#define HW_POWER_VDDDCTRL__LINREG_OFFSET_BM (0x3 << 16) #define HW_POWER_VDDDCTRL__ENABLE_LINREG (1 << 21) #define HW_POWER_VDDACTRL (*(volatile uint32_t *)(HW_POWER_BASE + 0x50)) @@ -59,6 +86,8 @@ #define HW_POWER_VDDACTRL__TRG_BM 0x1f #define HW_POWER_VDDACTRL__TRG_STEP 25 /* mV */ #define HW_POWER_VDDACTRL__TRG_MIN 1500 /* mV */ +#define HW_POWER_VDDACTRL__LINREG_OFFSET_BP 12 +#define HW_POWER_VDDACTRL__LINREG_OFFSET_BM (0x3 << 12) #define HW_POWER_VDDACTRL__ENABLE_LINREG (1 << 17) #define HW_POWER_VDDIOCTRL (*(volatile uint32_t *)(HW_POWER_BASE + 0x60)) @@ -66,6 +95,8 @@ #define HW_POWER_VDDIOCTRL__TRG_BM 0x1f #define HW_POWER_VDDIOCTRL__TRG_STEP 25 /* mV */ #define HW_POWER_VDDIOCTRL__TRG_MIN 2800 /* mV */ +#define HW_POWER_VDDIOCTRL__LINREG_OFFSET_BP 12 +#define HW_POWER_VDDIOCTRL__LINREG_OFFSET_BM (0x3 << 12) #define HW_POWER_VDDMEMCTRL (*(volatile uint32_t *)(HW_POWER_BASE + 0x70)) #define HW_POWER_VDDMEMCTRL__TRG_BP 0 @@ -74,6 +105,17 @@ #define HW_POWER_VDDMEMCTRL__TRG_MIN 1700 /* mV */ #define HW_POWER_VDDMEMCTRL__ENABLE_LINREG (1 << 8) +#define HW_POWER_DCDC4P2 (*(volatile uint32_t *)(HW_POWER_BASE + 0x80)) +#define HW_POWER_DCDC4P2__CMPTRIP_BP 0 +#define HW_POWER_DCDC4P2__CMPTRIP_BM 0x1f +#define HW_POWER_DCDC4P2__CMPTRIP__0p85 0 +#define HW_POWER_DCDC4P2__ENABLE_DCDC (1 << 22) +#define HW_POWER_DCDC4P2__ENABLE_4P2 (1 << 23) +#define HW_POWER_DCDC4P2__DROPOUT_CTRL_BP 28 +#define HW_POWER_DCDC4P2__DROPOUT_CTRL_BM (0xf << 28) +#define HW_POWER_DCDC4P2__DROPOUT_CTRL__200mV (3 << 30) +#define HW_POWER_DCDC4P2__DROPOUT_CTRL__HIGHER (2 << 28) + #define HW_POWER_MISC (*(volatile uint32_t *)(HW_POWER_BASE + 0x90)) #define HW_POWER_MISC__SEL_PLLCLK 1 #define HW_POWER_MISC__FREQSEL_BP 4 @@ -89,10 +131,12 @@ #define HW_POWER_STS (*(volatile uint32_t *)(HW_POWER_BASE + 0xc0)) #define HW_POWER_STS__VBUSVALID (1 << 1) +#define HW_POWER_STS__CHRGSTS (1 << 11) #define HW_POWER_STS__PSWITCH_BP 20 #define HW_POWER_STS__PSWITCH_BM (3 << 20) #define HW_POWER_BATTMONITOR (*(volatile uint32_t *)(HW_POWER_BASE + 0xe0)) +#define HW_POWER_BATTMONITOR__ENBATADJ (1 << 10) #define HW_POWER_BATTMONITOR__BATT_VAL_BP 16 #define HW_POWER_BATTMONITOR__BATT_VAL_BM (0x3ff << 16) @@ -100,17 +144,39 @@ #define HW_POWER_RESET__UNLOCK 0x3E770000 #define HW_POWER_RESET__PWD 0x1 +void imx233_power_set_charge_current(unsigned current); /* in mA */ +void imx233_power_set_stop_current(unsigned current); /* in mA */ +void imx233_power_enable_batadj(bool enable); + struct imx233_power_info_t { int vddd; /* in mV */ bool vddd_linreg; /* VDDD source: linreg from VDDA or DC-DC */ + int vddd_linreg_offset; int vdda; /* in mV */ bool vdda_linreg; /* VDDA source: linreg from VDDIO or DC-DC */ + int vdda_linreg_offset; int vddio; /* in mV */ + int vddio_linreg_offset; int vddmem; /* in mV */ bool vddmem_linreg; /* VDDMEM source: linreg from VDDIO or off */ bool dcdc_sel_pllclk; /* clock source of DC-DC: pll or 24MHz xtal */ int dcdc_freqsel; + int charge_current; + int stop_current; + bool charging; + bool batt_adj; + bool _4p2_enable; + bool _4p2_dcdc; + int _4p2_cmptrip; + int _4p2_dropout; + bool _5v_pwd_charge_4p2; + int _5v_charge_4p2_limit; + bool _5v_dcdc_xfer; + bool _5v_enable_dcdc; + int _5v_vbusvalid_thr; + bool _5v_vbusvalid_detect; + bool _5v_vbus_cmps; }; #define POWER_INFO_VDDD (1 << 0) @@ -118,7 +184,10 @@ struct imx233_power_info_t #define POWER_INFO_VDDIO (1 << 2) #define POWER_INFO_VDDMEM (1 << 3) #define POWER_INFO_DCDC (1 << 4) -#define POWER_INFO_ALL 0x1f +#define POWER_INFO_CHARGE (1 << 5) +#define POWER_INFO_4P2 (1 << 6) +#define POWER_INFO_5V (1 << 7) +#define POWER_INFO_ALL 0xff struct imx233_power_info_t imx233_power_get_info(unsigned flags); diff --git a/firmware/target/arm/imx233/powermgmt-imx233.c b/firmware/target/arm/imx233/powermgmt-imx233.c index 0f24fa41ff..97f6e08e12 100644 --- a/firmware/target/arm/imx233/powermgmt-imx233.c +++ b/firmware/target/arm/imx233/powermgmt-imx233.c @@ -21,30 +21,21 @@ #include "powermgmt.h" #include "power-imx233.h" +#include "usb-target.h" +#include "string.h" +//#define LOGF_ENABLE +#include "logf.h" -const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT] = -{ - 3659 -}; - -const unsigned short battery_level_shutoff[BATTERY_TYPES_COUNT] = -{ - 3630 -}; - -/* voltages (millivolt) of 0%, 10%, ... 100% when charging disabled */ -const unsigned short percent_to_volt_discharge[BATTERY_TYPES_COUNT][11] = -{ - /* Toshiba Gigabeat S Li Ion 700mAH figured from discharge curve */ - { 3659, 3719, 3745, 3761, 3785, 3813, 3856, 3926, 3984, 4040, 4121 }, -}; +#if !defined(IMX233_CHARGE_CURRENT) || !defined(IMX233_STOP_CURRENT) \ + || !defined(IMX233_CHARGING_TIMEOUT) || !defined(IMX233_TOPOFF_TIMEOUT) +#error You must define IMX233_CHARGE_CURRENT, IMX233_STOP_CURRENT, \ + IMX233_CHARGING_TIMEOUT and IMX233_TOPOFF_TIMEOUT ! +#endif -/* voltages (millivolt) of 0%, 10%, ... 100% when charging enabled */ -const unsigned short percent_to_volt_charge[11] = -{ - /* Toshiba Gigabeat S Li Ion 700mAH figured from charge curve */ - 4028, 4063, 4087, 4111, 4135, 4156, 4173, 4185, 4194, 4202, 4208 -}; +/* charger state is maintained in charge_state (see powermgmt.h) */ +static int timeout_charging; /* timeout before charging will be declared broken */ +static int timeout_topping_off; /* timeout before stopping charging after topping off */ +static int timeout_4p2_ilimit_increase; /* timeout before increasing 4p2 ilimit */ /* Returns battery voltage from ADC [millivolts] */ unsigned int battery_adc_voltage(void) @@ -55,12 +46,115 @@ unsigned int battery_adc_voltage(void) void powermgmt_init_target(void) { + imx233_power_set_charge_current(IMX233_CHARGE_CURRENT); + imx233_power_set_stop_current(IMX233_STOP_CURRENT); + /* assume that adc_init was called and battery monitoring via LRADC setup */ + __REG_SET(HW_POWER_BATTMONITOR) = HW_POWER_BATTMONITOR__ENBATADJ; + /* make sure we are in a known state: disable charger and 4p2 */ + __REG_SET(HW_POWER_CHARGE) = HW_POWER_CHARGE__PWD_BATTCHRG; + __REG_CLR(HW_POWER_DCDC4P2) = HW_POWER_DCDC4P2__ENABLE_DCDC | + HW_POWER_DCDC4P2__ENABLE_4P2; + __REG_SET(HW_POWER_5VCTRL) = HW_POWER_5VCTRL__PWD_CHARGE_4P2; + charge_state = DISCHARGING; } void charging_algorithm_step(void) { + bool is_5v_present = usb_plugged(); + + /* initial state & 5v -> battery transition */ + if(!is_5v_present && charge_state != DISCHARGING) + { + logf("pwrmgmt: * -> discharging"); + logf("pwrmgmt: disable charger and 4p2"); + /* 5V has been lost: disable 4p2 power rail */ + __REG_SET(HW_POWER_CHARGE) = HW_POWER_CHARGE__PWD_BATTCHRG; + __REG_CLR(HW_POWER_DCDC4P2) = HW_POWER_DCDC4P2__ENABLE_DCDC | + HW_POWER_DCDC4P2__ENABLE_4P2; + __REG_SET(HW_POWER_5VCTRL) = HW_POWER_5VCTRL__PWD_CHARGE_4P2; + charge_state = DISCHARGING; + } + /* battery -> 5v transition */ + else if(is_5v_present && charge_state == DISCHARGING) + { + logf("pwrmgmt: discharging -> trickle"); + logf("pwrmgmt: begin charging 4p2"); + /* 5V has been detected: prepare 4.2V power rail for activation */ + __REG_SET(HW_POWER_DCDC4P2) = HW_POWER_DCDC4P2__ENABLE_4P2; + __REG_SET(HW_POWER_CHARGE) = HW_POWER_CHARGE__ENABLE_LOAD; + __FIELD_SET(HW_POWER_5VCTRL, CHARGE_4P2_ILIMIT, 1); + __REG_CLR(HW_POWER_5VCTRL) = HW_POWER_5VCTRL__PWD_CHARGE_4P2;// FIXME: manual error ? + __REG_SET(HW_POWER_DCDC4P2) = HW_POWER_DCDC4P2__ENABLE_DCDC; + timeout_4p2_ilimit_increase = current_tick + HZ / 100; + charge_state = TRICKLE; + } + else if(charge_state == TRICKLE && TIME_AFTER(current_tick, timeout_4p2_ilimit_increase)) + { + /* if 4.2V current limit has not reached 780mA, increase it slowly to + * charge the 4.2V capacitance */ + if(__XTRACT(HW_POWER_5VCTRL, CHARGE_4P2_ILIMIT) != 0x3f) + { + //logf("pwrmgmt: incr 4.2 ilimit"); + HW_POWER_5VCTRL += 1 << HW_POWER_5VCTRL__CHARGE_4P2_ILIMIT_BP; + timeout_4p2_ilimit_increase = current_tick + HZ / 100; + } + /* we've reached the maximum, take action */ + else + { + logf("pwrmgmt: enable dcdc and charger"); + logf("pwrmgmt: trickle -> charging"); + /* adjust arbitration between 4.2 and battery */ + __FIELD_SET(HW_POWER_DCDC4P2, CMPTRIP, 0); /* 85% */ + __FIELD_SET(HW_POWER_DCDC4P2, DROPOUT_CTRL, 0xe); /* select greater, 200 mV drop */ + __REG_CLR(HW_POWER_5VCTRL) = HW_POWER_5VCTRL__DCDC_XFER; + __REG_SET(HW_POWER_5VCTRL) = HW_POWER_5VCTRL__ENABLE_DCDC; + /* enable battery charging */ + __REG_CLR(HW_POWER_CHARGE) = HW_POWER_CHARGE__PWD_BATTCHRG; + charge_state = CHARGING; + timeout_charging = current_tick + IMX233_CHARGING_TIMEOUT; + } + } + else if(charge_state == CHARGING && TIME_AFTER(current_tick, timeout_charging)) + { + /* we have charged for a too long time, declare charger broken */ + logf("pwrmgmt: charging timeout exceeded!"); + logf("pwrmgmt: charging -> error"); + /* stop charging */ + __REG_SET(HW_POWER_5VCTRL) = HW_POWER_5VCTRL__PWD_CHARGE_4P2; + /* goto error state */ + charge_state = CHARGE_STATE_ERROR; + } + else if(charge_state == CHARGING && !(HW_POWER_STS & HW_POWER_STS__CHRGSTS)) + { + logf("pwrmgmt: topping off"); + logf("pwrmgmt: charging -> topoff"); + charge_state = TOPOFF; + timeout_topping_off = current_tick + IMX233_TOPOFF_TIMEOUT; + } + else if(charge_state == TOPOFF && TIME_AFTER(current_tick, timeout_topping_off)) + { + logf("pwrmgmt: charging finished"); + logf("pwrmgmt: topoff -> disabled"); + /* stop charging */ + __REG_SET(HW_POWER_5VCTRL) = HW_POWER_5VCTRL__PWD_CHARGE_4P2; + charge_state = CHARGE_STATE_DISABLED; + } } void charging_algorithm_close(void) { } + +struct imx233_powermgmt_info_t imx233_powermgmt_get_info(void) +{ + struct imx233_powermgmt_info_t info; + memset(&info, 0, sizeof(info)); + info.state = charge_state; + info.charging_timeout = + charge_state == CHARGING ? timeout_charging - current_tick : 0; + info.topoff_timeout = + charge_state == TOPOFF ? timeout_topping_off - current_tick : 0; + info.incr_4p2_ilimit_timeout = + charge_state == TRICKLE ? timeout_4p2_ilimit_increase - current_tick : 0; + return info; +} diff --git a/firmware/target/arm/imx233/powermgmt-target.h b/firmware/target/arm/imx233/powermgmt-target.h deleted file mode 100644 index d539a66d16..0000000000 --- a/firmware/target/arm/imx233/powermgmt-target.h +++ /dev/null @@ -1,30 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright (C) 2011 by Amaury Pouly - * - * 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 - -#include "config.h" - -void powermgmt_init_target(void); -void charging_algorithm_step(void); -void charging_algorithm_close(void); - -#endif /* POWERMGMT_TARGET_H */ diff --git a/firmware/target/arm/imx233/sansa-fuzeplus/powermgmt-fuzeplus.c b/firmware/target/arm/imx233/sansa-fuzeplus/powermgmt-fuzeplus.c new file mode 100644 index 0000000000..3e068f007d --- /dev/null +++ b/firmware/target/arm/imx233/sansa-fuzeplus/powermgmt-fuzeplus.c @@ -0,0 +1,46 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2011 by Amaury Pouly + * + * 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. + * + ****************************************************************************/ +#include "config.h" +#include "powermgmt-target.h" + +const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT] = +{ + 3659 +}; + +const unsigned short battery_level_shutoff[BATTERY_TYPES_COUNT] = +{ + 3630 +}; + +/* voltages (millivolt) of 0%, 10%, ... 100% when charging disabled */ +const unsigned short percent_to_volt_discharge[BATTERY_TYPES_COUNT][11] = +{ + /* Toshiba Gigabeat S Li Ion 700mAH figured from discharge curve */ + { 3659, 3719, 3745, 3761, 3785, 3813, 3856, 3926, 3984, 4040, 4121 }, +}; + +/* voltages (millivolt) of 0%, 10%, ... 100% when charging enabled */ +const unsigned short percent_to_volt_charge[11] = +{ + /* Toshiba Gigabeat S Li Ion 700mAH figured from charge curve */ + 4028, 4063, 4087, 4111, 4135, 4156, 4173, 4185, 4194, 4202, 4208 +}; diff --git a/firmware/target/arm/imx233/sansa-fuzeplus/powermgmt-target.h b/firmware/target/arm/imx233/sansa-fuzeplus/powermgmt-target.h new file mode 100644 index 0000000000..34d1d00e1d --- /dev/null +++ b/firmware/target/arm/imx233/sansa-fuzeplus/powermgmt-target.h @@ -0,0 +1,48 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2011 by Amaury Pouly + * + * 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 + +#include "config.h" +#include "powermgmt-imx233.h" + +/* Fuze+ OF settings: + * - current ramp slope: 50 mA/s + * - conditioning threshold voltage: 3 V + * - conditioning max voltage: 3.1 V + * - conditioning current: 60 mA + * - conditioning timeout: 1 h + * - charging voltage: 4.2 V + * - charging current: 200 mA + * - charging threshold current: 30 mA + * - charging timeout: 4 h + * - top off period: 30 min + * - high die temperature: 71 °C + * - low die temperature: 56 °C + * - safe temperature current: 30 mA + */ + +#define IMX233_CHARGE_CURRENT 200 +#define IMX233_STOP_CURRENT 30 +#define IMX233_TOPOFF_TIMEOUT (30 * 60 * HZ) +#define IMX233_CHARGING_TIMEOUT (4 * 3600 * HZ) + +#endif /* POWERMGMT_TARGET_H */ -- cgit v1.2.3