diff --git a/firmware/export/config/sansaclipplus.h b/firmware/export/config/sansaclipplus.h
index 79a7776..059f4b8 100644
--- a/firmware/export/config/sansaclipplus.h
+++ b/firmware/export/config/sansaclipplus.h
@@ -173,10 +173,8 @@

#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_ARC

/* enable these for the experimental usb stack */
#define HAVE_USBSTACK
@@ -184,8 +182,6 @@
#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 87a1447..a0ededf 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
+ 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;
@@ -436,12 +433,15 @@ static void ascodec_read_cb(unsigned const char *data, unsigned int len)
return;

if (data[0] & 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) {
+ ascodec_enrd0_shadow |= CHG_STATUS;
IFDEBUG(int_chg_insert++);
} else {
+ ascodec_enrd0_shadow &= ~CHG_STATUS;
IFDEBUG(int_chg_remove++);
}
}
@@ -468,23 +468,69 @@ 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
+ while(!(ascodec_read(AS3514_IRQ_ENRD2) & IRQ_ADC))
+ yield();
+#endif
}
-#endif /* CONFIG_CPU == AS3525 */

+#ifdef CONFIG_CHARGING

-void ascodec_enable_endofch_irq(void)
+#if CONFIG_CPU != AS3525
+/* since each read of enrd0 register will clear the interrupts, we must
+ * centralize all the reads, cache the results, and clear individual bits when
+ * they are requested
+ */
+void read_enrd0(void)
{
- ascodec_write(AS3514_IRQ_ENRD0, ascodec_enrd0_shadow | CHG_ENDOFCH);
+ int enrd0 = ascodec_read(AS3514_IRQ_ENRD0);
+
+ if (enrd0 & CHG_ENDOFCH) { /* chg finished */
+ ascodec_enrd0_shadow |= CHG_ENDOFCH;
+ IFDEBUG(int_chg_finished++);
+ }
+ if (enrd0 & CHG_CHANGED) { /* chg status changed */
+ if (enrd0 & CHG_STATUS) {
+ ascodec_enrd0_shadow |= CHG_STATUS;
+ } else {
+ ascodec_enrd0_shadow &= ~CHG_STATUS;
+ }
+ }
+ if (enrd0 & USB_CHANGED) { /* usb status changed */
+ if (enrd0 & USB_STATUS) {
+ usb_insert_int();
+ } else {
+ usb_remove_int();
+ }
+ }
}
+#endif

-void ascodec_disable_endofch_irq(void)
+bool ascodec_endofch(void)
{
- ascodec_write(AS3514_IRQ_ENRD0, ascodec_enrd0_shadow & ~CHG_ENDOFCH);
+#if CONFIG_CPU != AS3525
+ read_enrd0();
+#endif
+ bool ret = ascodec_enrd0_shadow & CHG_ENDOFCH;
+ ascodec_enrd0_shadow &= ~CHG_ENDOFCH; // clear interrupt
+ return ret;
}

+bool ascodec_chg_status(void)
+{
+#if CONFIG_CPU != AS3525
+ read_enrd0();
+#endif
+ return ascodec_enrd0_shadow & CHG_STATUS;
+}
+#endif
+
/*
* NOTE:
* After the conversion to interrupts, ascodec_(lock|unlock) are only used by
diff --git a/firmware/target/arm/as3525/ascodec-target.h b/firmware/target/arm/as3525/ascodec-target.h
index 3794754..f20b29c 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
@@ -119,18 +119,13 @@ 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)
-{
- /* FIXME: Doesn't work yet on AS3525v2 */
-}
-#endif

-void ascodec_enable_endofch_irq(void);
+static inline void ascodec_monitor_endofch(void) {} /* already enabled */
+
+bool ascodec_endofch(void);

-void ascodec_disable_endofch_irq(void);
+bool ascodec_chg_status(void);

#endif /* !SIMULATOR */

diff --git a/firmware/target/arm/as3525/power-as3525.c b/firmware/target/arm/as3525/power-as3525.c
index 3570d7c..7b93dd1 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 74bfc17..be62752 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 4c54dc1..6df6d7c 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 c87d869..c655ec5 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 void ascodec_disable_endofch_irq(void)
+static inline bool ascodec_endofch(void)
{
- ascodec_write(AS3514_IRQ_ENRD0, 0);
+ return ascodec_read(AS3514_IRQ_ENRD0) & CHG_ENDOFCH;
+}
+
+static inline void ascodec_monitor_endofch(void)
+{
+ ascodec_write(AS3514_IRQ_ENRD0, IRQ_ENDOFCH);
}

static inline void ascodec_wait_adc_finished(void)
diff --git a/firmware/target/arm/powermgmt-ascodec.c b/firmware/target/arm/powermgmt-ascodec.c
index b463486..90c52aa 100644
--- a/firmware/target/arm/powermgmt-ascodec.c
+++ b/firmware/target/arm/powermgmt-ascodec.c
@@ -94,7 +94,6 @@ 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);

@@ -109,13 +108,12 @@ static void disable_charger(void)
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();

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,7 +123,7 @@ static void enable_charger(void)
void powermgmt_init_target(void)
{
/* Everything CHARGER, OFF! */
- ascodec_disable_endofch_irq();
+ ascodec_monitor_endofch();
ascodec_write(AS3514_CHARGER,
TMPSUP_OFF | CHG_I_50MA | CHG_V_3_90V | CHG_OFF);
}
@@ -148,7 +146,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 +161,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;