From 88c55d7290b7c360075557c40fdf65ceeeaf0c4b Mon Sep 17 00:00:00 2001 From: Rafaël Carré Date: Mon, 17 May 2010 20:53:25 +0000 Subject: as3514/as3543 fixes - Enable end of charge monitoring once, it doesn't need to be disabled - Acknowledge the first (wrong) end of charge interrupt on charger enable (this had been broken in r25299) - Centralize reads to ENRD* registers and cache the results when needed - on PP it is not needed because reads are atomic, we only check for end of charge when the charging, and for charger presence when discharging as3525v2 (using as3543) specifics - I got the datasheet today from AMS, thanks to them for being so fast and not require me to sign tons of papers! - USB detection now works on as3525v2 using the as3543. Clip+ won't reboot to OF yet, it needs mkamsboot support first (usbstack disabled) - Charging should work, the CHARGER register is at a different place, it is an extended PMU register -> use ascodec_read/write_charger() to access it - real interrupts are not used yet for ENRD, we get thousands of interrupts per second, apparently only limited by the i2c clock. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@26116 a1c6a512-1295-4272-9138-f99709370657 --- apps/debug_menu.c | 10 +-- firmware/export/as3514.h | 4 ++ firmware/export/config/sansaclipplus.h | 8 +-- firmware/target/arm/as3525/ascodec-as3525.c | 98 ++++++++++++++++++++--------- firmware/target/arm/as3525/ascodec-target.h | 66 ++++++++++++------- firmware/target/arm/as3525/power-as3525.c | 2 +- firmware/target/arm/as3525/usb-as3525.c | 21 ------- firmware/target/arm/as3525/usb-target.h | 2 - firmware/target/arm/ascodec-target.h | 23 +++++-- firmware/target/arm/powermgmt-ascodec.c | 22 +++---- 10 files changed, 153 insertions(+), 103 deletions(-) diff --git a/apps/debug_menu.c b/apps/debug_menu.c index e8104f74dc..f805dae339 100644 --- a/apps/debug_menu.c +++ b/apps/debug_menu.c @@ -108,7 +108,8 @@ #endif #if defined(SANSA_E200) || defined(SANSA_C200) || defined(PHILIPS_SA9200) \ - || defined(SANSA_CLIP) || defined(SANSA_FUZE) || defined(SANSA_C200V2) + || (CONFIG_CPU == AS3525 && defined(CONFIG_CHARGING)) \ + || CONFIG_CPU == AS3525v2 #include "ascodec.h" #include "as3514.h" #endif @@ -1656,8 +1657,8 @@ static bool view_battery(void) lcd_puts(0, line++, "T Battery: ?"); } -#elif defined(SANSA_E200) || defined(SANSA_C200) || defined(SANSA_CLIP) || \ - defined(SANSA_FUZE) || defined (SANSA_C200V2) +#elif defined(SANSA_E200) || defined(SANSA_C200) || CONFIG_CPU == AS3525 || \ + CONFIG_CPU == AS3525v2 const int first = CHARGE_STATE_DISABLED; static const char * const chrgstate_strings[] = { @@ -1678,8 +1679,7 @@ static bool view_battery(void) lcd_putsf(0, 4, "State: %s", str ? str : ""); - lcd_putsf(0, 5, "CHARGER: %02X", - ascodec_read(AS3514_CHARGER)); + lcd_putsf(0, 5, "CHARGER: %02X", ascodec_read_charger()); #elif defined(IPOD_NANO2G) y = pmu_read_battery_voltage(); lcd_putsf(17, 1, "RAW: %d.%03d V", y / 1000, y % 1000); diff --git a/firmware/export/as3514.h b/firmware/export/as3514.h index df149c274a..6916e2fb4a 100644 --- a/firmware/export/as3514.h +++ b/firmware/export/as3514.h @@ -67,6 +67,10 @@ extern void audiohw_set_lineout_vol(int vol_l, int vol_r); #define AS3514_PLLMODE 0x1d +#ifdef HAVE_AS3543 +#define AS3543_CHARGER 0x19 /* PMU: sub register 1 (CHGVBUS1) */ +#endif + #define AS3514_SYSTEM 0x20 #define AS3514_CVDD_DCDC3 0x21 #define AS3514_CHARGER 0x22 diff --git a/firmware/export/config/sansaclipplus.h b/firmware/export/config/sansaclipplus.h index 79a7776b51..574c3ecc80 100644 --- a/firmware/export/config/sansaclipplus.h +++ b/firmware/export/config/sansaclipplus.h @@ -173,19 +173,15 @@ #define USB_HANDLED_BY_OF -#if 0 /* disabled since there is no USB driver */ - /* USB On-the-go */ -#define CONFIG_USBOTG USBOTG_ARC +#define CONFIG_USBOTG USBOTG_AS3525 /* enable these for the experimental usb stack */ -#define HAVE_USBSTACK +//#define HAVE_USBSTACK #define USB_VENDOR_ID 0x0781 #define USB_PRODUCT_ID 0x74d1 #endif /* BOOTLOADER */ -#endif - /* Virtual LED (icon) */ #define CONFIG_LED LED_VIRTUAL diff --git a/firmware/target/arm/as3525/ascodec-as3525.c b/firmware/target/arm/as3525/ascodec-as3525.c index 87a1447c63..52d50ef077 100644 --- a/firmware/target/arm/as3525/ascodec-as3525.c +++ b/firmware/target/arm/as3525/ascodec-as3525.c @@ -185,10 +185,18 @@ void ascodec_init(void) I2C2_IMR = 0x00; /* disable interrupts */ I2C2_INT_CLR |= I2C2_RIS; /* clear interrupt status */ - VIC_INT_ENABLE = INTERRUPT_I2C_AUDIO | INTERRUPT_AUDIO; + VIC_INT_ENABLE = INTERRUPT_I2C_AUDIO; +#if CONFIG_CPU == AS3525 /* interrupts do not work correctly on as3525v2 */ + VIC_INT_ENABLE = INTERRUPT_AUDIO; +#endif /* Generate irq for usb+charge status change */ - ascodec_write(AS3514_IRQ_ENRD0, /*IRQ_CHGSTAT |*/ IRQ_USBSTAT); + ascodec_write(AS3514_IRQ_ENRD0, +#ifdef CONFIG_CHARGING /* m200v4 can't charge */ + IRQ_CHGSTAT | IRQ_ENDOFCH | +#endif + IRQ_USBSTAT); + /* Generate irq for push-pull, active high, irq on rtc+adc change */ ascodec_write(AS3514_IRQ_ENRD2, IRQ_PUSHPULL | IRQ_HIGHACTIVE | /*IRQ_RTC |*/ IRQ_ADC); @@ -342,19 +350,8 @@ static void ascodec_wait(struct ascodec_request *req) void ascodec_async_write(unsigned int index, unsigned int value, struct ascodec_request *req) { - switch(index) { - case AS3514_CVDD_DCDC3: - /* prevent setting of the LREG_CP_not bit */ + if (index == AS3514_CVDD_DCDC3) /* prevent setting of the LREG_CP_not bit */ value &= ~(1 << 5); - break; - case AS3514_IRQ_ENRD0: - /* save value in register shadow - * for ascodec_(en|dis)able_endofch_irq() */ - ascodec_enrd0_shadow = value; - break; - default: - break; - } ascodec_req_init(req, ASCODEC_REQ_WRITE, index, 1); req->data[0] = value; @@ -429,24 +426,37 @@ int ascodec_readbytes(unsigned int index, unsigned int len, unsigned char *data) return i; } -#if CONFIG_CPU == AS3525 -static void ascodec_read_cb(unsigned const char *data, unsigned int len) +/* + * Reading AS3514_IRQ_ENRD0 clears all interrupt bits, so we cache the results + * and clear individual bits when a specific interrupt is checked: + * - we clear the ENDOFCH (end of charge) interrupt when it's read + * - we set the usb and charger presence when the status change is detected + * + * on AS3525(v1) ENRD0 is only read in an interrupt handler + * on AS3525v2 the interrupt handler doesn't work (yet), so we read the register + * synchronously. + * - To avoid race conditions all the reads to this register must be atomic. + * We don't need to disable interrupts when reading it because all the reads + * (in powermgmt-ascodec.c and power-as3525.c) are performed by the same + * thread (the power thread). + */ +static void cache_enrd0(int enrd0) { - if (len != 3) /* some error happened? */ - return; - - if (data[0] & CHG_ENDOFCH) { /* chg finished */ + if (enrd0 & CHG_ENDOFCH) { /* chg finished */ + ascodec_enrd0_shadow |= CHG_ENDOFCH; IFDEBUG(int_chg_finished++); } - if (data[0] & CHG_CHANGED) { /* chg status changed */ - if (data[0] & CHG_STATUS) { + if (enrd0 & CHG_CHANGED) { /* chg status changed */ + if (enrd0 & CHG_STATUS) { + ascodec_enrd0_shadow |= CHG_STATUS; IFDEBUG(int_chg_insert++); } else { + ascodec_enrd0_shadow &= ~CHG_STATUS; IFDEBUG(int_chg_remove++); } } - if (data[0] & USB_CHANGED) { /* usb status changed */ - if (data[0] & USB_STATUS) { + if (enrd0 & USB_CHANGED) { /* usb status changed */ + if (enrd0 & USB_STATUS) { IFDEBUG(int_usb_insert++); usb_insert_int(); } else { @@ -454,6 +464,16 @@ static void ascodec_read_cb(unsigned const char *data, unsigned int len) usb_remove_int(); } } +} + +#if CONFIG_CPU == AS3525 +static void ascodec_read_cb(unsigned const char *data, unsigned int len) +{ + if (len != 3) /* some error happened? */ + return; + + cache_enrd0(data[0]); + if (data[2] & IRQ_RTC) { /* rtc irq */ /* * Can be configured for once per second or once per minute, @@ -468,22 +488,40 @@ static void ascodec_read_cb(unsigned const char *data, unsigned int len) VIC_INT_ENABLE = INTERRUPT_AUDIO; } +#endif /* CONFIG_CPU == AS3525 */ + void ascodec_wait_adc_finished(void) { +#if CONFIG_CPU == AS3525 wakeup_wait(&adc_wkup, TIMEOUT_BLOCK); +#else + /* no interrupts, busy wait + * XXX: make sure this is the only reader of IRQ_ENRD2 + */ + while(!(ascodec_read(AS3514_IRQ_ENRD2) & IRQ_ADC)) + yield(); +#endif } -#endif /* CONFIG_CPU == AS3525 */ - -void ascodec_enable_endofch_irq(void) +#ifdef CONFIG_CHARGING +bool ascodec_endofch(void) { - ascodec_write(AS3514_IRQ_ENRD0, ascodec_enrd0_shadow | CHG_ENDOFCH); +#if CONFIG_CPU != AS3525 + cache_enrd0(ascodec_read(AS3514_IRQ_ENRD0)); +#endif + bool ret = ascodec_enrd0_shadow & CHG_ENDOFCH; + ascodec_enrd0_shadow &= ~CHG_ENDOFCH; // clear interrupt + return ret; } -void ascodec_disable_endofch_irq(void) +bool ascodec_chg_status(void) { - ascodec_write(AS3514_IRQ_ENRD0, ascodec_enrd0_shadow & ~CHG_ENDOFCH); +#if CONFIG_CPU != AS3525 + cache_enrd0(ascodec_read(AS3514_IRQ_ENRD0)); +#endif + return ascodec_enrd0_shadow & CHG_STATUS; } +#endif /* CONFIG_CHARGING */ /* * NOTE: diff --git a/firmware/target/arm/as3525/ascodec-target.h b/firmware/target/arm/as3525/ascodec-target.h index 37947541c2..8ce9a428d3 100644 --- a/firmware/target/arm/as3525/ascodec-target.h +++ b/firmware/target/arm/as3525/ascodec-target.h @@ -52,7 +52,7 @@ /* * How many bytes we using in struct ascodec_request for the data buffer. * 4 fits the alignment best right now. - * We don't actually use more than 2 at the moment (in adc_read). + * We don't actually use more than 3 at the moment (when reading interrupts) * Upper limit would be 255 since DACNT is 8 bits! */ #define ASCODEC_REQ_MAXLEN 4 @@ -74,19 +74,6 @@ void ascodec_init(void); int ascodec_write(unsigned int index, unsigned int value); -#if CONFIG_CPU == AS3525v2 -static inline void ascodec_write_pmu(unsigned int index, unsigned int subreg, - unsigned int value) -{ - /* we disable interrupts to make sure no operation happen on the i2c bus - * between selecting the sub register and writing to it */ - int oldstatus = disable_irq_save(); - ascodec_write(AS3543_PMU_ENABLE, 8|subreg); - ascodec_write(index, value); - restore_irq(oldstatus); -} -#endif - int ascodec_read(unsigned int index); int ascodec_readbytes(unsigned int index, unsigned int len, unsigned char *data); @@ -119,18 +106,55 @@ void ascodec_lock(void); void ascodec_unlock(void); -#if CONFIG_CPU == AS3525 void ascodec_wait_adc_finished(void); -#else -static inline void ascodec_wait_adc_finished(void) + +static inline void ascodec_monitor_endofch(void) {} /* already enabled */ + +bool ascodec_endofch(void); + +bool ascodec_chg_status(void); + +#if CONFIG_CPU == AS3525v2 +static inline void ascodec_write_pmu(unsigned int index, unsigned int subreg, + unsigned int value) +{ + /* we disable interrupts to make sure no operation happen on the i2c bus + * between selecting the sub register and writing to it */ + int oldstatus = disable_irq_save(); + ascodec_write(AS3543_PMU_ENABLE, 8|subreg); + ascodec_write(index, value); + restore_irq(oldstatus); +} + +static inline int ascodec_read_pmu(unsigned int index, unsigned int subreg) { - /* FIXME: Doesn't work yet on AS3525v2 */ + /* we disable interrupts to make sure no operation happen on the i2c bus + * between selecting the sub register and reading it */ + int oldstatus = disable_irq_save(); + ascodec_write(AS3543_PMU_ENABLE, 8|subreg); + int ret = ascodec_read(index); + restore_irq(oldstatus); + return ret; } -#endif +#endif /* CONFIG_CPU == AS3525v2 */ -void ascodec_enable_endofch_irq(void); +static inline void ascodec_write_charger(int value) +{ +#if CONFIG_CPU == AS3525 + ascodec_write(AS3514_CHARGER, value); +#else + ascodec_write_pmu(AS3543_CHARGER, 1, value); +#endif +} -void ascodec_disable_endofch_irq(void); +static inline int ascodec_read_charger(void) +{ +#if CONFIG_CPU == AS3525 + return ascodec_read(AS3514_CHARGER); +#else + return ascodec_read_pmu(AS3543_CHARGER, 1); +#endif +} #endif /* !SIMULATOR */ diff --git a/firmware/target/arm/as3525/power-as3525.c b/firmware/target/arm/as3525/power-as3525.c index 3570d7c75a..7b93dd1cd1 100644 --- a/firmware/target/arm/as3525/power-as3525.c +++ b/firmware/target/arm/as3525/power-as3525.c @@ -41,7 +41,7 @@ void power_init(void) #if CONFIG_CHARGING unsigned int power_input_status(void) { - return (ascodec_read(AS3514_IRQ_ENRD0) & (1<<5)) ? + return ascodec_chg_status() ? POWER_INPUT_MAIN_CHARGER : POWER_INPUT_NONE; /* TODO: Handle USB and other sources properly */ diff --git a/firmware/target/arm/as3525/usb-as3525.c b/firmware/target/arm/as3525/usb-as3525.c index 74bfc17364..be62752033 100644 --- a/firmware/target/arm/as3525/usb-as3525.c +++ b/firmware/target/arm/as3525/usb-as3525.c @@ -29,13 +29,7 @@ #include "power.h" #include "as3525.h" -#if CONFIG_CPU == AS3525 static int usb_status = USB_EXTRACTED; -#else -#if defined(SANSA_CLIPV2) -#define USB_DETECT_PIN 6 -#endif -#endif void usb_enable(bool on) { @@ -51,12 +45,8 @@ void usb_enable(bool on) void usb_init_device(void) { -#ifdef USB_DETECT_PIN - GPIOA_DIR &= ~(1 << USB_DETECT_PIN); /* set as input */ -#endif } -#if CONFIG_CPU == AS3525 void usb_insert_int(void) { usb_status = USB_INSERTED; @@ -71,14 +61,3 @@ int usb_detect(void) { return usb_status; } -#else -int usb_detect(void) -{ -#ifdef USB_DETECT_PIN - if (GPIOA_PIN( USB_DETECT_PIN )) - return USB_INSERTED; - else -#endif - return USB_EXTRACTED; -} -#endif diff --git a/firmware/target/arm/as3525/usb-target.h b/firmware/target/arm/as3525/usb-target.h index 4c54dc182d..6df6d7c1d5 100644 --- a/firmware/target/arm/as3525/usb-target.h +++ b/firmware/target/arm/as3525/usb-target.h @@ -23,9 +23,7 @@ void usb_init_device(void); int usb_detect(void); -#if CONFIG_CPU == AS3525 void usb_insert_int(void); void usb_remove_int(void); -#endif /* CONFIG_CPU == AS3525 */ #endif /* USB_TARGET_H */ diff --git a/firmware/target/arm/ascodec-target.h b/firmware/target/arm/ascodec-target.h index c87d869ebb..68d9905a6b 100644 --- a/firmware/target/arm/ascodec-target.h +++ b/firmware/target/arm/ascodec-target.h @@ -59,14 +59,19 @@ static inline void ascodec_unlock(void) i2c_unlock(); } -static inline void ascodec_enable_endofch_irq(void) +static inline bool ascodec_chg_status(void) { - ascodec_write(AS3514_IRQ_ENRD0, IRQ_ENDOFCH); + return ascodec_read(AS3514_IRQ_ENRD0) & CHG_STATUS; +} + +static inline bool ascodec_endofch(void) +{ + return ascodec_read(AS3514_IRQ_ENRD0) & CHG_ENDOFCH; } -static inline void ascodec_disable_endofch_irq(void) +static inline void ascodec_monitor_endofch(void) { - ascodec_write(AS3514_IRQ_ENRD0, 0); + ascodec_write(AS3514_IRQ_ENRD0, IRQ_ENDOFCH); } static inline void ascodec_wait_adc_finished(void) @@ -81,6 +86,16 @@ static inline void ascodec_wait_adc_finished(void) */ } +static inline void ascodec_write_charger(int value) +{ + ascodec_write(AS3514_CHARGER, value); +} + +static inline int ascodec_read_charger(void) +{ + return ascodec_read(AS3514_CHARGER); +} + extern void ascodec_suppressor_on(bool on); #endif /* CPU_PP */ diff --git a/firmware/target/arm/powermgmt-ascodec.c b/firmware/target/arm/powermgmt-ascodec.c index b463486346..e50367fe93 100644 --- a/firmware/target/arm/powermgmt-ascodec.c +++ b/firmware/target/arm/powermgmt-ascodec.c @@ -94,9 +94,7 @@ static void battery_voltage_sync(void) /* Disable charger and minimize all settings. Reset timers, etc. */ static void disable_charger(void) { - ascodec_disable_endofch_irq(); - ascodec_write(AS3514_CHARGER, - TMPSUP_OFF | CHG_I_50MA | CHG_V_3_90V | CHG_OFF); + ascodec_write_charger(TMPSUP_OFF | CHG_I_50MA | CHG_V_3_90V | CHG_OFF); if (charge_state > DISCHARGING) charge_state = DISCHARGING; /* Not an error state already */ @@ -108,14 +106,13 @@ static void disable_charger(void) /* Enable charger with specified settings. Start timers, etc. */ static void enable_charger(void) { - ascodec_write(AS3514_CHARGER, BATT_CHG_I | BATT_CHG_V); - /* Watch for end of charge. Temperature supervision is handled in - * hardware. Charger status can be read and has no interrupt enable. */ - ascodec_enable_endofch_irq(); + ascodec_write_charger(BATT_CHG_I | BATT_CHG_V); sleep(HZ/10); /* Allow charger turn-on time (it could be gradual). */ - ascodec_disable_endofch_irq(); + /* acknowledge first end of charging interrupt, it seems to happen both + * at charger plug and charger unplug */ + ascodec_endofch(); charge_state = CHARGING; charger_total_timer = CHARGER_TOTAL_TIMER; @@ -125,9 +122,8 @@ static void enable_charger(void) void powermgmt_init_target(void) { /* Everything CHARGER, OFF! */ - ascodec_disable_endofch_irq(); - ascodec_write(AS3514_CHARGER, - TMPSUP_OFF | CHG_I_50MA | CHG_V_3_90V | CHG_OFF); + ascodec_monitor_endofch(); + ascodec_write_charger(TMPSUP_OFF | CHG_I_50MA | CHG_V_3_90V | CHG_OFF); } static inline void charger_plugged(void) @@ -148,7 +144,7 @@ static inline void charger_control(void) if (BATT_FULL_VOLTAGE == thresh) { /* Wait for CHG_status to be indicated. */ - if ((ascodec_read(AS3514_IRQ_ENRD0) & CHG_STATUS) == 0) + if (!ascodec_chg_status()) break; batt_threshold = BATT_VAUTO_RECHARGE; @@ -163,7 +159,7 @@ static inline void charger_control(void) case CHARGING: { - if ((ascodec_read(AS3514_IRQ_ENRD0) & CHG_ENDOFCH) == 0) + if (!ascodec_endofch()) { if (--charger_total_timer > 0) break; -- cgit v1.2.3