1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
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;