summaryrefslogtreecommitdiff
path: root/firmware/target/arm/as3525/ascodec-as3525.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/target/arm/as3525/ascodec-as3525.c')
-rw-r--r--firmware/target/arm/as3525/ascodec-as3525.c571
1 files changed, 451 insertions, 120 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}