diff options
Diffstat (limited to 'firmware/target/arm')
-rw-r--r-- | firmware/target/arm/as3525/ascodec-as3525.c | 309 | ||||
-rw-r--r-- | firmware/target/arm/as3525/ascodec-target.h | 51 | ||||
-rw-r--r-- | firmware/target/arm/as3525/system-as3525.c | 1 |
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 | |||
66 | static struct mutex as_mtx; | 84 | static struct mutex as_mtx; |
67 | 85 | ||
86 | static unsigned char *req_data_ptr = NULL; | ||
87 | static struct ascodec_request *req_head = NULL; | ||
88 | static struct ascodec_request *req_tail = NULL; | ||
89 | |||
90 | static void ascodec_start_req(struct ascodec_request *req); | ||
91 | static int ascodec_continue_req(struct ascodec_request *req, int irq_status); | ||
92 | static void ascodec_finish_req(struct ascodec_request *req); | ||
93 | |||
94 | void 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 | |||
68 | void i2c_init(void) | 122 | void 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 */ | 158 | void ascodec_req_init(struct ascodec_request *req, int type, |
101 | int 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 | |||
169 | void 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 | |||
186 | static 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 | |||
211 | static 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 | |||
234 | static 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 | |||
270 | static 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 | |||
279 | static 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 | */ | ||
297 | void 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 */ | ||
311 | int 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 | */ | ||
329 | void 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 */ |
129 | int ascodec_read(unsigned int index) | 341 | int 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 | ||
152 | int ascodec_readbytes(int index, int len, unsigned char *data) | 351 | int 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 | */ | ||
172 | void ascodec_lock(void) | 387 | void 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 | |||
56 | typedef void (ascodec_cb_fn)(unsigned const char *data, unsigned cnt); | ||
57 | |||
58 | struct 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 | |||
44 | void ascodec_init(void); | 69 | void ascodec_init(void); |
45 | 70 | ||
46 | int ascodec_write(unsigned int index, unsigned int value); | 71 | int ascodec_write(unsigned int index, unsigned int value); |
47 | 72 | ||
48 | int ascodec_read(unsigned int index); | 73 | int ascodec_read(unsigned int index); |
49 | 74 | ||
50 | int ascodec_readbytes(int index, int len, unsigned char *data); | 75 | int 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 | */ | ||
83 | void 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 | */ | ||
93 | void ascodec_async_read(unsigned index, unsigned int len, | ||
94 | struct ascodec_request *req, ascodec_cb_fn *cb); | ||
95 | |||
96 | void ascodec_req_init(struct ascodec_request *req, int type, | ||
97 | unsigned int index, unsigned int cnt); | ||
98 | |||
99 | void ascodec_submit(struct ascodec_request *req); | ||
51 | 100 | ||
52 | void ascodec_lock(void); | 101 | void 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 |