AMS SoC's: Some register bit changes need interrupt protection: timer API and CGU_PERI.
[kugel-rb.git] / firmware / target / arm / as3525 / ascodec-as3525.c
blob822d48e241a5661d7766c04e3efc2b7fd05325c2
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2008 by Bertrik Sikken
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
23 Provides access to the codec/charger/rtc/adc part of the as3525.
24 This part is on address 0x46 of the internal i2c bus in the as3525.
25 Registers in the codec part seem to be nearly identical to the registers
26 in the AS3514 (used in the "v1" versions of the sansa c200 and e200).
28 I2C register description:
29 * I2C2_CNTRL needs to be set to 0x51 for transfers to work at all.
30 bit 0: ? possibly related to using ACKs during transfers
31 bit 1: direction of transfer (0 = write, 1 = read)
32 bit 2: use 2-byte slave address
33 * I2C2_IMR, I2C2_RIS, I2C2_MIS, I2C2_INT_CLR interrupt bits:
34 bit 2: byte read interrupt
35 bit 3: byte write interrupt
36 bit 4: ? possibly some kind of error status
37 bit 7: ACK error
38 * I2C2_SR (status register) indicates in bit 0 if a transfer is busy.
39 * I2C2_SLAD0 contains the i2c slave address to read from / write to.
40 * I2C2_CPSR0/1 is the divider from the peripheral clock to the i2c clock.
41 * I2C2_DACNT sets the number of bytes to transfer and actually starts it.
43 When a transfer is attempted to a non-existing i2c slave address,
44 interrupt bit 7 is raised and DACNT is not decremented after the transfer.
47 #include "ascodec-target.h"
48 #include "clock-target.h"
49 #include "kernel.h"
50 #include "system.h"
51 #include "as3525.h"
52 #include "i2c.h"
53 #include "usb-target.h"
55 #define I2C2_DATA *((volatile unsigned int *)(I2C_AUDIO_BASE + 0x00))
56 #define I2C2_SLAD0 *((volatile unsigned int *)(I2C_AUDIO_BASE + 0x04))
57 #define I2C2_CNTRL *((volatile unsigned int *)(I2C_AUDIO_BASE + 0x0C))
58 #define I2C2_DACNT *((volatile unsigned int *)(I2C_AUDIO_BASE + 0x10))
59 #define I2C2_CPSR0 *((volatile unsigned int *)(I2C_AUDIO_BASE + 0x1C))
60 #define I2C2_CPSR1 *((volatile unsigned int *)(I2C_AUDIO_BASE + 0x20))
61 #define I2C2_IMR *((volatile unsigned int *)(I2C_AUDIO_BASE + 0x24))
62 #define I2C2_RIS *((volatile unsigned int *)(I2C_AUDIO_BASE + 0x28))
63 #define I2C2_MIS *((volatile unsigned int *)(I2C_AUDIO_BASE + 0x2C))
64 #define I2C2_SR *((volatile unsigned int *)(I2C_AUDIO_BASE + 0x30))
65 #define I2C2_INT_CLR *((volatile unsigned int *)(I2C_AUDIO_BASE + 0x40))
66 #define I2C2_SADDR *((volatile unsigned int *)(I2C_AUDIO_BASE + 0x44))
68 #define I2C2_CNTRL_MASTER 0x01
69 #define I2C2_CNTRL_READ 0x02
70 #define I2C2_CNTRL_WRITE 0x00
71 #define I2C2_CNTRL_RESET 0x10
72 #define I2C2_CNTRL_REPSTARTEN 0x40
74 #define I2C2_CNTRL_DEFAULT (I2C2_CNTRL_MASTER|I2C2_CNTRL_REPSTARTEN|I2C2_CNTRL_RESET)
76 #define I2C2_IRQ_TXEMPTY 0x04
77 #define I2C2_IRQ_RXFULL 0x08
78 #define I2C2_IRQ_RXOVER 0x10
79 #define I2C2_IRQ_ACKTIMEO 0x80
81 #define REQ_UNFINISHED 0
82 #define REQ_FINISHED 1
83 #define REQ_RETRY 2
85 #ifdef DEBUG
86 #define IFDEBUG(x) x
87 #else
88 #define IFDEBUG(x)
89 #endif
91 #define ASCODEC_REQ_READ 0
92 #define ASCODEC_REQ_WRITE 1
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!
100 #define ASCODEC_REQ_MAXLEN 4
102 typedef void (ascodec_cb_fn)(unsigned const char *data, unsigned cnt);
104 struct ascodec_request {
105 unsigned char type;
106 unsigned char index;
107 unsigned char status;
108 unsigned char cnt;
109 unsigned char data[ASCODEC_REQ_MAXLEN];
110 struct wakeup wkup;
111 ascodec_cb_fn *callback;
112 struct ascodec_request *next;
116 static struct mutex as_mtx;
118 static int ascodec_enrd0_shadow = 0;
120 static unsigned char *req_data_ptr = NULL;
121 static struct ascodec_request *req_head = NULL;
122 static struct ascodec_request *req_tail = NULL;
124 static struct wakeup adc_wkup;
125 static struct ascodec_request as_audio_req;
127 #ifdef DEBUG
128 static int int_audio_ctr = 0;
129 static int int_chg_finished = 0;
130 static int int_chg_insert = 0;
131 static int int_chg_remove = 0;
132 static int int_usb_insert = 0;
133 static int int_usb_remove = 0;
134 static int int_rtc = 0;
135 static int int_adc = 0;
136 #endif /* DEBUG */
138 /* returns != 0 when busy */
139 static inline int i2c_busy(void)
141 return (I2C2_SR & 1);
144 static void ascodec_finish_req(struct ascodec_request *req)
147 * Wait if still busy, unfortunately this happens since
148 * the controller is running at a low divisor, so it's
149 * still busy when we serviced the interrupt.
150 * I tried upping the i2c speed to 4MHz which
151 * made the number of busywait cycles much smaller
152 * (none for reads and only a few for writes),
153 * but who knows if it's reliable at that frequency. ;)
154 * For one thing, 8MHz doesn't work, so 4MHz is likely
155 * borderline.
156 * In general writes need much more wait cycles than reads
157 * for some reason, possibly because we read the data register
158 * for reads, which will likely block the processor while
159 * the i2c controller responds to the register read.
161 while (i2c_busy());
163 /* disable clock - already in IRQ context */
164 CGU_PERI &= ~CGU_I2C_AUDIO_MASTER_CLOCK_ENABLE;
166 req->status = 1;
168 if (req->callback) {
169 req->callback(req->data, req_data_ptr - req->data);
171 wakeup_signal(&req->wkup);
173 req_head = req->next;
174 req->next = NULL;
175 if (req_head == NULL)
176 req_tail = NULL;
180 static int ascodec_continue_req(struct ascodec_request *req, int irq_status)
182 if ((irq_status & (I2C2_IRQ_RXOVER|I2C2_IRQ_ACKTIMEO)) > 0) {
183 /* some error occured, restart the request */
184 return REQ_RETRY;
186 if (req->type == ASCODEC_REQ_READ &&
187 (irq_status & I2C2_IRQ_RXFULL) > 0) {
188 *(req_data_ptr++) = I2C2_DATA;
189 } else {
190 if (req->cnt > 1 &&
191 (irq_status & I2C2_IRQ_TXEMPTY) > 0) {
192 I2C2_DATA = *(req_data_ptr++);
196 req->index++;
197 if (--req->cnt > 0)
198 return REQ_UNFINISHED;
200 return REQ_FINISHED;
203 static void ascodec_start_req(struct ascodec_request *req)
205 int unmask = 0;
207 /* enable clock */
208 bitset32(&CGU_PERI, CGU_I2C_AUDIO_MASTER_CLOCK_ENABLE);
210 /* start transfer */
211 I2C2_SADDR = req->index;
212 if (req->type == ASCODEC_REQ_READ) {
213 req_data_ptr = req->data;
214 /* start transfer */
215 I2C2_CNTRL = I2C2_CNTRL_DEFAULT | I2C2_CNTRL_READ;
216 unmask = I2C2_IRQ_RXFULL|I2C2_IRQ_RXOVER;
217 } else {
218 req_data_ptr = &req->data[1];
219 I2C2_CNTRL = I2C2_CNTRL_DEFAULT | I2C2_CNTRL_WRITE;
220 I2C2_DATA = req->data[0];
221 unmask = I2C2_IRQ_TXEMPTY|I2C2_IRQ_ACKTIMEO;
224 I2C2_DACNT = req->cnt;
225 I2C2_IMR |= unmask; /* enable interrupts */
228 void INT_I2C_AUDIO(void)
230 int irq_status = I2C2_MIS;
231 int status = REQ_FINISHED;
233 if (req_head != NULL)
234 status = ascodec_continue_req(req_head, irq_status);
236 I2C2_INT_CLR |= irq_status; /* clear interrupt status */
238 if (status != REQ_UNFINISHED) {
239 /* mask rx/tx interrupts */
240 I2C2_IMR &= ~(I2C2_IRQ_TXEMPTY|I2C2_IRQ_RXFULL|
241 I2C2_IRQ_RXOVER|I2C2_IRQ_ACKTIMEO);
243 if (status == REQ_FINISHED)
244 ascodec_finish_req(req_head);
247 * If status == REQ_RETRY, this will restart
248 * the request because we didn't remove it from
249 * the request list
251 if (req_head)
252 ascodec_start_req(req_head);
256 void i2c_init(void)
260 /* initialises the internal i2c bus and prepares for transfers to the codec */
261 void ascodec_init(void)
263 int prescaler;
265 mutex_init(&as_mtx);
266 wakeup_init(&adc_wkup);
268 /* enable clock */
269 bitset32(&CGU_PERI, CGU_I2C_AUDIO_MASTER_CLOCK_ENABLE);
271 /* prescaler for i2c clock */
272 prescaler = AS3525_I2C_PRESCALER;
273 I2C2_CPSR0 = prescaler & 0xFF; /* 8 lsb */
274 I2C2_CPSR1 = (prescaler >> 8) & 0x3; /* 2 msb */
276 /* set i2c slave address of codec part */
277 I2C2_SLAD0 = AS3514_I2C_ADDR << 1;
279 I2C2_CNTRL = I2C2_CNTRL_DEFAULT;
281 I2C2_IMR = 0x00; /* disable interrupts */
282 I2C2_INT_CLR |= I2C2_RIS; /* clear interrupt status */
283 VIC_INT_ENABLE = INTERRUPT_I2C_AUDIO;
284 VIC_INT_ENABLE = INTERRUPT_AUDIO;
286 /* detect if USB was connected at startup since there is no transition */
287 ascodec_enrd0_shadow = ascodec_read(AS3514_IRQ_ENRD0);
288 if(ascodec_enrd0_shadow & USB_STATUS)
289 usb_insert_int();
290 else
291 usb_remove_int();
293 /* Generate irq for usb+charge status change */
294 ascodec_write(AS3514_IRQ_ENRD0,
295 #ifdef CONFIG_CHARGING /* m200v4 can't charge */
296 IRQ_CHGSTAT | IRQ_ENDOFCH |
297 #endif
298 IRQ_USBSTAT);
300 #if CONFIG_CPU == AS3525v2
301 /* XIRQ = IRQ, active low reset signal, 6mA push-pull output */
302 ascodec_write_pmu(0x1a, 3, (1<<2)|3); /* 1A-3 = Out_Cntr3 register */
303 /* Generate irq on (rtc,) adc change */
304 ascodec_write(AS3514_IRQ_ENRD2, /*IRQ_RTC |*/ IRQ_ADC);
305 #else
306 /* Generate irq for push-pull, active high, irq on rtc+adc change */
307 ascodec_write(AS3514_IRQ_ENRD2, IRQ_PUSHPULL | IRQ_HIGHACTIVE |
308 /*IRQ_RTC |*/ IRQ_ADC);
309 #endif
312 static void ascodec_req_init(struct ascodec_request *req, int type,
313 unsigned int index, unsigned int cnt)
315 wakeup_init(&req->wkup);
316 req->next = NULL;
317 req->callback = NULL;
318 req->type = type;
319 req->index = index;
320 req->cnt = cnt;
323 static void ascodec_submit(struct ascodec_request *req)
325 int oldlevel = disable_irq_save();
327 req->status = 0;
329 if (req_head == NULL) {
330 req_tail = req_head = req;
331 ascodec_start_req(req);
332 } else {
333 req_tail->next = req;
334 req_tail = req;
337 restore_irq(oldlevel);
340 static int irq_disabled(void)
342 unsigned long cpsr;
344 asm volatile ("mrs %0, cpsr" : "=r"(cpsr));
346 return (cpsr & IRQ_STATUS) == IRQ_DISABLED;
349 static void ascodec_wait(struct ascodec_request *req)
351 if (!irq_disabled()) {
352 wakeup_wait(&req->wkup, TIMEOUT_BLOCK);
353 return;
356 while (req->status == 0) {
357 if (I2C2_MIS) INT_I2C_AUDIO();
362 * The request struct passed in must be allocated statically.
363 * If you call ascodec_async_write from different places, each
364 * call needs it's own request struct.
366 static void ascodec_async_write(unsigned int index, unsigned int value,
367 struct ascodec_request *req)
369 if (index == AS3514_CVDD_DCDC3) /* prevent setting of the LREG_CP_not bit */
370 value &= ~(1 << 5);
372 ascodec_req_init(req, ASCODEC_REQ_WRITE, index, 1);
373 req->data[0] = value;
374 ascodec_submit(req);
377 /* returns 0 on success, <0 otherwise */
378 int ascodec_write(unsigned int index, unsigned int value)
380 struct ascodec_request req;
382 ascodec_async_write(index, value, &req);
383 ascodec_wait(&req);
385 return 0;
389 * The request struct passed in must be allocated statically.
390 * If you call ascodec_async_read from different places, each
391 * call needs it's own request struct.
392 * If len is bigger than ASCODEC_REQ_MAXLEN it will be
393 * set to ASCODEC_REQ_MAXLEN.
395 static void ascodec_async_read(unsigned int index, unsigned int len,
396 struct ascodec_request *req, ascodec_cb_fn *cb)
398 if (len > ASCODEC_REQ_MAXLEN)
399 len = ASCODEC_REQ_MAXLEN; /* can't fit more in one request */
401 ascodec_req_init(req, ASCODEC_REQ_READ, index, len);
402 req->callback = cb;
403 ascodec_submit(req);
406 /* returns value read on success, <0 otherwise */
407 int ascodec_read(unsigned int index)
409 struct ascodec_request req;
411 ascodec_async_read(index, 1, &req, NULL);
412 ascodec_wait(&req);
414 return req.data[0];
417 int ascodec_readbytes(unsigned int index, unsigned int len, unsigned char *data)
419 int i, j;
420 struct ascodec_request req;
422 /* index and cnt will be filled in later, just use 0 */
423 ascodec_req_init(&req, ASCODEC_REQ_READ, 0, 0);
425 i = 0;
426 while (len > 0) {
427 int cnt = len > ASCODEC_REQ_MAXLEN ? ASCODEC_REQ_MAXLEN : len;
429 req.index = index;
430 req.cnt = cnt;
432 ascodec_submit(&req);
433 ascodec_wait(&req);
435 for (j=0; j<cnt; j++) data[i++] = req.data[j];
437 len -= cnt;
438 index += cnt;
441 return i;
444 static void ascodec_read_cb(unsigned const char *data, unsigned int len)
446 if (UNLIKELY(len != 3)) /* some error happened? */
447 panicf("INT_AUDIO callback got %d regs", len);
449 if (data[0] & CHG_ENDOFCH) { /* chg finished */
450 ascodec_enrd0_shadow |= CHG_ENDOFCH;
451 IFDEBUG(int_chg_finished++);
453 if (data[0] & CHG_CHANGED) { /* chg status changed */
454 if (data[0] & CHG_STATUS) {
455 ascodec_enrd0_shadow |= CHG_STATUS;
456 IFDEBUG(int_chg_insert++);
457 } else {
458 ascodec_enrd0_shadow &= ~CHG_STATUS;
459 IFDEBUG(int_chg_remove++);
462 if (data[0] & USB_CHANGED) { /* usb status changed */
463 if (data[0] & USB_STATUS) {
464 IFDEBUG(int_usb_insert++);
465 usb_insert_int();
466 } else {
467 IFDEBUG(int_usb_remove++);
468 usb_remove_int();
471 if (data[2] & IRQ_RTC) { /* rtc irq */
473 * Can be configured for once per second or once per minute,
474 * default is once per second
476 IFDEBUG(int_rtc++);
478 if (data[2] & IRQ_ADC) { /* adc finished */
479 IFDEBUG(int_adc++);
480 wakeup_signal(&adc_wkup);
482 VIC_INT_ENABLE = INTERRUPT_AUDIO;
485 void INT_AUDIO(void)
487 VIC_INT_EN_CLEAR = INTERRUPT_AUDIO;
488 IFDEBUG(int_audio_ctr++);
490 ascodec_async_read(AS3514_IRQ_ENRD0, 3, &as_audio_req, ascodec_read_cb);
493 void ascodec_wait_adc_finished(void)
495 wakeup_wait(&adc_wkup, TIMEOUT_BLOCK);
498 #ifdef CONFIG_CHARGING
499 bool ascodec_endofch(void)
501 bool ret = ascodec_enrd0_shadow & CHG_ENDOFCH;
502 ascodec_enrd0_shadow &= ~CHG_ENDOFCH; // clear interrupt
503 return ret;
506 bool ascodec_chg_status(void)
508 return ascodec_enrd0_shadow & CHG_STATUS;
510 #endif /* CONFIG_CHARGING */
513 * NOTE:
514 * After the conversion to interrupts, ascodec_(lock|unlock) are only used by
515 * adc-as3514.c to protect against other threads corrupting the result by using
516 * the ADC at the same time.
517 * Concurrent ascodec_(async_)?(read|write) calls are instead protected
518 * because ascodec_submit() is atomic and concurrent requests will wait
519 * in the queue until the current request is finished.
521 void ascodec_lock(void)
523 mutex_lock(&as_mtx);
526 void ascodec_unlock(void)
528 mutex_unlock(&as_mtx);