summaryrefslogtreecommitdiff
path: root/firmware/target/arm
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2017-01-21 08:04:43 -0500
committerGerrit Rockbox <gerrit@rockbox.org>2017-01-25 00:05:13 +0100
commit783c77531c35e62dd754c510c4f2beefe6df4a9d (patch)
tree79e747eb1ba3716b56e5178145536f316c44e403 /firmware/target/arm
parenta1d1832049146925400b57c4cd81b0739b674971 (diff)
downloadrockbox-783c77531c35e62dd754c510c4f2beefe6df4a9d.tar.gz
rockbox-783c77531c35e62dd754c510c4f2beefe6df4a9d.zip
AMS: Return ascodec to interrupt-based I2C2 driver
1. Slightly revised and regularized internal interface. Callback is used for read and write to provide completion signal instead of having two mechanisms. 2. Lower overhead for asynchronous or alterate completion callbacks. We now only init what is required by the transfer. A couple unneeded structure members were also nixed. 3. Fixes a bug that would neglect a semaphore wait if pumping the I2C interrupts in a loop when not in thread state or interrupts are masked. 4. Corrects broken initialization order by defining KDEV_INIT, which makes kernel_init() call kernel_device_init() to initialize additional devices _after_ the kernel, threading and synchronization objects are safe to use. 5. Locking set_cpu_frequency has to be done at the highest level in system.c to ensure the boost counter and the frequency are both set in agreement. Reconcile the locking inteface between PP and AMS (the only two currently using locking there) to keep it clean. Now works fine with voltages in GIT HEAD on my Fuze v2, type 0. Previously, everything crashed and died instantly. action.c calling set_cpu_frequency from a tick was part of it. The rest may have been related to 3. and 4. Honestly, I'm not certain! Testing by Mihail Zenkov indicates it solves our problems. This will get the developer builds running again after the kernel assert code push. Change-Id: Ie245994fb3e318dd5ef48e383ce61fdd977224d4
Diffstat (limited to 'firmware/target/arm')
-rw-r--r--firmware/target/arm/as3525/ascodec-as3525.c571
-rw-r--r--firmware/target/arm/as3525/debug-as3525.c30
-rw-r--r--firmware/target/arm/as3525/system-as3525.c37
-rw-r--r--firmware/target/arm/as3525/system-target.h21
-rw-r--r--firmware/target/arm/pp/system-pp5002.c19
-rw-r--r--firmware/target/arm/pp/system-pp502x.c21
-rw-r--r--firmware/target/arm/pp/system-target.h17
7 files changed, 583 insertions, 133 deletions
diff --git a/firmware/target/arm/as3525/ascodec-as3525.c b/firmware/target/arm/as3525/ascodec-as3525.c
index ec25a415a5..e144f07ed4 100644
--- a/firmware/target/arm/as3525/ascodec-as3525.c
+++ b/firmware/target/arm/as3525/ascodec-as3525.c
@@ -50,7 +50,7 @@
50#include "system.h" 50#include "system.h"
51#include "as3525.h" 51#include "as3525.h"
52#include "i2c.h" 52#include "i2c.h"
53#include "logf.h" 53#include <linked_list.h>
54 54
55#define I2C2_DATA *((volatile unsigned int *)(I2C_AUDIO_BASE + 0x00)) 55#define I2C2_DATA *((volatile unsigned int *)(I2C_AUDIO_BASE + 0x00))
56#define I2C2_SLAD0 *((volatile unsigned int *)(I2C_AUDIO_BASE + 0x04)) 56#define I2C2_SLAD0 *((volatile unsigned int *)(I2C_AUDIO_BASE + 0x04))
@@ -78,234 +78,495 @@
78#define I2C2_IRQ_RXOVER 0x10 78#define I2C2_IRQ_RXOVER 0x10
79#define I2C2_IRQ_ACKTIMEO 0x80 79#define I2C2_IRQ_ACKTIMEO 0x80
80 80
81#define REQ_UNFINISHED 0
82#define REQ_FINISHED 1
83#define REQ_RETRY 2
81 84
82static struct mutex as_mtx; 85#ifdef DEBUG
83 86#define IFDEBUG(x) x
84#if CONFIG_CHARGING 87#else
85static bool chg_status = false; 88#define IFDEBUG(x)
86static bool endofch = false;
87#endif 89#endif
88 90
89/* returns true when busy */ 91#define ASCODEC_REQ_READ 0
90static inline bool i2c_busy(void) 92#define ASCODEC_REQ_WRITE 1
93
94/*
95 * How many bytes we using in struct ascodec_request for the data buffer.
96 * 4 fits the alignment best right now.
97 * We don't actually use more than 3 at the moment (when reading interrupts)
98 * Upper limit would be 255 since DACNT is 8 bits!
99 */
100#define ASCODEC_REQ_MAXLEN 4
101
102struct ascodec_request;
103typedef void (ascodec_cb_fn)(struct ascodec_request *req);
104
105struct ascodec_request {
106 /* standard request members */
107 struct ll_node node; /* request list link (first!) */
108 unsigned char type; /* reqest type (read or write) */
109 unsigned char index; /* initial i2c sub address */
110 unsigned char cnt; /* bytes remaining */
111 unsigned char data[ASCODEC_REQ_MAXLEN]; /* actual I/O data */
112
113 /* members relevant when a callback is specified (callback != NULL) */
114 ascodec_cb_fn *callback; /* pointer to callback function */
115 intptr_t cbdata; /* data for callback function */
116 int len_done; /* amount actually transferred */
117};
118
119/* I2C driver data */
120static struct mutex as_mtx;
121static struct ll_head req_list;
122static unsigned char *req_data_ptr;
123#define REQ_FIRST ((struct ascodec_request *)req_list.head)
124
125/* INT_AUDIO interrupt data */
126static void ascodec_int_audio_cb(struct ascodec_request *req);
127void INT_I2C_AUDIO(void);
128static struct ascodec_request as_audio_req;
129static struct semaphore adc_done_sem;
130static unsigned long ascodec_enrd0_shadow = 0;
131
132static void ascodec_wait_cb(struct ascodec_request *req);
133
134/** --debugging help-- **/
135
136#ifdef DEBUG
137/* counters for debugging INT_AUDIO */
138static struct int_audio_counters {
139 int int_audio;
140 int int_chg_finished;
141 int int_chg_insert;
142 int int_chg_remove;
143 int int_usb_insert;
144 int int_usb_remove;
145 int int_rtc;
146 int int_adc;
147} int_audio_counters;
148#endif /* DEBUG */
149
150#define COUNT_INT(x) IFDEBUG((int_audio_counters.int_##x)++)
151
152
153/** --stock request and callback functionality -- **/
154
155/* init for common request data (call before submitting) */
156static inline void ascodec_req_init(struct ascodec_request *req, int type,
157 unsigned int index, unsigned int cnt)
91{ 158{
92 return (I2C2_SR & 1); 159 req->type = type;
160 req->index = index;
161 req->cnt = cnt;
93} 162}
94 163
95void i2c_init(void) 164/* stock no-wait init for request (use any callback and data) */
165static inline void ascodec_async_init(struct ascodec_request *req,
166 ascodec_cb_fn *callback, intptr_t cbdata)
96{ 167{
168 /* cbdata is unused if no callback is used */
169 if ((req->callback = callback))
170 req->cbdata = cbdata;
97} 171}
98 172
99static void i2c2_init(void) 173/* initialize the stock completion callback */
174static inline void ascodec_wait_init(struct ascodec_request *req,
175 struct semaphore *completep)
100{ 176{
101 int prescaler; 177 req->callback = ascodec_wait_cb;
102 178 req->cbdata = (intptr_t)completep;
103 /* prescaler for i2c clock */ 179 semaphore_init(completep, 1, 0);
104 prescaler = AS3525_I2C_PRESCALER; 180}
105 I2C2_CPSR0 = prescaler & 0xFF; /* 8 lsb */
106 I2C2_CPSR1 = (prescaler >> 8) & 0x3; /* 2 msb */
107 181
108 /* set i2c slave address of codec part */ 182/* caller waits here when using ascodec_wait_cb to do synchronous transfers */
109 I2C2_SLAD0 = AS3514_I2C_ADDR << 1; 183static void ascodec_wait(struct ascodec_request *req)
184{
185 struct semaphore *completep = (struct semaphore *)req->cbdata;
186 int timeout = TIMEOUT_BLOCK;
110 187
111 I2C2_CNTRL = I2C2_CNTRL_DEFAULT; 188 if (!irq_enabled() || !is_thread_context()) {
189 timeout = TIMEOUT_NOBLOCK; /* poll semaphore, no block */
190 }
112 191
192 while (semaphore_wait(completep, timeout) == OBJ_WAIT_TIMEDOUT) {
193 /* pump the i2c interrupts ourselves (only waiting can do this!) */
194 if (I2C2_MIS) {
195 INT_I2C_AUDIO();
196 }
197 }
113} 198}
114 199
115/* initialises the internal i2c bus and prepares for transfers to the codec */ 200/* stock callback used in order to wait for a transfer to complete */
116void ascodec_init(void) 201static void ascodec_wait_cb(struct ascodec_request *req)
117{ 202{
203 struct semaphore *completep = (struct semaphore *)req->cbdata;
204 semaphore_release(completep);
205}
118 206
119 mutex_init(&as_mtx); 207
208/**-- I2C2 interrupt handling --**/
209
210/* start the controller on the next transfer */
211static void ascodec_start_req(struct ascodec_request *req)
212{
213 int unmask = 0;
120 214
121 /* enable clock */ 215 /* enable clock */
122 bitset32(&CGU_PERI, CGU_I2C_AUDIO_MASTER_CLOCK_ENABLE); 216 bitset32(&CGU_PERI, CGU_I2C_AUDIO_MASTER_CLOCK_ENABLE);
123 217
124 i2c2_init(); 218 /* start transfer */
219 I2C2_SADDR = req->index;
220
221 if (req->type == ASCODEC_REQ_READ) {
222 req_data_ptr = req->data;
223 I2C2_CNTRL = I2C2_CNTRL_DEFAULT | I2C2_CNTRL_READ;
224 unmask = I2C2_IRQ_RXFULL|I2C2_IRQ_RXOVER;
225 } else {
226 req_data_ptr = &req->data[1];
227 I2C2_CNTRL = I2C2_CNTRL_DEFAULT | I2C2_CNTRL_WRITE;
228 I2C2_DATA = req->data[0];
229 unmask = I2C2_IRQ_TXEMPTY|I2C2_IRQ_ACKTIMEO;
230 }
125 231
126 /* Generate irq for usb+charge status change */ 232 I2C2_DACNT = req->cnt;
127 ascodec_write(AS3514_IRQ_ENRD0, 233 I2C2_IMR |= unmask; /* enable interrupts */
128#if CONFIG_CHARGING /* m200v4 can't charge */ 234}
129 IRQ_CHGSTAT | IRQ_ENDOFCH |
130#endif
131 IRQ_USBSTAT);
132 235
133#if CONFIG_CPU == AS3525v2 236/* send the next bytes or read bytes received */
134 /* XIRQ = IRQ, active low reset signal, 6mA push-pull output */ 237static int ascodec_continue_req(struct ascodec_request *req, int irq_status)
135 ascodec_write_pmu(0x1a, 3, (1<<2)|3); /* 1A-3 = Out_Cntr3 register */ 238{
136 /* reset for compatible with old bootloader */ 239 if ((irq_status & (I2C2_IRQ_RXOVER|I2C2_IRQ_ACKTIMEO)) > 0) {
137 ascodec_write(AS3514_IRQ_ENRD2, 0x0); 240 /* some error occured, restart the request */
138#else 241 return REQ_RETRY;
139 /* Generate irq for push-pull, active high, irq on rtc+adc change */ 242 }
140 ascodec_write(AS3514_IRQ_ENRD2, IRQ_PUSHPULL | IRQ_HIGHACTIVE);
141#endif
142 243
143 VIC_INT_ENABLE = INTERRUPT_AUDIO; 244 if (req->type == ASCODEC_REQ_READ &&
245 (irq_status & I2C2_IRQ_RXFULL) > 0) {
246 *req_data_ptr++ = I2C2_DATA;
247 } else {
248 if (req->cnt > 1 &&
249 (irq_status & I2C2_IRQ_TXEMPTY) > 0) {
250 I2C2_DATA = *req_data_ptr++;
251 }
252 }
144 253
145 /* detect if USB was connected at startup since there is no transition */ 254 req->index++;
146 int data = ascodec_read(AS3514_IRQ_ENRD0); 255 if (--req->cnt > 0)
256 return REQ_UNFINISHED;
147 257
148 if(data & USB_STATUS) 258 return REQ_FINISHED;
149 usb_insert_int(); 259}
150 260
151#if CONFIG_CHARGING 261/* complete the request and call the completion callback, if any */
152 chg_status = data & CHG_STATUS; 262static void ascodec_finish_req(struct ascodec_request *req)
153#endif 263{
264 /*
265 * Wait if still busy, unfortunately this happens since
266 * the controller is running at a low divisor, so it's
267 * still busy when we serviced the interrupt.
268 * I tried upping the i2c speed to 4MHz which
269 * made the number of busywait cycles much smaller
270 * (none for reads and only a few for writes),
271 * but who knows if it's reliable at that frequency. ;)
272 * For one thing, 8MHz doesn't work, so 4MHz is likely
273 * borderline.
274 * In general writes need much more wait cycles than reads
275 * for some reason, possibly because we read the data register
276 * for reads, which will likely block the processor while
277 * the i2c controller responds to the register read.
278 */
279 while (I2C2_SR & 1);
280
281 if (req->callback) {
282 req->len_done = req_data_ptr - req->data;
283 req->callback(req);
284 }
154} 285}
155 286
156/* returns false if transfer incomplete */ 287/* ISR for I2C2 */
157static bool i2c2_transfer(void) 288void INT_I2C_AUDIO(void)
158{ 289{
159 static int try = 0; 290 struct ascodec_request *req = REQ_FIRST;
160 291
161 /* wait for transfer*/ 292 int irq_status = I2C2_MIS;
162 int i = 10000; 293 int status = ascodec_continue_req(req, irq_status);
163 while (I2C2_DACNT != 0 && i--);
164 294
165 if (!i) { 295 I2C2_INT_CLR = irq_status; /* clear interrupt status */
166 if (try == 5)
167 panicf("I2C2 reset failed");
168 296
169 logf("reset I2C2 %d", try); 297 if (status != REQ_UNFINISHED) {
298 /* mask rx/tx interrupts */
299 I2C2_IMR &= ~(I2C2_IRQ_TXEMPTY|I2C2_IRQ_RXFULL|
300 I2C2_IRQ_RXOVER|I2C2_IRQ_ACKTIMEO);
170 301
171 i2c2_init(); 302 if (status == REQ_FINISHED)
303 ascodec_finish_req(req);
172 304
173 try++; 305 int oldlevel = disable_irq_save(); /* IRQs are stacked */
174 return false; 306
175 } 307 /*
308 * If status == REQ_RETRY, this will restart the request from where
309 * it left off because we didn't remove it from the request list
310 */
176 311
177 try = 0; 312 if (status == REQ_FINISHED) {
178 return true; 313 ll_remove_next(&req_list, NULL);
314 }
315
316 req = REQ_FIRST;
317 if (req == NULL) {
318 /* disable clock */
319 bitclr32(&CGU_PERI, CGU_I2C_AUDIO_MASTER_CLOCK_ENABLE);
320 } else {
321 ascodec_start_req(req);
322 }
323
324 restore_irq(oldlevel);
325 }
179} 326}
180 327
181void ascodec_write(unsigned int index, unsigned int value) 328
329/** --Routines for reading and writing data on the bus-- **/
330
331/* add the request to the queue */
332static void ascodec_submit(struct ascodec_request *req)
182{ 333{
183 ascodec_lock(); 334 int oldlevel = disable_irq_save();
335
336 ll_insert_last(&req_list, &req->node);
337
338 if (REQ_FIRST == req) {
339 /* first on list? start driver */
340 ascodec_start_req(req);
341 }
342
343 restore_irq(oldlevel);
344}
184 345
346/*
347 * The request struct passed in must be allocated statically.
348 * If you call ascodec_async_write from different places, each
349 * call needs it's own request struct.
350 */
351static void ascodec_async_write(struct ascodec_request *req,
352 unsigned int index, unsigned int value)
353{
185#ifndef HAVE_AS3543 354#ifndef HAVE_AS3543
186 if (index == AS3514_CVDD_DCDC3) /* prevent setting of the LREG_CP_not bit */ 355 if (index == AS3514_CVDD_DCDC3) /* prevent setting of the LREG_CP_not bit */
187 value &= ~(1 << 5); 356 value &= ~(1 << 5);
188#endif 357#endif
189 358
190 do { 359 ascodec_req_init(req, ASCODEC_REQ_WRITE, index, 1);
191 /* wait if still busy */ 360 req->data[0] = value;
192 while (i2c_busy()); 361 ascodec_submit(req);
362}
363
364void ascodec_write(unsigned int index, unsigned int value)
365{
366 struct ascodec_request req;
367 struct semaphore complete;
193 368
194 /* start transfer */ 369 ascodec_wait_init(&req, &complete);
195 I2C2_SADDR = index; 370 ascodec_async_write(&req, index, value);
196 I2C2_CNTRL &= ~(1 << 1); 371 ascodec_wait(&req);
197 I2C2_DATA = value; 372}
198 I2C2_DACNT = 1;
199 373
200 } while (!i2c2_transfer()); 374/*
375 * The request struct passed in must be allocated statically.
376 * If you call ascodec_async_read from different places, each
377 * call needs it's own request struct.
378 * If len is bigger than ASCODEC_REQ_MAXLEN it will be
379 * set to ASCODEC_REQ_MAXLEN.
380 */
381static void ascodec_async_read(struct ascodec_request *req,
382 unsigned int index, unsigned int len)
383{
384 if (len > ASCODEC_REQ_MAXLEN)
385 len = ASCODEC_REQ_MAXLEN; /* can't fit more in one request */
201 386
202 ascodec_unlock(); 387 ascodec_req_init(req, ASCODEC_REQ_READ, index, len);
388 ascodec_submit(req);
203} 389}
204 390
391/* read data synchronously */
205int ascodec_read(unsigned int index) 392int ascodec_read(unsigned int index)
206{ 393{
207 int data; 394 struct ascodec_request req;
395 struct semaphore complete;
208 396
209 ascodec_lock(); 397 ascodec_wait_init(&req, &complete);
398 ascodec_async_read(&req, index, 1);
399 ascodec_wait(&req);
210 400
211 do { 401 return req.data[0];
212 /* wait if still busy */ 402}
213 while (i2c_busy());
214 403
215 /* start transfer */ 404/* read an array of bytes */
216 I2C2_SADDR = index; 405void ascodec_readbytes(unsigned int index, unsigned int len, unsigned char *data)
217 I2C2_CNTRL |= (1 << 1); 406{
218 I2C2_DACNT = 1; 407 struct ascodec_request req;
408 struct semaphore complete;
219 409
220 } while (!i2c2_transfer()); 410 ascodec_wait_init(&req, &complete);
221 411
222 data = I2C2_DATA; 412 /* index and cnt will be filled in later, just use 0 */
413 ascodec_req_init(&req, ASCODEC_REQ_READ, 0, 0);
223 414
224 ascodec_unlock(); 415 int i = 0;
225 416
226 return data; 417 while (len > 0) {
227} 418 int cnt = MIN(len, ASCODEC_REQ_MAXLEN);
228 419
229void ascodec_readbytes(unsigned int index, unsigned int len, unsigned char *data) 420 req.index = index;
230{ 421 req.cnt = cnt;
231 unsigned int i; 422
423 ascodec_submit(&req);
424 ascodec_wait(&req);
425
426 for (int j = 0; j < cnt; j++)
427 data[i++] = req.data[j];
232 428
233 for(i = 0; i < len; i++) 429 len -= cnt;
234 data[i] = ascodec_read(index+i); 430 index += cnt;
431 }
235} 432}
236 433
237#if CONFIG_CPU == AS3525v2 434#if CONFIG_CPU == AS3525v2
435/* write special PMU subregisters */
238void ascodec_write_pmu(unsigned int index, unsigned int subreg, 436void ascodec_write_pmu(unsigned int index, unsigned int subreg,
239 unsigned int value) 437 unsigned int value)
240{ 438{
241 ascodec_lock(); 439 struct ascodec_request reqs[2];
440 struct semaphore complete;
441
442 ascodec_async_init(&reqs[0], NULL, 0);
443 ascodec_wait_init(&reqs[1], &complete);
444
445 int oldstatus = disable_irq_save();
242 /* we submit consecutive requests to make sure no operations happen on the 446 /* we submit consecutive requests to make sure no operations happen on the
243 * i2c bus between selecting the sub register and writing to it */ 447 * i2c bus between selecting the sub register and writing to it */
244 ascodec_write(AS3543_PMU_ENABLE, 8 | subreg); 448 ascodec_async_write(&reqs[0], AS3543_PMU_ENABLE, 8 | subreg);
245 ascodec_write(index, value); 449 ascodec_async_write(&reqs[1], index, value);
450 restore_irq(oldstatus);
246 451
247 ascodec_unlock(); 452 /* Wait for second request to finish */
453 ascodec_wait(&reqs[1]);
248} 454}
249 455
456/* read special PMU subregisters */
250int ascodec_read_pmu(unsigned int index, unsigned int subreg) 457int ascodec_read_pmu(unsigned int index, unsigned int subreg)
251{ 458{
252 ascodec_lock(); 459 struct ascodec_request reqs[2];
460 struct semaphore complete;
461
462 ascodec_async_init(&reqs[0], NULL, 0);
463 ascodec_wait_init(&reqs[1], &complete);
464
465 int oldstatus = disable_irq_save();
253 /* we submit consecutive requests to make sure no operations happen on the 466 /* we submit consecutive requests to make sure no operations happen on the
254 * i2c bus between selecting the sub register and reading it */ 467 * i2c bus between selecting the sub register and reading it */
255 ascodec_write(AS3543_PMU_ENABLE, subreg); 468 ascodec_async_write(&reqs[0], AS3543_PMU_ENABLE, subreg);
256 int ret = ascodec_read(index); 469 ascodec_async_read(&reqs[1], index, 1);
470 restore_irq(oldstatus);
257 471
258 ascodec_unlock(); 472 /* Wait for second request to finish */
473 ascodec_wait(&reqs[1]);
259 474
260 return ret; 475 return reqs[1].data[0];
261} 476}
262#endif /* CONFIG_CPU == AS3525v2 */ 477#endif /* CONFIG_CPU == AS3525v2 */
263 478
264void INT_AUDIO(void) 479/* callback that receives results of reading INT_AUDIO status register */
480static void ascodec_int_audio_cb(struct ascodec_request *req)
265{ 481{
266 int oldstatus = disable_irq_save(); 482 unsigned char * const data = req->data;
267 int data = ascodec_read(AS3514_IRQ_ENRD0);
268 483
269#if CONFIG_CHARGING 484 if (UNLIKELY(req->len_done != 3)) { /* some error happened? */
270 if (data & CHG_ENDOFCH) { /* chg finished */ 485 panicf("INT_AUDIO callback got %d regs", req->len_done);
271 endofch = true;
272 } 486 }
273 487
274 chg_status = data & CHG_STATUS; 488 if (data[0] & CHG_ENDOFCH) { /* chg finished */
275#endif 489 COUNT_INT(chg_finished);
490 ascodec_enrd0_shadow |= CHG_ENDOFCH;
491 }
492
493 if (data[0] & CHG_CHANGED) { /* chg status changed */
494 if (data[0] & CHG_STATUS) {
495 COUNT_INT(chg_insert);
496 ascodec_enrd0_shadow |= CHG_STATUS;
497 } else {
498 COUNT_INT(chg_remove);
499 ascodec_enrd0_shadow &= ~CHG_STATUS;
500 }
501 }
276 502
277 if (data & USB_CHANGED) { /* usb status changed */ 503 if (data[0] & USB_CHANGED) { /* usb status changed */
278 if (data & USB_STATUS) { 504 if (data[0] & USB_STATUS) {
505 COUNT_INT(usb_insert);
279 usb_insert_int(); 506 usb_insert_int();
280 } else { 507 } else {
508 COUNT_INT(usb_remove);
281 usb_remove_int(); 509 usb_remove_int();
282 } 510 }
283 } 511 }
284 512
285 restore_irq(oldstatus); 513 if (data[2] & IRQ_RTC) { /* rtc irq */
514 /*
515 * Can be configured for once per second or once per minute,
516 * default is once per second
517 */
518 COUNT_INT(rtc);
519 }
520
521 if (data[2] & IRQ_ADC) { /* adc finished */
522 COUNT_INT(adc);
523 semaphore_release(&adc_done_sem);
524 }
525
526 VIC_INT_ENABLE = INTERRUPT_AUDIO;
527}
528
529/* ISR for all various ascodec events */
530void INT_AUDIO(void)
531{
532 VIC_INT_EN_CLEAR = INTERRUPT_AUDIO;
533 COUNT_INT(audio);
534
535 ascodec_async_read(&as_audio_req, AS3514_IRQ_ENRD0, 3);
536}
537
538/* wait for ADC to finish conversion */
539void ascodec_wait_adc_finished(void)
540{
541 semaphore_wait(&adc_done_sem, TIMEOUT_BLOCK);
286} 542}
287 543
288#if CONFIG_CHARGING 544#if CONFIG_CHARGING
545/* read sticky end-of-charge bit and clear it */
289bool ascodec_endofch(void) 546bool ascodec_endofch(void)
290{ 547{
291 int oldstatus = disable_irq_save(); 548 int oldlevel = disable_irq_save();
292 bool ret = endofch; 549
293 endofch = false; 550 bool ret = ascodec_enrd0_shadow & CHG_ENDOFCH;
294 restore_irq(oldstatus); 551 ascodec_enrd0_shadow &= ~CHG_ENDOFCH; /* clear interrupt */
552
553 restore_irq(oldlevel);
295 554
296 return ret; 555 return ret;
297} 556}
298 557
558/* read the presence state of the charger */
299bool ascodec_chg_status(void) 559bool ascodec_chg_status(void)
300{ 560{
301 return chg_status; 561 return ascodec_enrd0_shadow & CHG_STATUS;
302} 562}
303 563
304void ascodec_monitor_endofch(void) 564void ascodec_monitor_endofch(void)
305{ 565{
306 /* already enabled */ 566 /* end of charge status interrupt already enabled */
307} 567}
308 568
569/* write charger control register */
309void ascodec_write_charger(int value) 570void ascodec_write_charger(int value)
310{ 571{
311#if CONFIG_CPU == AS3525 572#if CONFIG_CPU == AS3525
@@ -315,6 +576,7 @@ void ascodec_write_charger(int value)
315#endif 576#endif
316} 577}
317 578
579/* read charger control register */
318int ascodec_read_charger(void) 580int ascodec_read_charger(void)
319{ 581{
320#if CONFIG_CPU == AS3525 582#if CONFIG_CPU == AS3525
@@ -325,6 +587,15 @@ int ascodec_read_charger(void)
325} 587}
326#endif /* CONFIG_CHARGING */ 588#endif /* CONFIG_CHARGING */
327 589
590/*
591 * NOTE:
592 * After the conversion to interrupts, ascodec_(lock|unlock) are only used by
593 * adc-as3514.c to protect against other threads corrupting the result by using
594 * the ADC at the same time.
595 * Concurrent ascodec_(async_)?(read|write) calls are instead protected
596 * because ascodec_submit() is atomic and concurrent requests will wait
597 * in the queue until the current request is finished.
598 */
328void ascodec_lock(void) 599void ascodec_lock(void)
329{ 600{
330 mutex_lock(&as_mtx); 601 mutex_lock(&as_mtx);
@@ -334,3 +605,63 @@ void ascodec_unlock(void)
334{ 605{
335 mutex_unlock(&as_mtx); 606 mutex_unlock(&as_mtx);
336} 607}
608
609
610/** --Startup initialization-- **/
611
612void i2c_init(void)
613{
614 /* required function but called too late for our needs */
615}
616
617/* initialises the internal i2c bus and prepares for transfers to the codec */
618void ascodec_init(void)
619{
620 int prescaler;
621
622 ll_init(&req_list);
623 mutex_init(&as_mtx);
624 ascodec_async_init(&as_audio_req, ascodec_int_audio_cb, 0);
625 semaphore_init(&adc_done_sem, 1, 0);
626
627 /* enable clock */
628 bitset32(&CGU_PERI, CGU_I2C_AUDIO_MASTER_CLOCK_ENABLE);
629
630 /* prescaler for i2c clock */
631 prescaler = AS3525_I2C_PRESCALER;
632 I2C2_CPSR0 = prescaler & 0xFF; /* 8 lsb */
633 I2C2_CPSR1 = (prescaler >> 8) & 0x3; /* 2 msb */
634
635 /* set i2c slave address of codec part */
636 I2C2_SLAD0 = AS3514_I2C_ADDR << 1;
637
638 I2C2_CNTRL = I2C2_CNTRL_DEFAULT;
639
640 I2C2_IMR = 0x00; /* disable interrupts */
641 I2C2_INT_CLR = I2C2_RIS; /* clear interrupt status */
642 VIC_INT_ENABLE = INTERRUPT_I2C_AUDIO;
643 VIC_INT_ENABLE = INTERRUPT_AUDIO;
644
645 /* detect if USB was connected at startup since there is no transition */
646 ascodec_enrd0_shadow = ascodec_read(AS3514_IRQ_ENRD0);
647 if(ascodec_enrd0_shadow & USB_STATUS)
648 usb_insert_int();
649
650 /* Generate irq for usb+charge status change */
651 ascodec_write(AS3514_IRQ_ENRD0,
652#if CONFIG_CHARGING /* m200v4 can't charge */
653 IRQ_CHGSTAT | IRQ_ENDOFCH |
654#endif
655 IRQ_USBSTAT);
656
657#if CONFIG_CPU == AS3525v2
658 /* XIRQ = IRQ, active low reset signal, 6mA push-pull output */
659 ascodec_write_pmu(0x1a, 3, (1<<2)|3); /* 1A-3 = Out_Cntr3 register */
660 /* Generate irq on (rtc,) adc change */
661 ascodec_write(AS3514_IRQ_ENRD2, /*IRQ_RTC |*/ IRQ_ADC);
662#else
663 /* Generate irq for push-pull, active high, irq on rtc+adc change */
664 ascodec_write(AS3514_IRQ_ENRD2, IRQ_PUSHPULL | IRQ_HIGHACTIVE |
665 /*IRQ_RTC |*/ IRQ_ADC);
666#endif
667}
diff --git a/firmware/target/arm/as3525/debug-as3525.c b/firmware/target/arm/as3525/debug-as3525.c
index 1ee4de64cc..a1e69834dd 100644
--- a/firmware/target/arm/as3525/debug-as3525.c
+++ b/firmware/target/arm/as3525/debug-as3525.c
@@ -605,3 +605,33 @@ end:
605 lcd_setfont(FONT_UI); 605 lcd_setfont(FONT_UI);
606 return false; 606 return false;
607} 607}
608
609#ifdef HAVE_ADJUSTABLE_CPU_VOLTAGE
610/* Return CPU voltage setting in millivolts */
611int get_cpu_voltage_setting(void)
612{
613 int value;
614
615#if CONFIG_CPU == AS3525
616 value = ascodec_read(AS3514_CVDD_DCDC3) & 0x3;
617 value = 1200 - value * 50;
618#else /* as3525v2 */
619 value = ascodec_read_pmu(0x17, 1) & 0x7f;
620
621 /* Calculate in 0.1mV steps */
622 if (value == 0)
623 /* 0 volts */;
624 else if (value <= 0x40)
625 value = 6000 + value * 125;
626 else if (value <= 0x70)
627 value = 14000 + (value - 0x40) * 250;
628 else if (value <= 0x7f)
629 value = 26000 + (value - 0x70) * 500;
630
631 /* Return voltage setting in millivolts */
632 value = (value + 5) / 10;
633#endif /* CONFIG_CPU */
634
635 return value;
636}
637#endif /* HAVE_ADJUSTABLE_CPU_VOLTAGE */
diff --git a/firmware/target/arm/as3525/system-as3525.c b/firmware/target/arm/as3525/system-as3525.c
index 8aa2d02ab7..0ea0c8fba4 100644
--- a/firmware/target/arm/as3525/system-as3525.c
+++ b/firmware/target/arm/as3525/system-as3525.c
@@ -33,6 +33,8 @@
33#include "backlight-target.h" 33#include "backlight-target.h"
34#include "lcd.h" 34#include "lcd.h"
35 35
36struct mutex cpufreq_mtx;
37
36/* Charge Pump and Power management Settings */ 38/* Charge Pump and Power management Settings */
37#define AS314_CP_DCDC3_SETTING \ 39#define AS314_CP_DCDC3_SETTING \
38 ((0<<7) | /* CP_SW Auto-Switch Margin 0=200/300 1=150/255 */ \ 40 ((0<<7) | /* CP_SW Auto-Switch Margin 0=200/300 1=150/255 */ \
@@ -144,6 +146,7 @@ static const struct { int source; void (*isr) (void); } vec_int_srcs[] =
144 { INT_SRC_USB, INT_USB_FUNC, }, 146 { INT_SRC_USB, INT_USB_FUNC, },
145 { INT_SRC_TIMER1, INT_TIMER1 }, 147 { INT_SRC_TIMER1, INT_TIMER1 },
146 { INT_SRC_TIMER2, INT_TIMER2 }, 148 { INT_SRC_TIMER2, INT_TIMER2 },
149 { INT_SRC_I2C_AUDIO, INT_I2C_AUDIO },
147 { INT_SRC_AUDIO, INT_AUDIO }, 150 { INT_SRC_AUDIO, INT_AUDIO },
148 /* Lowest priority at the end of the list */ 151 /* Lowest priority at the end of the list */
149}; 152};
@@ -322,6 +325,12 @@ void system_init(void)
322 setup_vic(); 325 setup_vic();
323 326
324 dma_init(); 327 dma_init();
328}
329
330/* this is called after kernel and threading are initialized */
331void kernel_device_init(void)
332{
333 mutex_init(&cpufreq_mtx);
325 334
326 ascodec_init(); 335 ascodec_init();
327 336
@@ -329,7 +338,8 @@ void system_init(void)
329#ifdef HAVE_AS3543 338#ifdef HAVE_AS3543
330 /* PLL: disable audio PLL, we use MCLK already */ 339 /* PLL: disable audio PLL, we use MCLK already */
331 ascodec_write_pmu(0x1A, 7, 0x02); 340 ascodec_write_pmu(0x1A, 7, 0x02);
332 /* DCDC_Cntr: set switching speed of CVDD1/2 power supplies to 1 MHz */ 341 /* DCDC_Cntr: set switching speed of CVDD1/2 power supplies to 1 MHz,
342 immediate change */
333 ascodec_write_pmu(0x17, 7, 0x30); 343 ascodec_write_pmu(0x17, 7, 0x30);
334 /* Out_Cntr2: set drive strength of 24 MHz and 32 kHz clocks to 1 mA */ 344 /* Out_Cntr2: set drive strength of 24 MHz and 32 kHz clocks to 1 mA */
335 ascodec_write_pmu(0x1A, 2, 0xCC); 345 ascodec_write_pmu(0x1A, 2, 0xCC);
@@ -414,11 +424,28 @@ void udelay(unsigned usecs)
414 424
415#ifndef BOOTLOADER 425#ifndef BOOTLOADER
416#ifdef HAVE_ADJUSTABLE_CPU_FREQ 426#ifdef HAVE_ADJUSTABLE_CPU_FREQ
427bool set_cpu_frequency__lock(void)
428{
429 if (get_processor_mode() != CPU_MODE_THREAD_CONTEXT)
430 return false;
431
432 mutex_lock(&cpufreq_mtx);
433 return true;
434}
435
436void set_cpu_frequency__unlock(void)
437{
438 mutex_unlock(&cpufreq_mtx);
439}
417 440
418#if CONFIG_CPU == AS3525 441#if CONFIG_CPU == AS3525
419void set_cpu_frequency(long frequency) 442void set_cpu_frequency(long frequency)
420{ 443{
421 if(frequency == CPUFREQ_MAX) 444 if (frequency == cpu_frequency)
445 {
446 /* avoid redundant activity */
447 }
448 else if(frequency == CPUFREQ_MAX)
422 { 449 {
423#ifdef HAVE_ADJUSTABLE_CPU_VOLTAGE 450#ifdef HAVE_ADJUSTABLE_CPU_VOLTAGE
424 /* Increasing frequency so boost voltage before change */ 451 /* Increasing frequency so boost voltage before change */
@@ -464,7 +491,11 @@ void set_cpu_frequency(long frequency)
464#else /* as3525v2 */ 491#else /* as3525v2 */
465void set_cpu_frequency(long frequency) 492void set_cpu_frequency(long frequency)
466{ 493{
467 if(frequency == CPUFREQ_MAX) 494 if (frequency == cpu_frequency)
495 {
496 /* avoid redundant activity */
497 }
498 else if(frequency == CPUFREQ_MAX)
468 { 499 {
469#ifdef HAVE_ADJUSTABLE_CPU_VOLTAGE 500#ifdef HAVE_ADJUSTABLE_CPU_VOLTAGE
470 /* Set CVDD1 power supply */ 501 /* Set CVDD1 power supply */
diff --git a/firmware/target/arm/as3525/system-target.h b/firmware/target/arm/as3525/system-target.h
index db5bb892ef..4fbbb46d5d 100644
--- a/firmware/target/arm/as3525/system-target.h
+++ b/firmware/target/arm/as3525/system-target.h
@@ -21,12 +21,17 @@
21#ifndef SYSTEM_TARGET_H 21#ifndef SYSTEM_TARGET_H
22#define SYSTEM_TARGET_H 22#define SYSTEM_TARGET_H
23 23
24/* we need some system things initialized after the kernel init */
25#define KDEV_INIT
26
24#include "system-arm.h" 27#include "system-arm.h"
25#include "mmu-arm.h" 28#include "mmu-arm.h"
26#include "panic.h" 29#include "panic.h"
27 30
28#include "clock-target.h" /* CPUFREQ_* are defined here */ 31#include "clock-target.h" /* CPUFREQ_* are defined here */
29 32
33void kernel_device_init(void);
34
30#define STORAGE_WANTS_ALIGN 35#define STORAGE_WANTS_ALIGN
31 36
32/* We can use a interrupt-based mechanism on the fuzev2 */ 37/* We can use a interrupt-based mechanism on the fuzev2 */
@@ -68,4 +73,20 @@ static inline void mdelay(unsigned msecs)
68void usb_insert_int(void); 73void usb_insert_int(void);
69void usb_remove_int(void); 74void usb_remove_int(void);
70 75
76#ifdef HAVE_ADJUSTABLE_CPU_FREQ
77#define CPU_BOOST_LOCK_DEFINED
78
79static inline bool cpu_boost_lock(void)
80{
81 bool set_cpu_frequency__lock(void);
82 return set_cpu_frequency__lock();
83}
84
85static inline void cpu_boost_unlock(void)
86{
87 void set_cpu_frequency__unlock(void);
88 set_cpu_frequency__unlock();
89}
90#endif /* HAVE_ADJUSTABLE_CPU_FREQ */
91
71#endif /* SYSTEM_TARGET_H */ 92#endif /* SYSTEM_TARGET_H */
diff --git a/firmware/target/arm/pp/system-pp5002.c b/firmware/target/arm/pp/system-pp5002.c
index 3186d3739a..388f962fce 100644
--- a/firmware/target/arm/pp/system-pp5002.c
+++ b/firmware/target/arm/pp/system-pp5002.c
@@ -24,6 +24,11 @@
24#include "adc-target.h" 24#include "adc-target.h"
25#include "button-target.h" 25#include "button-target.h"
26 26
27#if defined(HAVE_ADJUSTABLE_CPU_FREQ) && (NUM_CORES > 1)
28#include "corelock.h"
29static struct corelock cpufreq_cl SHAREDBSS_ATTR;
30#endif
31
27extern void TIMER1(void); 32extern void TIMER1(void);
28extern void TIMER2(void); 33extern void TIMER2(void);
29 34
@@ -122,6 +127,18 @@ static void ipod_init_cache(void)
122} 127}
123 128
124#ifdef HAVE_ADJUSTABLE_CPU_FREQ 129#ifdef HAVE_ADJUSTABLE_CPU_FREQ
130#if NUM_CORES > 1
131void set_cpu_frequency__lock(void)
132{
133 corelock_lock(&cpufreq_cl);
134}
135
136void set_cpu_frequency__unlock(void)
137{
138 corelock_unlock(&cpufreq_cl);
139}
140#endif /* NUM_CORES > 1 */
141
125void set_cpu_frequency(long frequency) 142void set_cpu_frequency(long frequency)
126#else 143#else
127static void pp_set_cpu_frequency(long frequency) 144static void pp_set_cpu_frequency(long frequency)
@@ -193,7 +210,7 @@ void system_init(void)
193 210
194#ifdef HAVE_ADJUSTABLE_CPU_FREQ 211#ifdef HAVE_ADJUSTABLE_CPU_FREQ
195#if NUM_CORES > 1 212#if NUM_CORES > 1
196 cpu_boost_init(); 213 corelock_init(&cpufreq_cl);
197#endif 214#endif
198#else 215#else
199 pp_set_cpu_frequency(CPUFREQ_MAX); 216 pp_set_cpu_frequency(CPUFREQ_MAX);
diff --git a/firmware/target/arm/pp/system-pp502x.c b/firmware/target/arm/pp/system-pp502x.c
index 99b536e132..102cfd8fea 100644
--- a/firmware/target/arm/pp/system-pp502x.c
+++ b/firmware/target/arm/pp/system-pp502x.c
@@ -308,16 +308,24 @@ void scale_suspend_core(bool suspend)
308} 308}
309 309
310#ifdef HAVE_ADJUSTABLE_CPU_FREQ 310#ifdef HAVE_ADJUSTABLE_CPU_FREQ
311#if NUM_CORES > 1
312void set_cpu_frequency__lock(void)
313{
314 corelock_lock(&cpufreq_cl);
315}
316
317void set_cpu_frequency__unlock(void)
318{
319 corelock_unlock(&cpufreq_cl);
320}
321#endif /* NUM_CORES > 1 */
322
311void set_cpu_frequency(long frequency) ICODE_ATTR; 323void set_cpu_frequency(long frequency) ICODE_ATTR;
312void set_cpu_frequency(long frequency) 324void set_cpu_frequency(long frequency)
313#else 325#else
314static void pp_set_cpu_frequency(long frequency) 326static void pp_set_cpu_frequency(long frequency)
315#endif 327#endif
316{ 328{
317#if defined(HAVE_ADJUSTABLE_CPU_FREQ) && (NUM_CORES > 1)
318 corelock_lock(&cpufreq_cl);
319#endif
320
321 switch (frequency) 329 switch (frequency)
322 { 330 {
323 /* Note1: The PP5022 PLL must be run at >= 96MHz 331 /* Note1: The PP5022 PLL must be run at >= 96MHz
@@ -424,10 +432,6 @@ static void pp_set_cpu_frequency(long frequency)
424 DEV_INIT2 &= ~INIT_PLL; /* disable PLL power */ 432 DEV_INIT2 &= ~INIT_PLL; /* disable PLL power */
425 break; 433 break;
426 } 434 }
427
428#if defined(HAVE_ADJUSTABLE_CPU_FREQ) && (NUM_CORES > 1)
429 corelock_unlock(&cpufreq_cl);
430#endif
431} 435}
432#endif /* !BOOTLOADER || (SANSA_E200 || SANSA_C200 || PHILIPS_SA9200) */ 436#endif /* !BOOTLOADER || (SANSA_E200 || SANSA_C200 || PHILIPS_SA9200) */
433 437
@@ -544,7 +548,6 @@ void system_init(void)
544#ifdef HAVE_ADJUSTABLE_CPU_FREQ 548#ifdef HAVE_ADJUSTABLE_CPU_FREQ
545#if NUM_CORES > 1 549#if NUM_CORES > 1
546 corelock_init(&cpufreq_cl); 550 corelock_init(&cpufreq_cl);
547 cpu_boost_init();
548#endif 551#endif
549#else 552#else
550 pp_set_cpu_frequency(CPUFREQ_MAX); 553 pp_set_cpu_frequency(CPUFREQ_MAX);
diff --git a/firmware/target/arm/pp/system-target.h b/firmware/target/arm/pp/system-target.h
index d372b65502..1e947195bd 100644
--- a/firmware/target/arm/pp/system-target.h
+++ b/firmware/target/arm/pp/system-target.h
@@ -199,4 +199,21 @@ void system_prepare_fw_start(void);
199 199
200#endif /* BOOTLOADER */ 200#endif /* BOOTLOADER */
201 201
202#if defined(HAVE_ADJUSTABLE_CPU_FREQ) && (NUM_CORES > 1)
203#define CPU_BOOST_LOCK_DEFINED
204
205static inline bool cpu_boost_lock(void)
206{
207 void set_cpu_frequency__lock(void);
208 set_cpu_frequency__lock();
209 return true;
210}
211
212static inline void cpu_boost_unlock(void)
213{
214 void set_cpu_frequency__unlock(void);
215 set_cpu_frequency__unlock();
216}
217#endif /* HAVE_ADJUSTABLE_CPU_FREQ && NUM_CORES > 1 */
218
202#endif /* SYSTEM_TARGET_H */ 219#endif /* SYSTEM_TARGET_H */