Added battery profile change to correct file, removed unused powermgmt-as3525.c
[kugel-rb.git] / firmware / target / arm / as3525 / ascodec-as3525.c
blob87a1447c6349984c03f556c272f71bc07d1c0a19
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 static struct mutex as_mtx;
93 static int ascodec_enrd0_shadow = 0;
95 static unsigned char *req_data_ptr = NULL;
96 static struct ascodec_request *req_head = NULL;
97 static struct ascodec_request *req_tail = NULL;
99 #if CONFIG_CPU == AS3525
100 static struct wakeup adc_wkup;
101 static struct ascodec_request as_audio_req;
103 #ifdef DEBUG
104 static int int_audio_ctr = 0;
105 static int int_chg_finished = 0;
106 static int int_chg_insert = 0;
107 static int int_chg_remove = 0;
108 static int int_usb_insert = 0;
109 static int int_usb_remove = 0;
110 static int int_rtc = 0;
111 static int int_adc = 0;
112 #endif /* DEBUG */
114 static void ascodec_read_cb(unsigned const char *data, unsigned int len);
115 #endif /* CONFIG_CPU == AS3525 */
117 static void ascodec_start_req(struct ascodec_request *req);
118 static int ascodec_continue_req(struct ascodec_request *req, int irq_status);
119 static void ascodec_finish_req(struct ascodec_request *req);
121 #if CONFIG_CPU == AS3525
122 void INT_AUDIO(void)
124 VIC_INT_EN_CLEAR = INTERRUPT_AUDIO;
125 IFDEBUG(int_audio_ctr++);
127 ascodec_async_read(AS3514_IRQ_ENRD0, 3, &as_audio_req, ascodec_read_cb);
129 #endif /* CONFIG_CPU == AS3525 */
131 void INT_I2C_AUDIO(void)
133 int irq_status = I2C2_MIS;
134 int status = REQ_FINISHED;
136 if (req_head != NULL)
137 status = ascodec_continue_req(req_head, irq_status);
139 I2C2_INT_CLR |= irq_status; /* clear interrupt status */
141 if (status != REQ_UNFINISHED) {
142 /* mask rx/tx interrupts */
143 I2C2_IMR &= ~(I2C2_IRQ_TXEMPTY|I2C2_IRQ_RXFULL|
144 I2C2_IRQ_RXOVER|I2C2_IRQ_ACKTIMEO);
146 if (status == REQ_FINISHED)
147 ascodec_finish_req(req_head);
150 * If status == REQ_RETRY, this will restart
151 * the request because we didn't remove it from
152 * the request list
154 if (req_head)
155 ascodec_start_req(req_head);
159 void i2c_init(void)
163 /* initialises the internal i2c bus and prepares for transfers to the codec */
164 void ascodec_init(void)
166 int prescaler;
168 mutex_init(&as_mtx);
169 #if CONFIG_CPU == AS3525
170 wakeup_init(&adc_wkup);
171 #endif
173 /* enable clock */
174 CGU_PERI |= CGU_I2C_AUDIO_MASTER_CLOCK_ENABLE;
176 /* prescaler for i2c clock */
177 prescaler = AS3525_I2C_PRESCALER;
178 I2C2_CPSR0 = prescaler & 0xFF; /* 8 lsb */
179 I2C2_CPSR1 = (prescaler >> 8) & 0x3; /* 2 msb */
181 /* set i2c slave address of codec part */
182 I2C2_SLAD0 = AS3514_I2C_ADDR << 1;
184 I2C2_CNTRL = I2C2_CNTRL_DEFAULT;
186 I2C2_IMR = 0x00; /* disable interrupts */
187 I2C2_INT_CLR |= I2C2_RIS; /* clear interrupt status */
188 VIC_INT_ENABLE = INTERRUPT_I2C_AUDIO | INTERRUPT_AUDIO;
190 /* Generate irq for usb+charge status change */
191 ascodec_write(AS3514_IRQ_ENRD0, /*IRQ_CHGSTAT |*/ IRQ_USBSTAT);
192 /* Generate irq for push-pull, active high, irq on rtc+adc change */
193 ascodec_write(AS3514_IRQ_ENRD2, IRQ_PUSHPULL | IRQ_HIGHACTIVE |
194 /*IRQ_RTC |*/ IRQ_ADC);
197 /* returns != 0 when busy */
198 static int i2c_busy(void)
200 return (I2C2_SR & 1);
203 void ascodec_req_init(struct ascodec_request *req, int type,
204 unsigned int index, unsigned int cnt)
206 wakeup_init(&req->wkup);
207 req->next = NULL;
208 req->callback = NULL;
209 req->type = type;
210 req->index = index;
211 req->cnt = cnt;
214 void ascodec_submit(struct ascodec_request *req)
216 int oldlevel = disable_irq_save();
218 req->status = 0;
220 if (req_head == NULL) {
221 req_tail = req_head = req;
222 ascodec_start_req(req);
223 } else {
224 req_tail->next = req;
225 req_tail = req;
228 restore_irq(oldlevel);
231 static void ascodec_start_req(struct ascodec_request *req)
233 int unmask = 0;
235 /* enable clock */
236 CGU_PERI |= CGU_I2C_AUDIO_MASTER_CLOCK_ENABLE;
238 /* start transfer */
239 I2C2_SADDR = req->index;
240 if (req->type == ASCODEC_REQ_READ) {
241 req_data_ptr = req->data;
242 /* start transfer */
243 I2C2_CNTRL = I2C2_CNTRL_DEFAULT | I2C2_CNTRL_READ;
244 unmask = I2C2_IRQ_RXFULL|I2C2_IRQ_RXOVER;
245 } else {
246 req_data_ptr = &req->data[1];
247 I2C2_CNTRL = I2C2_CNTRL_DEFAULT | I2C2_CNTRL_WRITE;
248 I2C2_DATA = req->data[0];
249 unmask = I2C2_IRQ_TXEMPTY|I2C2_IRQ_ACKTIMEO;
252 I2C2_DACNT = req->cnt;
253 I2C2_IMR |= unmask; /* enable interrupts */
256 static int ascodec_continue_req(struct ascodec_request *req, int irq_status)
258 if ((irq_status & (I2C2_IRQ_RXOVER|I2C2_IRQ_ACKTIMEO)) > 0) {
259 /* some error occured, restart the request */
260 return REQ_RETRY;
262 if (req->type == ASCODEC_REQ_READ &&
263 (irq_status & I2C2_IRQ_RXFULL) > 0) {
264 *(req_data_ptr++) = I2C2_DATA;
265 } else {
266 if (req->cnt > 1 &&
267 (irq_status & I2C2_IRQ_TXEMPTY) > 0) {
268 I2C2_DATA = *(req_data_ptr++);
272 req->index++;
273 if (--req->cnt > 0)
274 return REQ_UNFINISHED;
276 return REQ_FINISHED;
279 static void ascodec_finish_req(struct ascodec_request *req)
282 * Wait if still busy, unfortunately this happens since
283 * the controller is running at a low divisor, so it's
284 * still busy when we serviced the interrupt.
285 * I tried upping the i2c speed to 4MHz which
286 * made the number of busywait cycles much smaller
287 * (none for reads and only a few for writes),
288 * but who knows if it's reliable at that frequency. ;)
289 * For one thing, 8MHz doesn't work, so 4MHz is likely
290 * borderline.
291 * In general writes need much more wait cycles than reads
292 * for some reason, possibly because we read the data register
293 * for reads, which will likely block the processor while
294 * the i2c controller responds to the register read.
296 while (i2c_busy());
298 /* disable clock */
299 CGU_PERI &= ~CGU_I2C_AUDIO_MASTER_CLOCK_ENABLE;
301 req->status = 1;
303 if (req->callback) {
304 req->callback(req->data, req_data_ptr - req->data);
306 wakeup_signal(&req->wkup);
308 req_head = req->next;
309 req->next = NULL;
310 if (req_head == NULL)
311 req_tail = NULL;
315 static int irq_disabled(void)
317 unsigned long cpsr;
319 asm volatile ("mrs %0, cpsr" : "=r"(cpsr));
321 return (cpsr & IRQ_STATUS) == IRQ_DISABLED;
324 static void ascodec_wait(struct ascodec_request *req)
326 if (!irq_disabled()) {
327 wakeup_wait(&req->wkup, TIMEOUT_BLOCK);
328 return;
331 while (req->status == 0) {
332 if (I2C2_MIS) INT_I2C_AUDIO();
337 * The request struct passed in must be allocated statically.
338 * If you call ascodec_async_write from different places, each
339 * call needs it's own request struct.
340 * This comment is duplicated in .c and .h for your convenience.
342 void ascodec_async_write(unsigned int index, unsigned int value,
343 struct ascodec_request *req)
345 switch(index) {
346 case AS3514_CVDD_DCDC3:
347 /* prevent setting of the LREG_CP_not bit */
348 value &= ~(1 << 5);
349 break;
350 case AS3514_IRQ_ENRD0:
351 /* save value in register shadow
352 * for ascodec_(en|dis)able_endofch_irq() */
353 ascodec_enrd0_shadow = value;
354 break;
355 default:
356 break;
359 ascodec_req_init(req, ASCODEC_REQ_WRITE, index, 1);
360 req->data[0] = value;
361 ascodec_submit(req);
364 /* returns 0 on success, <0 otherwise */
365 int ascodec_write(unsigned int index, unsigned int value)
367 struct ascodec_request req;
369 ascodec_async_write(index, value, &req);
370 ascodec_wait(&req);
372 return 0;
376 * The request struct passed in must be allocated statically.
377 * If you call ascodec_async_read from different places, each
378 * call needs it's own request struct.
379 * If len is bigger than ASCODEC_REQ_MAXLEN it will be
380 * set to ASCODEC_REQ_MAXLEN.
381 * This comment is duplicated in .c and .h for your convenience.
383 void ascodec_async_read(unsigned int index, unsigned int len,
384 struct ascodec_request *req, ascodec_cb_fn *cb)
386 if (len > ASCODEC_REQ_MAXLEN)
387 len = ASCODEC_REQ_MAXLEN; /* can't fit more in one request */
389 ascodec_req_init(req, ASCODEC_REQ_READ, index, len);
390 req->callback = cb;
391 ascodec_submit(req);
394 /* returns value read on success, <0 otherwise */
395 int ascodec_read(unsigned int index)
397 struct ascodec_request req;
399 ascodec_async_read(index, 1, &req, NULL);
400 ascodec_wait(&req);
402 return req.data[0];
405 int ascodec_readbytes(unsigned int index, unsigned int len, unsigned char *data)
407 int i, j;
408 struct ascodec_request req;
410 /* index and cnt will be filled in later, just use 0 */
411 ascodec_req_init(&req, ASCODEC_REQ_READ, 0, 0);
413 i = 0;
414 while (len > 0) {
415 int cnt = len > ASCODEC_REQ_MAXLEN ? ASCODEC_REQ_MAXLEN : len;
417 req.index = index;
418 req.cnt = cnt;
420 ascodec_submit(&req);
421 ascodec_wait(&req);
423 for (j=0; j<cnt; j++) data[i++] = req.data[j];
425 len -= cnt;
426 index += cnt;
429 return i;
432 #if CONFIG_CPU == AS3525
433 static void ascodec_read_cb(unsigned const char *data, unsigned int len)
435 if (len != 3) /* some error happened? */
436 return;
438 if (data[0] & CHG_ENDOFCH) { /* chg finished */
439 IFDEBUG(int_chg_finished++);
441 if (data[0] & CHG_CHANGED) { /* chg status changed */
442 if (data[0] & CHG_STATUS) {
443 IFDEBUG(int_chg_insert++);
444 } else {
445 IFDEBUG(int_chg_remove++);
448 if (data[0] & USB_CHANGED) { /* usb status changed */
449 if (data[0] & USB_STATUS) {
450 IFDEBUG(int_usb_insert++);
451 usb_insert_int();
452 } else {
453 IFDEBUG(int_usb_remove++);
454 usb_remove_int();
457 if (data[2] & IRQ_RTC) { /* rtc irq */
459 * Can be configured for once per second or once per minute,
460 * default is once per second
462 IFDEBUG(int_rtc++);
464 if (data[2] & IRQ_ADC) { /* adc finished */
465 IFDEBUG(int_adc++);
466 wakeup_signal(&adc_wkup);
468 VIC_INT_ENABLE = INTERRUPT_AUDIO;
471 void ascodec_wait_adc_finished(void)
473 wakeup_wait(&adc_wkup, TIMEOUT_BLOCK);
475 #endif /* CONFIG_CPU == AS3525 */
478 void ascodec_enable_endofch_irq(void)
480 ascodec_write(AS3514_IRQ_ENRD0, ascodec_enrd0_shadow | CHG_ENDOFCH);
483 void ascodec_disable_endofch_irq(void)
485 ascodec_write(AS3514_IRQ_ENRD0, ascodec_enrd0_shadow & ~CHG_ENDOFCH);
489 * NOTE:
490 * After the conversion to interrupts, ascodec_(lock|unlock) are only used by
491 * adc-as3514.c to protect against other threads corrupting the result by using
492 * the ADC at the same time.
493 * Concurrent ascodec_(async_)?(read|write) calls are instead protected
494 * because ascodec_submit() is atomic and concurrent requests will wait
495 * in the queue until the current request is finished.
497 void ascodec_lock(void)
499 mutex_lock(&as_mtx);
502 void ascodec_unlock(void)
504 mutex_unlock(&as_mtx);