diff options
author | Michael Sevakis <jethead71@rockbox.org> | 2011-12-18 10:41:43 +0000 |
---|---|---|
committer | Michael Sevakis <jethead71@rockbox.org> | 2011-12-18 10:41:43 +0000 |
commit | 292e7cab73c75260b7cfb7f90ad28938c8f1117a (patch) | |
tree | 4341681314958c3a9aa663e5ace52eb4b1e107ab /firmware/target/arm/imx31/mc13783-imx31.c | |
parent | 0efabb3d19e7690585641c4a19eca131d15b59d8 (diff) | |
download | rockbox-292e7cab73c75260b7cfb7f90ad28938c8f1117a.tar.gz rockbox-292e7cab73c75260b7cfb7f90ad28938c8f1117a.zip |
Gigabeat S: PMIC SPI improvement and bugfixes.
Nick some aspects from the as3525 ascodec driver to improve throughput in
the beast's SPI communications by switching tranfer descriptors to the
caller's stack and getting rid of thread synchronization.
Fix a bug that suddenly became obvious that could permanently stall the SPI
driver because all data could be shifted out before the interrupt could get
serviced. In that case, it needs a kick to restart it. Should probably put
the SPI interrupt priority above DVFS.
A tweak to the event registration interface to simplify it.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@31353 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware/target/arm/imx31/mc13783-imx31.c')
-rw-r--r-- | firmware/target/arm/imx31/mc13783-imx31.c | 278 |
1 files changed, 106 insertions, 172 deletions
diff --git a/firmware/target/arm/imx31/mc13783-imx31.c b/firmware/target/arm/imx31/mc13783-imx31.c index 094fbaa58b..268c33a549 100644 --- a/firmware/target/arm/imx31/mc13783-imx31.c +++ b/firmware/target/arm/imx31/mc13783-imx31.c | |||
@@ -34,10 +34,6 @@ static int mc13783_thread_stack[DEFAULT_STACK_SIZE/sizeof(int)]; | |||
34 | static const char * const mc13783_thread_name = "pmic"; | 34 | static const char * const mc13783_thread_name = "pmic"; |
35 | static struct semaphore mc13783_svc_wake; | 35 | static struct semaphore mc13783_svc_wake; |
36 | 36 | ||
37 | /* Synchronous thread communication objects */ | ||
38 | static struct mutex mc13783_spi_mutex; | ||
39 | static struct semaphore mc13783_spi_complete; | ||
40 | |||
41 | /* Tracking for which interrupts are enabled */ | 37 | /* Tracking for which interrupts are enabled */ |
42 | static uint32_t pmic_int_enabled[2] = | 38 | static uint32_t pmic_int_enabled[2] = |
43 | { 0x00000000, 0x00000000 }; | 39 | { 0x00000000, 0x00000000 }; |
@@ -50,32 +46,27 @@ static const unsigned char pmic_ints_regs[2] = | |||
50 | 46 | ||
51 | static volatile unsigned int mc13783_thread_id = 0; | 47 | static volatile unsigned int mc13783_thread_id = 0; |
52 | 48 | ||
53 | static void mc13783_xfer_complete_cb(struct spi_transfer_desc *trans); | 49 | /* Extend the basic SPI transfer descriptor with our own fields */ |
54 | 50 | struct mc13783_transfer_desc | |
55 | /* Transfer descriptor for synchronous reads and writes */ | ||
56 | static struct spi_transfer_desc mc13783_transfer = | ||
57 | { | 51 | { |
58 | .node = &mc13783_spi, | 52 | struct spi_transfer_desc xfer; |
59 | .txbuf = NULL, | 53 | union |
60 | .rxbuf = NULL, | 54 | { |
61 | .count = 0, | 55 | struct semaphore sema; |
62 | .callback = mc13783_xfer_complete_cb, | 56 | uint32_t data; |
63 | .next = NULL, | 57 | }; |
64 | }; | 58 | }; |
65 | 59 | ||
66 | /* Called when a transfer is finished and data is ready/written */ | 60 | /* Called when a transfer is finished and data is ready/written */ |
67 | static void mc13783_xfer_complete_cb(struct spi_transfer_desc *xfer) | 61 | static void mc13783_xfer_complete_cb(struct spi_transfer_desc *xfer) |
68 | { | 62 | { |
69 | if (xfer->count != 0) | 63 | semaphore_release(&((struct mc13783_transfer_desc *)xfer)->sema); |
70 | return; | ||
71 | |||
72 | semaphore_release(&mc13783_spi_complete); | ||
73 | } | 64 | } |
74 | 65 | ||
75 | static inline bool wait_for_transfer_complete(void) | 66 | static inline bool wait_for_transfer_complete(struct mc13783_transfer_desc *xfer) |
76 | { | 67 | { |
77 | return semaphore_wait(&mc13783_spi_complete, HZ*2) | 68 | return semaphore_wait(&xfer->sema, TIMEOUT_BLOCK) |
78 | == OBJ_WAIT_SUCCEEDED && mc13783_transfer.count == 0; | 69 | == OBJ_WAIT_SUCCEEDED && xfer->xfer.count == 0; |
79 | } | 70 | } |
80 | 71 | ||
81 | static void mc13783_interrupt_thread(void) | 72 | static void mc13783_interrupt_thread(void) |
@@ -114,15 +105,14 @@ static void mc13783_interrupt_thread(void) | |||
114 | /* .count is surely expected to be > 0 */ | 105 | /* .count is surely expected to be > 0 */ |
115 | do | 106 | do |
116 | { | 107 | { |
117 | enum mc13783_event_sets set = event->set; | 108 | unsigned int set = event->int_id / MC13783_INT_ID_SET_DIV; |
118 | uint32_t pnd = pending[set]; | 109 | uint32_t pnd = pending[set]; |
119 | uint32_t mask = event->mask; | 110 | uint32_t mask = 1 << (event->int_id & MC13783_INT_ID_NUM_MASK); |
120 | 111 | ||
121 | if (pnd & mask) | 112 | if (pnd & mask) |
122 | { | 113 | { |
123 | event->callback(); | 114 | event->callback(); |
124 | pnd &= ~mask; | 115 | pending[set] = pnd & ~mask; |
125 | pending[set] = pnd; | ||
126 | } | 116 | } |
127 | 117 | ||
128 | if ((pending[0] | pending[1]) == 0) | 118 | if ((pending[0] | pending[1]) == 0) |
@@ -147,9 +137,6 @@ void INIT_ATTR mc13783_init(void) | |||
147 | { | 137 | { |
148 | /* Serial interface must have been initialized first! */ | 138 | /* Serial interface must have been initialized first! */ |
149 | semaphore_init(&mc13783_svc_wake, 1, 0); | 139 | semaphore_init(&mc13783_svc_wake, 1, 0); |
150 | mutex_init(&mc13783_spi_mutex); | ||
151 | |||
152 | semaphore_init(&mc13783_spi_complete, 1, 0); | ||
153 | 140 | ||
154 | /* Enable the PMIC SPI module */ | 141 | /* Enable the PMIC SPI module */ |
155 | spi_enable_module(&mc13783_spi); | 142 | spi_enable_module(&mc13783_spi); |
@@ -183,205 +170,169 @@ void mc13783_close(void) | |||
183 | bool mc13783_enable_event(enum mc13783_event_ids id) | 170 | bool mc13783_enable_event(enum mc13783_event_ids id) |
184 | { | 171 | { |
185 | const struct mc13783_event * const event = &mc13783_events[id]; | 172 | const struct mc13783_event * const event = &mc13783_events[id]; |
186 | int set = event->set; | 173 | unsigned int set = event->int_id / MC13783_INT_ID_SET_DIV; |
187 | uint32_t mask = event->mask; | 174 | uint32_t mask = 1 << (event->int_id & MC13783_INT_ID_NUM_MASK); |
188 | |||
189 | mutex_lock(&mc13783_spi_mutex); | ||
190 | 175 | ||
191 | pmic_int_enabled[set] |= mask; | 176 | pmic_int_enabled[set] |= mask; |
192 | mc13783_clear(pmic_intm_regs[set], mask); | 177 | mc13783_clear(pmic_intm_regs[set], mask); |
193 | 178 | ||
194 | mutex_unlock(&mc13783_spi_mutex); | ||
195 | |||
196 | return true; | 179 | return true; |
197 | } | 180 | } |
198 | 181 | ||
199 | void mc13783_disable_event(enum mc13783_event_ids id) | 182 | void mc13783_disable_event(enum mc13783_event_ids id) |
200 | { | 183 | { |
201 | const struct mc13783_event * const event = &mc13783_events[id]; | 184 | const struct mc13783_event * const event = &mc13783_events[id]; |
202 | int set = event->set; | 185 | unsigned int set = event->int_id / MC13783_INT_ID_SET_DIV; |
203 | uint32_t mask = event->mask; | 186 | uint32_t mask = 1 << (event->int_id & MC13783_INT_ID_NUM_MASK); |
204 | |||
205 | mutex_lock(&mc13783_spi_mutex); | ||
206 | 187 | ||
207 | pmic_int_enabled[set] &= ~mask; | 188 | pmic_int_enabled[set] &= ~mask; |
208 | mc13783_set(pmic_intm_regs[set], mask); | 189 | mc13783_set(pmic_intm_regs[set], mask); |
209 | |||
210 | mutex_unlock(&mc13783_spi_mutex); | ||
211 | } | 190 | } |
212 | 191 | ||
213 | uint32_t mc13783_set(unsigned address, uint32_t bits) | 192 | static inline bool mc13783_transfer(struct spi_transfer_desc *xfer, |
193 | uint32_t *txbuf, | ||
194 | uint32_t *rxbuf, | ||
195 | int count, | ||
196 | spi_transfer_cb_fn_type callback) | ||
214 | { | 197 | { |
215 | uint32_t data; | 198 | xfer->node = &mc13783_spi; |
216 | 199 | xfer->txbuf = txbuf; | |
217 | mutex_lock(&mc13783_spi_mutex); | 200 | xfer->rxbuf = rxbuf; |
218 | 201 | xfer->count = count; | |
219 | data = mc13783_read(address); | 202 | xfer->callback = callback; |
220 | 203 | xfer->next = NULL; | |
221 | if (data != MC13783_DATA_ERROR) | ||
222 | mc13783_write(address, data | bits); | ||
223 | 204 | ||
224 | mutex_unlock(&mc13783_spi_mutex); | 205 | return spi_transfer(xfer); |
206 | } | ||
225 | 207 | ||
226 | return data; | 208 | uint32_t mc13783_set(unsigned address, uint32_t bits) |
209 | { | ||
210 | return mc13783_write_masked(address, bits, bits); | ||
227 | } | 211 | } |
228 | 212 | ||
229 | uint32_t mc13783_clear(unsigned address, uint32_t bits) | 213 | uint32_t mc13783_clear(unsigned address, uint32_t bits) |
230 | { | 214 | { |
231 | uint32_t data; | 215 | return mc13783_write_masked(address, 0, bits); |
232 | |||
233 | mutex_lock(&mc13783_spi_mutex); | ||
234 | |||
235 | data = mc13783_read(address); | ||
236 | |||
237 | if (data != MC13783_DATA_ERROR) | ||
238 | mc13783_write(address, data & ~bits); | ||
239 | |||
240 | mutex_unlock(&mc13783_spi_mutex); | ||
241 | |||
242 | return data; | ||
243 | } | 216 | } |
244 | 217 | ||
245 | int mc13783_write(unsigned address, uint32_t data) | 218 | /* Called when the first transfer of mc13783_write_masked is complete */ |
219 | static void mc13783_write_masked_cb(struct spi_transfer_desc *xfer) | ||
246 | { | 220 | { |
247 | uint32_t packet; | 221 | struct mc13783_transfer_desc *desc = (struct mc13783_transfer_desc *)xfer; |
248 | int i; | 222 | uint32_t *packets = desc->xfer.rxbuf; /* Will have been advanced by 1 */ |
223 | packets[0] |= packets[-1] & ~desc->data; | ||
224 | } | ||
249 | 225 | ||
226 | uint32_t mc13783_write_masked(unsigned address, uint32_t data, uint32_t mask) | ||
227 | { | ||
250 | if (address >= MC13783_NUM_REGS) | 228 | if (address >= MC13783_NUM_REGS) |
251 | return -1; | 229 | return MC13783_DATA_ERROR; |
230 | |||
231 | mask &= 0xffffff; | ||
252 | 232 | ||
253 | packet = (1 << 31) | (address << 25) | (data & 0xffffff); | 233 | uint32_t packets[2] = |
234 | { | ||
235 | address << 25, | ||
236 | (1 << 31) | (address << 25) | (data & mask) | ||
237 | }; | ||
254 | 238 | ||
255 | mutex_lock(&mc13783_spi_mutex); | 239 | struct mc13783_transfer_desc xfers[2]; |
240 | xfers[0].data = mask; | ||
241 | semaphore_init(&xfers[1].sema, 1, 0); | ||
256 | 242 | ||
257 | mc13783_transfer.txbuf = &packet; | 243 | unsigned long cpsr = disable_irq_save(); |
258 | mc13783_transfer.rxbuf = NULL; | ||
259 | mc13783_transfer.count = 1; | ||
260 | 244 | ||
261 | i = -1; | 245 | /* Queue up two transfers in a row */ |
246 | bool ok = mc13783_transfer(&xfers[0].xfer, &packets[0], &packets[0], 1, | ||
247 | mc13783_write_masked_cb) && | ||
248 | mc13783_transfer(&xfers[1].xfer, &packets[1], NULL, 1, | ||
249 | mc13783_xfer_complete_cb); | ||
262 | 250 | ||
263 | if (spi_transfer(&mc13783_transfer) && wait_for_transfer_complete()) | 251 | restore_irq(cpsr); |
264 | i = 1 - mc13783_transfer.count; | ||
265 | 252 | ||
266 | mutex_unlock(&mc13783_spi_mutex); | 253 | if (ok && wait_for_transfer_complete(&xfers[1])) |
254 | return packets[0]; | ||
267 | 255 | ||
268 | return i; | 256 | return MC13783_DATA_ERROR; |
269 | } | 257 | } |
270 | 258 | ||
271 | uint32_t mc13783_write_masked(unsigned address, uint32_t data, uint32_t mask) | 259 | uint32_t mc13783_read(unsigned address) |
272 | { | 260 | { |
273 | uint32_t old; | 261 | if (address >= MC13783_NUM_REGS) |
262 | return MC13783_DATA_ERROR; | ||
274 | 263 | ||
275 | mutex_lock(&mc13783_spi_mutex); | 264 | uint32_t packet = address << 25; |
276 | 265 | ||
277 | old = mc13783_read(address); | 266 | struct mc13783_transfer_desc xfer; |
267 | semaphore_init(&xfer.sema, 1, 0); | ||
278 | 268 | ||
279 | if (old != MC13783_DATA_ERROR) | 269 | if (mc13783_transfer(&xfer.xfer, &packet, &packet, 1, |
270 | mc13783_xfer_complete_cb) && | ||
271 | wait_for_transfer_complete(&xfer)) | ||
280 | { | 272 | { |
281 | data = (old & ~mask) | (data & mask); | 273 | return packet; |
282 | |||
283 | if (mc13783_write(address, data) != 1) | ||
284 | old = MC13783_DATA_ERROR; | ||
285 | } | 274 | } |
286 | 275 | ||
287 | mutex_unlock(&mc13783_spi_mutex); | 276 | return MC13783_DATA_ERROR; |
288 | |||
289 | return old; | ||
290 | } | 277 | } |
291 | 278 | ||
292 | uint32_t mc13783_read(unsigned address) | 279 | int mc13783_write(unsigned address, uint32_t data) |
293 | { | 280 | { |
294 | uint32_t packet; | ||
295 | |||
296 | if (address >= MC13783_NUM_REGS) | 281 | if (address >= MC13783_NUM_REGS) |
297 | return MC13783_DATA_ERROR; | 282 | return -1; |
298 | |||
299 | packet = address << 25; | ||
300 | |||
301 | mutex_lock(&mc13783_spi_mutex); | ||
302 | 283 | ||
303 | mc13783_transfer.txbuf = &packet; | 284 | uint32_t packet = (1 << 31) | (address << 25) | (data & 0xffffff); |
304 | mc13783_transfer.rxbuf = &packet; | ||
305 | mc13783_transfer.count = 1; | ||
306 | 285 | ||
307 | if (!spi_transfer(&mc13783_transfer) || !wait_for_transfer_complete()) | 286 | struct mc13783_transfer_desc xfer; |
308 | packet = MC13783_DATA_ERROR; | 287 | semaphore_init(&xfer.sema, 1, 0); |
309 | 288 | ||
310 | mutex_unlock(&mc13783_spi_mutex); | 289 | if (mc13783_transfer(&xfer.xfer, &packet, NULL, 1, |
290 | mc13783_xfer_complete_cb) && | ||
291 | wait_for_transfer_complete(&xfer)) | ||
292 | { | ||
293 | return 1 - xfer.xfer.count; | ||
294 | } | ||
311 | 295 | ||
312 | return packet; | 296 | return -1; |
313 | } | 297 | } |
314 | 298 | ||
315 | int mc13783_read_regs(const unsigned char *regs, uint32_t *buffer, | 299 | int mc13783_read_regs(const unsigned char *regs, uint32_t *buffer, |
316 | int count) | 300 | int count) |
317 | { | 301 | { |
318 | int i; | 302 | struct mc13783_transfer_desc xfer; |
303 | semaphore_init(&xfer.sema, 1, 0); | ||
319 | 304 | ||
320 | for (i = 0; i < count; i++) | 305 | if (mc13783_read_async(&xfer.xfer, regs, buffer, count, |
306 | mc13783_xfer_complete_cb) && | ||
307 | wait_for_transfer_complete(&xfer)) | ||
321 | { | 308 | { |
322 | unsigned reg = regs[i]; | 309 | return count - xfer.xfer.count; |
323 | |||
324 | if (reg >= MC13783_NUM_REGS) | ||
325 | return -1; | ||
326 | |||
327 | buffer[i] = reg << 25; | ||
328 | } | 310 | } |
329 | 311 | ||
330 | mutex_lock(&mc13783_spi_mutex); | 312 | return -1; |
331 | |||
332 | mc13783_transfer.txbuf = buffer; | ||
333 | mc13783_transfer.rxbuf = buffer; | ||
334 | mc13783_transfer.count = count; | ||
335 | |||
336 | i = -1; | ||
337 | |||
338 | if (spi_transfer(&mc13783_transfer) && wait_for_transfer_complete()) | ||
339 | i = count - mc13783_transfer.count; | ||
340 | |||
341 | mutex_unlock(&mc13783_spi_mutex); | ||
342 | |||
343 | return i; | ||
344 | } | 313 | } |
345 | 314 | ||
346 | int mc13783_write_regs(const unsigned char *regs, uint32_t *buffer, | 315 | int mc13783_write_regs(const unsigned char *regs, uint32_t *buffer, |
347 | int count) | 316 | int count) |
348 | { | 317 | { |
349 | int i; | 318 | struct mc13783_transfer_desc xfer; |
319 | semaphore_init(&xfer.sema, 1, 0); | ||
350 | 320 | ||
351 | for (i = 0; i < count; i++) | 321 | if (mc13783_write_async(&xfer.xfer, regs, buffer, count, |
322 | mc13783_xfer_complete_cb) && | ||
323 | wait_for_transfer_complete(&xfer)) | ||
352 | { | 324 | { |
353 | unsigned reg = regs[i]; | 325 | return count - xfer.xfer.count; |
354 | |||
355 | if (reg >= MC13783_NUM_REGS) | ||
356 | return -1; | ||
357 | |||
358 | buffer[i] = (1 << 31) | (reg << 25) | (buffer[i] & 0xffffff); | ||
359 | } | 326 | } |
360 | 327 | ||
361 | mutex_lock(&mc13783_spi_mutex); | 328 | return -1; |
362 | |||
363 | mc13783_transfer.txbuf = buffer; | ||
364 | mc13783_transfer.rxbuf = NULL; | ||
365 | mc13783_transfer.count = count; | ||
366 | |||
367 | i = -1; | ||
368 | |||
369 | if (spi_transfer(&mc13783_transfer) && wait_for_transfer_complete()) | ||
370 | i = count - mc13783_transfer.count; | ||
371 | |||
372 | mutex_unlock(&mc13783_spi_mutex); | ||
373 | |||
374 | return i; | ||
375 | } | 329 | } |
376 | 330 | ||
377 | #if 0 /* Not needed right now */ | ||
378 | bool mc13783_read_async(struct spi_transfer_desc *xfer, | 331 | bool mc13783_read_async(struct spi_transfer_desc *xfer, |
379 | const unsigned char *regs, uint32_t *buffer, | 332 | const unsigned char *regs, uint32_t *buffer, |
380 | int count, spi_transfer_cb_fn_type callback) | 333 | int count, spi_transfer_cb_fn_type callback) |
381 | { | 334 | { |
382 | int i; | 335 | for (int i = 0; i < count; i++) |
383 | |||
384 | for (i = 0; i < count; i++) | ||
385 | { | 336 | { |
386 | unsigned reg = regs[i]; | 337 | unsigned reg = regs[i]; |
387 | 338 | ||
@@ -391,24 +342,14 @@ bool mc13783_read_async(struct spi_transfer_desc *xfer, | |||
391 | buffer[i] = reg << 25; | 342 | buffer[i] = reg << 25; |
392 | } | 343 | } |
393 | 344 | ||
394 | xfer->node = &mc13783_spi; | 345 | return mc13783_transfer(xfer, buffer, buffer, count, callback); |
395 | xfer->txbuf = buffer; | ||
396 | xfer->rxbuf = buffer; | ||
397 | xfer->count = count; | ||
398 | xfer->callback = callback; | ||
399 | xfer->next = NULL; | ||
400 | |||
401 | return spi_transfer(xfer); | ||
402 | } | 346 | } |
403 | #endif | ||
404 | 347 | ||
405 | bool mc13783_write_async(struct spi_transfer_desc *xfer, | 348 | bool mc13783_write_async(struct spi_transfer_desc *xfer, |
406 | const unsigned char *regs, uint32_t *buffer, | 349 | const unsigned char *regs, uint32_t *buffer, |
407 | int count, spi_transfer_cb_fn_type callback) | 350 | int count, spi_transfer_cb_fn_type callback) |
408 | { | 351 | { |
409 | int i; | 352 | for (int i = 0; i < count; i++) |
410 | |||
411 | for (i = 0; i < count; i++) | ||
412 | { | 353 | { |
413 | unsigned reg = regs[i]; | 354 | unsigned reg = regs[i]; |
414 | 355 | ||
@@ -418,12 +359,5 @@ bool mc13783_write_async(struct spi_transfer_desc *xfer, | |||
418 | buffer[i] = (1 << 31) | (reg << 25) | (buffer[i] & 0xffffff); | 359 | buffer[i] = (1 << 31) | (reg << 25) | (buffer[i] & 0xffffff); |
419 | } | 360 | } |
420 | 361 | ||
421 | xfer->node = &mc13783_spi; | 362 | return mc13783_transfer(xfer, buffer, NULL, count, callback); |
422 | xfer->txbuf = buffer; | ||
423 | xfer->rxbuf = NULL; | ||
424 | xfer->count = count; | ||
425 | xfer->callback = callback; | ||
426 | xfer->next = NULL; | ||
427 | |||
428 | return spi_transfer(xfer); | ||
429 | } | 363 | } |