summaryrefslogtreecommitdiff
path: root/firmware/target/arm/as3525/ascodec-as3525.c
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2015-02-14 15:18:52 -0500
committerMichael Sevakis <jethead71@rockbox.org>2015-03-16 03:27:08 -0400
commit36480c259fd087c6c24f0046addf8d5c0380889a (patch)
treefef736be4ab1c2df33d15715badd6b5ff04c2285 /firmware/target/arm/as3525/ascodec-as3525.c
parent98a69ec5009104088cdad3506836e2d042173f64 (diff)
downloadrockbox-36480c259fd087c6c24f0046addf8d5c0380889a.tar.gz
rockbox-36480c259fd087c6c24f0046addf8d5c0380889a.zip
Fix a race condition in as3525 I2C driver caused by stacked ISRs.
It was possible for interrupts of higher priority than the current IRQ level to attempt to restart the interface while it was still active on a transfer. The list modification also wasn't protected within the I2C ISR itself. Change-Id: I70635c307a1443bba6801c588cf1efde299db9a4
Diffstat (limited to 'firmware/target/arm/as3525/ascodec-as3525.c')
-rw-r--r--firmware/target/arm/as3525/ascodec-as3525.c42
1 files changed, 25 insertions, 17 deletions
diff --git a/firmware/target/arm/as3525/ascodec-as3525.c b/firmware/target/arm/as3525/ascodec-as3525.c
index be81178859..634648afe4 100644
--- a/firmware/target/arm/as3525/ascodec-as3525.c
+++ b/firmware/target/arm/as3525/ascodec-as3525.c
@@ -159,21 +159,12 @@ static void ascodec_finish_req(struct ascodec_request *req)
159 */ 159 */
160 while (i2c_busy()); 160 while (i2c_busy());
161 161
162 /* disable clock */
163 bitclr32(&CGU_PERI, CGU_I2C_AUDIO_MASTER_CLOCK_ENABLE);
164
165 req->status = 1; 162 req->status = 1;
166 163
167 if (req->callback) { 164 if (req->callback) {
168 req->callback(req->data, req_data_ptr - req->data); 165 req->callback(req->data, req_data_ptr - req->data);
169 } 166 }
170 semaphore_release(&req->complete); 167 semaphore_release(&req->complete);
171
172 req_head = req->next;
173 req->next = NULL;
174 if (req_head == NULL)
175 req_tail = NULL;
176
177} 168}
178 169
179static int ascodec_continue_req(struct ascodec_request *req, int irq_status) 170static int ascodec_continue_req(struct ascodec_request *req, int irq_status)
@@ -226,13 +217,12 @@ static void ascodec_start_req(struct ascodec_request *req)
226 217
227void INT_I2C_AUDIO(void) 218void INT_I2C_AUDIO(void)
228{ 219{
229 int irq_status = I2C2_MIS; 220 struct ascodec_request *req = req_head;
230 int status = REQ_FINISHED;
231 221
232 if (req_head != NULL) 222 int irq_status = I2C2_MIS;
233 status = ascodec_continue_req(req_head, irq_status); 223 int status = ascodec_continue_req(req, irq_status);
234 224
235 I2C2_INT_CLR |= irq_status; /* clear interrupt status */ 225 I2C2_INT_CLR = irq_status; /* clear interrupt status */
236 226
237 if (status != REQ_UNFINISHED) { 227 if (status != REQ_UNFINISHED) {
238 /* mask rx/tx interrupts */ 228 /* mask rx/tx interrupts */
@@ -240,15 +230,31 @@ void INT_I2C_AUDIO(void)
240 I2C2_IRQ_RXOVER|I2C2_IRQ_ACKTIMEO); 230 I2C2_IRQ_RXOVER|I2C2_IRQ_ACKTIMEO);
241 231
242 if (status == REQ_FINISHED) 232 if (status == REQ_FINISHED)
243 ascodec_finish_req(req_head); 233 ascodec_finish_req(req);
234
235 int oldlevel = disable_irq_save(); /* IRQs are stacked */
244 236
245 /* 237 /*
246 * If status == REQ_RETRY, this will restart 238 * If status == REQ_RETRY, this will restart
247 * the request because we didn't remove it from 239 * the request because we didn't remove it from
248 * the request list 240 * the request list
249 */ 241 */
250 if (req_head) 242
243 if (status == REQ_FINISHED) {
244 req_head = req->next;
245 req->next = NULL;
246 }
247
248 if (req_head == NULL) {
249 req_tail = NULL;
250
251 /* disable clock */
252 bitclr32(&CGU_PERI, CGU_I2C_AUDIO_MASTER_CLOCK_ENABLE);
253 } else {
251 ascodec_start_req(req_head); 254 ascodec_start_req(req_head);
255 }
256
257 restore_irq(oldlevel);
252 } 258 }
253} 259}
254 260
@@ -278,7 +284,7 @@ void ascodec_init(void)
278 I2C2_CNTRL = I2C2_CNTRL_DEFAULT; 284 I2C2_CNTRL = I2C2_CNTRL_DEFAULT;
279 285
280 I2C2_IMR = 0x00; /* disable interrupts */ 286 I2C2_IMR = 0x00; /* disable interrupts */
281 I2C2_INT_CLR |= I2C2_RIS; /* clear interrupt status */ 287 I2C2_INT_CLR = I2C2_RIS; /* clear interrupt status */
282 VIC_INT_ENABLE = INTERRUPT_I2C_AUDIO; 288 VIC_INT_ENABLE = INTERRUPT_I2C_AUDIO;
283 VIC_INT_ENABLE = INTERRUPT_AUDIO; 289 VIC_INT_ENABLE = INTERRUPT_AUDIO;
284 290
@@ -338,6 +344,8 @@ static void ascodec_submit(struct ascodec_request *req)
338 344
339static void ascodec_wait(struct ascodec_request *req) 345static void ascodec_wait(struct ascodec_request *req)
340{ 346{
347 /* NOTE: Only safe from thread context */
348
341 if (irq_enabled()) { 349 if (irq_enabled()) {
342 semaphore_wait(&req->complete, TIMEOUT_BLOCK); 350 semaphore_wait(&req->complete, TIMEOUT_BLOCK);
343 return; 351 return;