Commit 6361cdb630afa036dcff8586735c38d1aca55a7c
1 parent
70cf0b63
ARM PL181 MMCI fixes.
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2964 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
1 changed file
with
34 additions
and
11 deletions
hw/pl181.c
... | ... | @@ -36,9 +36,14 @@ typedef struct { |
36 | 36 | uint32_t datacnt; |
37 | 37 | uint32_t status; |
38 | 38 | uint32_t mask[2]; |
39 | - uint32_t fifocnt; | |
40 | 39 | int fifo_pos; |
41 | 40 | int fifo_len; |
41 | + /* The linux 2.6.21 driver is buggy, and misbehaves if new data arrives | |
42 | + while it is reading the FIFO. We hack around this be defering | |
43 | + subsequent transfers until after the driver polls the status word. | |
44 | + http://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=4446/1 | |
45 | + */ | |
46 | + int linux_hack; | |
42 | 47 | uint32_t fifo[PL181_FIFO_LEN]; |
43 | 48 | qemu_irq irq[2]; |
44 | 49 | } pl181_state; |
... | ... | @@ -182,7 +187,8 @@ static void pl181_fifo_run(pl181_state *s) |
182 | 187 | int is_read; |
183 | 188 | |
184 | 189 | is_read = (s->datactrl & PL181_DATA_DIRECTION) != 0; |
185 | - if (s->datacnt != 0 && (!is_read || sd_data_ready(s->card))) { | |
190 | + if (s->datacnt != 0 && (!is_read || sd_data_ready(s->card)) | |
191 | + && !s->linux_hack) { | |
186 | 192 | limit = is_read ? PL181_FIFO_LEN : 0; |
187 | 193 | n = 0; |
188 | 194 | value = 0; |
... | ... | @@ -217,7 +223,7 @@ static void pl181_fifo_run(pl181_state *s) |
217 | 223 | s->status |= PL181_STATUS_DATABLOCKEND; |
218 | 224 | DPRINTF("Transfer Complete\n"); |
219 | 225 | } |
220 | - if (s->datacnt == 0 && s->fifocnt == 0) { | |
226 | + if (s->datacnt == 0 && s->fifo_len == 0) { | |
221 | 227 | s->datactrl &= ~PL181_DATA_ENABLE; |
222 | 228 | DPRINTF("Data engine idle\n"); |
223 | 229 | } else { |
... | ... | @@ -252,6 +258,7 @@ static void pl181_fifo_run(pl181_state *s) |
252 | 258 | static uint32_t pl181_read(void *opaque, target_phys_addr_t offset) |
253 | 259 | { |
254 | 260 | pl181_state *s = (pl181_state *)opaque; |
261 | + uint32_t tmp; | |
255 | 262 | |
256 | 263 | offset -= s->base; |
257 | 264 | if (offset >= 0xfe0 && offset < 0x1000) { |
... | ... | @@ -285,24 +292,42 @@ static uint32_t pl181_read(void *opaque, target_phys_addr_t offset) |
285 | 292 | case 0x30: /* DataCnt */ |
286 | 293 | return s->datacnt; |
287 | 294 | case 0x34: /* Status */ |
288 | - return s->status; | |
295 | + tmp = s->status; | |
296 | + if (s->linux_hack) { | |
297 | + s->linux_hack = 0; | |
298 | + pl181_fifo_run(s); | |
299 | + pl181_update(s); | |
300 | + } | |
301 | + return tmp; | |
289 | 302 | case 0x3c: /* Mask0 */ |
290 | 303 | return s->mask[0]; |
291 | 304 | case 0x40: /* Mask1 */ |
292 | 305 | return s->mask[1]; |
293 | 306 | case 0x48: /* FifoCnt */ |
294 | - return s->fifocnt; | |
307 | + /* The documentation is somewhat vague about exactly what FifoCnt | |
308 | + does. On real hardware it appears to be when decrememnted | |
309 | + when a word is transfered between the FIFO and the serial | |
310 | + data engine. DataCnt is decremented after each byte is | |
311 | + transfered between the serial engine and the card. | |
312 | + We don't emulate this level of detail, so both can be the same. */ | |
313 | + tmp = (s->datacnt + 3) >> 2; | |
314 | + if (s->linux_hack) { | |
315 | + s->linux_hack = 0; | |
316 | + pl181_fifo_run(s); | |
317 | + pl181_update(s); | |
318 | + } | |
319 | + return tmp; | |
295 | 320 | case 0x80: case 0x84: case 0x88: case 0x8c: /* FifoData */ |
296 | 321 | case 0x90: case 0x94: case 0x98: case 0x9c: |
297 | 322 | case 0xa0: case 0xa4: case 0xa8: case 0xac: |
298 | 323 | case 0xb0: case 0xb4: case 0xb8: case 0xbc: |
299 | - if (s->fifocnt == 0) { | |
324 | + if (s->fifo_len == 0) { | |
300 | 325 | fprintf(stderr, "pl181: Unexpected FIFO read\n"); |
301 | 326 | return 0; |
302 | 327 | } else { |
303 | 328 | uint32_t value; |
304 | - s->fifocnt--; | |
305 | 329 | value = pl181_fifo_pop(s); |
330 | + s->linux_hack = 1; | |
306 | 331 | pl181_fifo_run(s); |
307 | 332 | pl181_update(s); |
308 | 333 | return value; |
... | ... | @@ -356,7 +381,6 @@ static void pl181_write(void *opaque, target_phys_addr_t offset, |
356 | 381 | s->datactrl = value & 0xff; |
357 | 382 | if (value & PL181_DATA_ENABLE) { |
358 | 383 | s->datacnt = s->datalength; |
359 | - s->fifocnt = (s->datalength + 3) >> 2; | |
360 | 384 | pl181_fifo_run(s); |
361 | 385 | } |
362 | 386 | break; |
... | ... | @@ -373,10 +397,9 @@ static void pl181_write(void *opaque, target_phys_addr_t offset, |
373 | 397 | case 0x90: case 0x94: case 0x98: case 0x9c: |
374 | 398 | case 0xa0: case 0xa4: case 0xa8: case 0xac: |
375 | 399 | case 0xb0: case 0xb4: case 0xb8: case 0xbc: |
376 | - if (s->fifocnt == 0) { | |
400 | + if (s->datacnt == 0) { | |
377 | 401 | fprintf(stderr, "pl181: Unexpected FIFO write\n"); |
378 | 402 | } else { |
379 | - s->fifocnt--; | |
380 | 403 | pl181_fifo_push(s, value); |
381 | 404 | pl181_fifo_run(s); |
382 | 405 | } |
... | ... | @@ -418,9 +441,9 @@ static void pl181_reset(void *opaque) |
418 | 441 | s->datactrl = 0; |
419 | 442 | s->datacnt = 0; |
420 | 443 | s->status = 0; |
444 | + s->linux_hack = 0; | |
421 | 445 | s->mask[0] = 0; |
422 | 446 | s->mask[1] = 0; |
423 | - s->fifocnt = 0; | |
424 | 447 | } |
425 | 448 | |
426 | 449 | void pl181_init(uint32_t base, BlockDriverState *bd, | ... | ... |