summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRafaël Carré <rafael.carre@gmail.com>2010-05-17 20:53:25 +0000
committerRafaël Carré <rafael.carre@gmail.com>2010-05-17 20:53:25 +0000
commit88c55d7290b7c360075557c40fdf65ceeeaf0c4b (patch)
tree29cf685626bf8c01bb64ed9456850f62e4a57d7d
parent2ed7745ddefc084ca7030d48b251882e94fa2f9c (diff)
downloadrockbox-88c55d7290b7c360075557c40fdf65ceeeaf0c4b.tar.gz
rockbox-88c55d7290b7c360075557c40fdf65ceeeaf0c4b.zip
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
-rw-r--r--apps/debug_menu.c10
-rw-r--r--firmware/export/as3514.h4
-rw-r--r--firmware/export/config/sansaclipplus.h8
-rw-r--r--firmware/target/arm/as3525/ascodec-as3525.c98
-rw-r--r--firmware/target/arm/as3525/ascodec-target.h66
-rw-r--r--firmware/target/arm/as3525/power-as3525.c2
-rw-r--r--firmware/target/arm/as3525/usb-as3525.c21
-rw-r--r--firmware/target/arm/as3525/usb-target.h2
-rw-r--r--firmware/target/arm/ascodec-target.h23
-rw-r--r--firmware/target/arm/powermgmt-ascodec.c22
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 : "<unknown>");
- 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;