Commit 6361cdb630afa036dcff8586735c38d1aca55a7c

Authored by pbrook
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,
... ...