summaryrefslogtreecommitdiff
path: root/firmware/target/arm/as3525
diff options
context:
space:
mode:
authorTobias Diedrich <ranma+coreboot@tdiedrich.de>2010-03-23 04:40:38 +0000
committerTobias Diedrich <ranma+coreboot@tdiedrich.de>2010-03-23 04:40:38 +0000
commit946d3d177e060d375d6e435879bc203a01e1b2de (patch)
tree58121491aa3186c2a7a24087b6db88709aa7dd6d /firmware/target/arm/as3525
parentdafca1405e6fa342b5740e74be43f0969b4606c8 (diff)
downloadrockbox-946d3d177e060d375d6e435879bc203a01e1b2de.tar.gz
rockbox-946d3d177e060d375d6e435879bc203a01e1b2de.zip
Rewrite ascodec_as3514.c to use interrupts.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@25297 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware/target/arm/as3525')
-rw-r--r--firmware/target/arm/as3525/ascodec-as3525.c309
-rw-r--r--firmware/target/arm/as3525/ascodec-target.h51
-rw-r--r--firmware/target/arm/as3525/system-as3525.c1
3 files changed, 314 insertions, 47 deletions
diff --git a/firmware/target/arm/as3525/ascodec-as3525.c b/firmware/target/arm/as3525/ascodec-as3525.c
index 3019ffdd15..ca81d7842f 100644
--- a/firmware/target/arm/as3525/ascodec-as3525.c
+++ b/firmware/target/arm/as3525/ascodec-as3525.c
@@ -47,6 +47,7 @@
47#include "ascodec-target.h" 47#include "ascodec-target.h"
48#include "clock-target.h" 48#include "clock-target.h"
49#include "kernel.h" 49#include "kernel.h"
50#include "system.h"
50#include "as3525.h" 51#include "as3525.h"
51#include "i2c.h" 52#include "i2c.h"
52 53
@@ -63,8 +64,61 @@
63#define I2C2_INT_CLR *((volatile unsigned int *)(I2C_AUDIO_BASE + 0x40)) 64#define I2C2_INT_CLR *((volatile unsigned int *)(I2C_AUDIO_BASE + 0x40))
64#define I2C2_SADDR *((volatile unsigned int *)(I2C_AUDIO_BASE + 0x44)) 65#define I2C2_SADDR *((volatile unsigned int *)(I2C_AUDIO_BASE + 0x44))
65 66
67#define I2C2_CNTRL_MASTER 0x01
68#define I2C2_CNTRL_READ 0x02
69#define I2C2_CNTRL_WRITE 0x00
70#define I2C2_CNTRL_RESET 0x10
71#define I2C2_CNTRL_REPSTARTEN 0x40
72
73#define I2C2_CNTRL_DEFAULT (I2C2_CNTRL_MASTER|I2C2_CNTRL_REPSTARTEN|I2C2_CNTRL_RESET)
74
75#define I2C2_IRQ_TXEMPTY 0x04
76#define I2C2_IRQ_RXFULL 0x08
77#define I2C2_IRQ_RXOVER 0x10
78#define I2C2_IRQ_ACKTIMEO 0x80
79
80#define REQ_UNFINISHED 0
81#define REQ_FINISHED 1
82#define REQ_RETRY 2
83
66static struct mutex as_mtx; 84static struct mutex as_mtx;
67 85
86static unsigned char *req_data_ptr = NULL;
87static struct ascodec_request *req_head = NULL;
88static struct ascodec_request *req_tail = NULL;
89
90static void ascodec_start_req(struct ascodec_request *req);
91static int ascodec_continue_req(struct ascodec_request *req, int irq_status);
92static void ascodec_finish_req(struct ascodec_request *req);
93
94void INT_I2C_AUDIO(void)
95{
96 int irq_status = I2C2_MIS;
97 int status = REQ_FINISHED;
98
99 if (req_head != NULL)
100 status = ascodec_continue_req(req_head, irq_status);
101
102 I2C2_INT_CLR |= irq_status; /* clear interrupt status */
103
104 if (status != REQ_UNFINISHED) {
105 /* mask rx/tx interrupts */
106 I2C2_IMR &= ~(I2C2_IRQ_TXEMPTY|I2C2_IRQ_RXFULL|
107 I2C2_IRQ_RXOVER|I2C2_IRQ_ACKTIMEO);
108
109 if (status == REQ_FINISHED)
110 ascodec_finish_req(req_head);
111
112 /*
113 * If status == REQ_RETRY, this will restart
114 * the request because we didn't remove it from
115 * the request list
116 */
117 if (req_head)
118 ascodec_start_req(req_head);
119 }
120}
121
68void i2c_init(void) 122void i2c_init(void)
69{ 123{
70} 124}
@@ -74,6 +128,8 @@ void ascodec_init(void)
74{ 128{
75 int prescaler; 129 int prescaler;
76 130
131 mutex_init(&as_mtx);
132
77 /* enable clock */ 133 /* enable clock */
78 CGU_PERI |= CGU_I2C_AUDIO_MASTER_CLOCK_ENABLE; 134 CGU_PERI |= CGU_I2C_AUDIO_MASTER_CLOCK_ENABLE;
79 135
@@ -85,9 +141,11 @@ void ascodec_init(void)
85 /* set i2c slave address of codec part */ 141 /* set i2c slave address of codec part */
86 I2C2_SLAD0 = AS3514_I2C_ADDR << 1; 142 I2C2_SLAD0 = AS3514_I2C_ADDR << 1;
87 143
88 I2C2_CNTRL = 0x51; 144 I2C2_CNTRL = I2C2_CNTRL_DEFAULT;
89 145
90 mutex_init(&as_mtx); 146 I2C2_IMR = 0x00; /* disable interrupts */
147 I2C2_INT_CLR |= I2C2_RIS; /* clear interrupt status */
148 VIC_INT_ENABLE = INTERRUPT_I2C_AUDIO;
91} 149}
92 150
93 151
@@ -97,78 +155,235 @@ static int i2c_busy(void)
97 return (I2C2_SR & 1); 155 return (I2C2_SR & 1);
98} 156}
99 157
100/* returns 0 on success, <0 otherwise */ 158void ascodec_req_init(struct ascodec_request *req, int type,
101int ascodec_write(unsigned int index, unsigned int value) 159 unsigned int index, unsigned int cnt)
160{
161 wakeup_init(&req->wkup);
162 req->next = NULL;
163 req->callback = NULL;
164 req->type = type;
165 req->index = index;
166 req->cnt = cnt;
167}
168
169void ascodec_submit(struct ascodec_request *req)
102{ 170{
103 ascodec_lock(); 171 int oldlevel = disable_irq_save();
104 172
105 /* wait if still busy */ 173 req->status = 0;
174
175 if (req_head == NULL) {
176 req_tail = req_head = req;
177 ascodec_start_req(req);
178 } else {
179 req_tail->next = req;
180 req_tail = req;
181 }
182
183 restore_irq(oldlevel);
184}
185
186static void ascodec_start_req(struct ascodec_request *req)
187{
188 int unmask = 0;
189
190 /* enable clock */
191 CGU_PERI |= CGU_I2C_AUDIO_MASTER_CLOCK_ENABLE;
192
193 /* start transfer */
194 I2C2_SADDR = req->index;
195 if (req->type == ASCODEC_REQ_READ) {
196 req_data_ptr = req->data;
197 /* start transfer */
198 I2C2_CNTRL = I2C2_CNTRL_DEFAULT | I2C2_CNTRL_READ;
199 unmask = I2C2_IRQ_RXFULL|I2C2_IRQ_RXOVER;
200 } else {
201 req_data_ptr = &req->data[1];
202 I2C2_CNTRL = I2C2_CNTRL_DEFAULT | I2C2_CNTRL_WRITE;
203 I2C2_DATA = req->data[0];
204 unmask = I2C2_IRQ_TXEMPTY|I2C2_IRQ_ACKTIMEO;
205 }
206
207 I2C2_DACNT = req->cnt;
208 I2C2_IMR |= unmask; /* enable interrupts */
209}
210
211static int ascodec_continue_req(struct ascodec_request *req, int irq_status)
212{
213 if ((irq_status & (I2C2_IRQ_RXOVER|I2C2_IRQ_ACKTIMEO)) > 0) {
214 /* some error occured, restart the request */
215 return REQ_RETRY;
216 }
217 if (req->type == ASCODEC_REQ_READ &&
218 (irq_status & I2C2_IRQ_RXFULL) > 0) {
219 *(req_data_ptr++) = I2C2_DATA;
220 } else {
221 if (req->cnt > 1 &&
222 (irq_status & I2C2_IRQ_TXEMPTY) > 0) {
223 I2C2_DATA = *(req_data_ptr++);
224 }
225 }
226
227 req->index++;
228 if (--req->cnt > 0)
229 return REQ_UNFINISHED;
230
231 return REQ_FINISHED;
232}
233
234static void ascodec_finish_req(struct ascodec_request *req)
235{
236 /*
237 * Wait if still busy, unfortunately this happens since
238 * the controller is running at a low divisor, so it's
239 * still busy when we serviced the interrupt.
240 * I tried upping the i2c speed to 4MHz which
241 * made the number of busywait cycles much smaller
242 * (none for reads and only a few for writes),
243 * but who knows if it's reliable at that frequency. ;)
244 * For one thing, 8MHz doesn't work, so 4MHz is likely
245 * borderline.
246 * In general writes need much more wait cycles than reads
247 * for some reason, possibly because we read the data register
248 * for reads, which will likely block the processor while
249 * the i2c controller responds to the register read.
250 */
106 while (i2c_busy()); 251 while (i2c_busy());
107 252
253 /* disable clock */
254 CGU_PERI &= ~CGU_I2C_AUDIO_MASTER_CLOCK_ENABLE;
255
256 req->status = 1;
257
258 if (req->callback) {
259 req->callback(req->data, req_data_ptr - req->data);
260 }
261 wakeup_signal(&req->wkup);
262
263 req_head = req->next;
264 req->next = NULL;
265 if (req_head == NULL)
266 req_tail = NULL;
267
268}
269
270static int irq_disabled(void)
271{
272 unsigned long cpsr;
273
274 asm volatile ("mrs %0, cpsr" : "=r"(cpsr));
275
276 return (cpsr & IRQ_STATUS) == IRQ_DISABLED;
277}
278
279static void ascodec_wait(struct ascodec_request *req)
280{
281 if (!irq_disabled()) {
282 wakeup_wait(&req->wkup, TIMEOUT_BLOCK);
283 return;
284 }
285
286 while (req->status == 0) {
287 if (I2C2_MIS) INT_I2C_AUDIO();
288 }
289}
290
291/*
292 * The request struct passed in must be allocated statically.
293 * If you call ascodec_async_write from different places, each
294 * call needs it's own request struct.
295 * This comment is duplicated in .c and .h for your convenience.
296 */
297void ascodec_async_write(unsigned int index, unsigned int value,
298 struct ascodec_request *req)
299{
108 if (index == AS3514_CVDD_DCDC3) { 300 if (index == AS3514_CVDD_DCDC3) {
109 /* prevent setting of the LREG_CP_not bit */ 301 /* prevent setting of the LREG_CP_not bit */
110 value &= ~(1 << 5); 302 value &= ~(1 << 5);
111 } 303 }
112
113 /* start transfer */
114 I2C2_SADDR = index;
115 I2C2_CNTRL &= ~(1 << 1);
116 I2C2_DATA = value;
117 I2C2_DACNT = 1;
118
119 /* wait for transfer */
120 while (I2C2_DACNT != 0);
121 304
122 ascodec_unlock(); 305 ascodec_req_init(req, ASCODEC_REQ_WRITE, index, 1);
306 req->data[0] = value;
307 ascodec_submit(req);
308}
309
310/* returns 0 on success, <0 otherwise */
311int ascodec_write(unsigned int index, unsigned int value)
312{
313 struct ascodec_request req;
314
315 ascodec_async_write(index, value, &req);
316 ascodec_wait(&req);
123 317
124 return 0; 318 return 0;
125} 319}
126 320
321/*
322 * The request struct passed in must be allocated statically.
323 * If you call ascodec_async_read from different places, each
324 * call needs it's own request struct.
325 * If len is bigger than ASCODEC_REQ_MAXLEN it will be
326 * set to ASCODEC_REQ_MAXLEN.
327 * This comment is duplicated in .c and .h for your convenience.
328 */
329void ascodec_async_read(unsigned int index, unsigned int len,
330 struct ascodec_request *req, ascodec_cb_fn *cb)
331{
332 if (len > ASCODEC_REQ_MAXLEN)
333 len = ASCODEC_REQ_MAXLEN; /* can't fit more in one request */
334
335 ascodec_req_init(req, ASCODEC_REQ_READ, index, len);
336 req->callback = cb;
337 ascodec_submit(req);
338}
127 339
128/* returns value read on success, <0 otherwise */ 340/* returns value read on success, <0 otherwise */
129int ascodec_read(unsigned int index) 341int ascodec_read(unsigned int index)
130{ 342{
131 int data; 343 struct ascodec_request req;
132
133 ascodec_lock();
134 344
135 /* wait if still busy */ 345 ascodec_async_read(index, 1, &req, NULL);
136 while (i2c_busy()); 346 ascodec_wait(&req);
137 347
138 /* start transfer */ 348 return req.data[0];
139 I2C2_SADDR = index;
140 I2C2_CNTRL |= (1 << 1);
141 I2C2_DACNT = 1;
142
143 /* wait for transfer*/
144 while (I2C2_DACNT != 0);
145 data = I2C2_DATA;
146
147 ascodec_unlock();
148
149 return data;
150} 349}
151 350
152int ascodec_readbytes(int index, int len, unsigned char *data) 351int ascodec_readbytes(unsigned int index, unsigned int len, unsigned char *data)
153{ 352{
154 int i; 353 int i, j;
354 struct ascodec_request req;
155 355
156 ascodec_lock(); 356 /* index and cnt will be filled in later, just use 0 */
357 ascodec_req_init(&req, ASCODEC_REQ_READ, 0, 0);
157 358
158 for(i=0; i<len; i++) 359 i = 0;
159 { 360 while (len > 0) {
160 int temp = ascodec_read(index+i); 361 int cnt = len > ASCODEC_REQ_MAXLEN ? ASCODEC_REQ_MAXLEN : len;
161 if(temp == -1) 362
162 break; 363 req.index = index;
163 else 364 req.cnt = cnt;
164 data[i] = temp;
165 }
166 365
167 ascodec_unlock(); 366 ascodec_submit(&req);
367 ascodec_wait(&req);
368
369 for (j=0; j<cnt; j++) data[i++] = req.data[j];
370
371 len -= cnt;
372 index += cnt;
373 }
168 374
169 return i; 375 return i;
170} 376}
171 377
378/*
379 * NOTE:
380 * After the conversion to interrupts, ascodec_(lock|unlock) are only used by
381 * adc-as3514.c to protect against other threads corrupting the result by using
382 * the ADC at the same time.
383 * Concurrent ascodec_(async_)?(read|write) calls are instead protected
384 * because ascodec_submit() is atomic and concurrent requests will wait
385 * in the queue until the current request is finished.
386 */
172void ascodec_lock(void) 387void ascodec_lock(void)
173{ 388{
174 mutex_lock(&as_mtx); 389 mutex_lock(&as_mtx);
@@ -178,3 +393,5 @@ void ascodec_unlock(void)
178{ 393{
179 mutex_unlock(&as_mtx); 394 mutex_unlock(&as_mtx);
180} 395}
396
397/* vim:set ts=4 sw=4 et: */
diff --git a/firmware/target/arm/as3525/ascodec-target.h b/firmware/target/arm/as3525/ascodec-target.h
index 3464bbaa51..4b110412c1 100644
--- a/firmware/target/arm/as3525/ascodec-target.h
+++ b/firmware/target/arm/as3525/ascodec-target.h
@@ -26,6 +26,7 @@
26#define _ASCODEC_TARGET_H 26#define _ASCODEC_TARGET_H
27 27
28#include "as3514.h" 28#include "as3514.h"
29#include "kernel.h" /* for struct wakeup */
29 30
30/* Charge Pump and Power management Settings */ 31/* Charge Pump and Power management Settings */
31#define AS314_CP_DCDC3_SETTING \ 32#define AS314_CP_DCDC3_SETTING \
@@ -41,13 +42,61 @@
41#define CVDD_1_10 2 42#define CVDD_1_10 2
42#define CVDD_1_05 3 43#define CVDD_1_05 3
43 44
45#define ASCODEC_REQ_READ 0
46#define ASCODEC_REQ_WRITE 1
47
48/*
49 * How many bytes we using in struct ascodec_request for the data buffer.
50 * 4 fits the alignment best right now.
51 * We don't actually use more than 2 at the moment (in adc_read).
52 * Upper limit would be 255 since DACNT is 8 bits!
53 */
54#define ASCODEC_REQ_MAXLEN 4
55
56typedef void (ascodec_cb_fn)(unsigned const char *data, unsigned cnt);
57
58struct ascodec_request {
59 unsigned char type;
60 unsigned char index;
61 unsigned char status;
62 unsigned char cnt;
63 unsigned char data[ASCODEC_REQ_MAXLEN];
64 struct wakeup wkup;
65 ascodec_cb_fn *callback;
66 struct ascodec_request *next;
67};
68
44void ascodec_init(void); 69void ascodec_init(void);
45 70
46int ascodec_write(unsigned int index, unsigned int value); 71int ascodec_write(unsigned int index, unsigned int value);
47 72
48int ascodec_read(unsigned int index); 73int ascodec_read(unsigned int index);
49 74
50int ascodec_readbytes(int index, int len, unsigned char *data); 75int ascodec_readbytes(unsigned int index, unsigned int len, unsigned char *data);
76
77/*
78 * The request struct passed in must be allocated statically.
79 * If you call ascodec_async_write from different places, each
80 * call needs it's own request struct.
81 * This comment is duplicated in .c and .h for your convenience.
82 */
83void ascodec_async_write(unsigned index, unsigned int value, struct ascodec_request *req);
84
85/*
86 * The request struct passed in must be allocated statically.
87 * If you call ascodec_async_read from different places, each
88 * call needs it's own request struct.
89 * If len is bigger than ASCODEC_REQ_MAXLEN it will be
90 * set to ASCODEC_REQ_MAXLEN.
91 * This comment is duplicated in .c and .h for your convenience.
92 */
93void ascodec_async_read(unsigned index, unsigned int len,
94 struct ascodec_request *req, ascodec_cb_fn *cb);
95
96void ascodec_req_init(struct ascodec_request *req, int type,
97 unsigned int index, unsigned int cnt);
98
99void ascodec_submit(struct ascodec_request *req);
51 100
52void ascodec_lock(void); 101void ascodec_lock(void);
53 102
diff --git a/firmware/target/arm/as3525/system-as3525.c b/firmware/target/arm/as3525/system-as3525.c
index 4bf5cd39c3..4ee3e594a5 100644
--- a/firmware/target/arm/as3525/system-as3525.c
+++ b/firmware/target/arm/as3525/system-as3525.c
@@ -111,6 +111,7 @@ struct vec_int_src vec_int_srcs[] =
111 { INT_SRC_TIMER2, INT_TIMER2 }, 111 { INT_SRC_TIMER2, INT_TIMER2 },
112 { INT_SRC_DMAC, INT_DMAC }, 112 { INT_SRC_DMAC, INT_DMAC },
113 { INT_SRC_NAND, INT_NAND }, 113 { INT_SRC_NAND, INT_NAND },
114 { INT_SRC_I2C_AUDIO, INT_I2C_AUDIO },
114#ifdef HAVE_MULTIDRIVE 115#ifdef HAVE_MULTIDRIVE
115 { INT_SRC_MCI0, INT_MCI0 }, 116 { INT_SRC_MCI0, INT_MCI0 },
116#endif 117#endif