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;