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,9 +36,14 @@ typedef struct {
36 uint32_t datacnt; 36 uint32_t datacnt;
37 uint32_t status; 37 uint32_t status;
38 uint32_t mask[2]; 38 uint32_t mask[2];
39 - uint32_t fifocnt;  
40 int fifo_pos; 39 int fifo_pos;
41 int fifo_len; 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 uint32_t fifo[PL181_FIFO_LEN]; 47 uint32_t fifo[PL181_FIFO_LEN];
43 qemu_irq irq[2]; 48 qemu_irq irq[2];
44 } pl181_state; 49 } pl181_state;
@@ -182,7 +187,8 @@ static void pl181_fifo_run(pl181_state *s) @@ -182,7 +187,8 @@ static void pl181_fifo_run(pl181_state *s)
182 int is_read; 187 int is_read;
183 188
184 is_read = (s->datactrl & PL181_DATA_DIRECTION) != 0; 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 limit = is_read ? PL181_FIFO_LEN : 0; 192 limit = is_read ? PL181_FIFO_LEN : 0;
187 n = 0; 193 n = 0;
188 value = 0; 194 value = 0;
@@ -217,7 +223,7 @@ static void pl181_fifo_run(pl181_state *s) @@ -217,7 +223,7 @@ static void pl181_fifo_run(pl181_state *s)
217 s->status |= PL181_STATUS_DATABLOCKEND; 223 s->status |= PL181_STATUS_DATABLOCKEND;
218 DPRINTF("Transfer Complete\n"); 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 s->datactrl &= ~PL181_DATA_ENABLE; 227 s->datactrl &= ~PL181_DATA_ENABLE;
222 DPRINTF("Data engine idle\n"); 228 DPRINTF("Data engine idle\n");
223 } else { 229 } else {
@@ -252,6 +258,7 @@ static void pl181_fifo_run(pl181_state *s) @@ -252,6 +258,7 @@ static void pl181_fifo_run(pl181_state *s)
252 static uint32_t pl181_read(void *opaque, target_phys_addr_t offset) 258 static uint32_t pl181_read(void *opaque, target_phys_addr_t offset)
253 { 259 {
254 pl181_state *s = (pl181_state *)opaque; 260 pl181_state *s = (pl181_state *)opaque;
  261 + uint32_t tmp;
255 262
256 offset -= s->base; 263 offset -= s->base;
257 if (offset >= 0xfe0 && offset < 0x1000) { 264 if (offset >= 0xfe0 && offset < 0x1000) {
@@ -285,24 +292,42 @@ static uint32_t pl181_read(void *opaque, target_phys_addr_t offset) @@ -285,24 +292,42 @@ static uint32_t pl181_read(void *opaque, target_phys_addr_t offset)
285 case 0x30: /* DataCnt */ 292 case 0x30: /* DataCnt */
286 return s->datacnt; 293 return s->datacnt;
287 case 0x34: /* Status */ 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 case 0x3c: /* Mask0 */ 302 case 0x3c: /* Mask0 */
290 return s->mask[0]; 303 return s->mask[0];
291 case 0x40: /* Mask1 */ 304 case 0x40: /* Mask1 */
292 return s->mask[1]; 305 return s->mask[1];
293 case 0x48: /* FifoCnt */ 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 case 0x80: case 0x84: case 0x88: case 0x8c: /* FifoData */ 320 case 0x80: case 0x84: case 0x88: case 0x8c: /* FifoData */
296 case 0x90: case 0x94: case 0x98: case 0x9c: 321 case 0x90: case 0x94: case 0x98: case 0x9c:
297 case 0xa0: case 0xa4: case 0xa8: case 0xac: 322 case 0xa0: case 0xa4: case 0xa8: case 0xac:
298 case 0xb0: case 0xb4: case 0xb8: case 0xbc: 323 case 0xb0: case 0xb4: case 0xb8: case 0xbc:
299 - if (s->fifocnt == 0) { 324 + if (s->fifo_len == 0) {
300 fprintf(stderr, "pl181: Unexpected FIFO read\n"); 325 fprintf(stderr, "pl181: Unexpected FIFO read\n");
301 return 0; 326 return 0;
302 } else { 327 } else {
303 uint32_t value; 328 uint32_t value;
304 - s->fifocnt--;  
305 value = pl181_fifo_pop(s); 329 value = pl181_fifo_pop(s);
  330 + s->linux_hack = 1;
306 pl181_fifo_run(s); 331 pl181_fifo_run(s);
307 pl181_update(s); 332 pl181_update(s);
308 return value; 333 return value;
@@ -356,7 +381,6 @@ static void pl181_write(void *opaque, target_phys_addr_t offset, @@ -356,7 +381,6 @@ static void pl181_write(void *opaque, target_phys_addr_t offset,
356 s->datactrl = value & 0xff; 381 s->datactrl = value & 0xff;
357 if (value & PL181_DATA_ENABLE) { 382 if (value & PL181_DATA_ENABLE) {
358 s->datacnt = s->datalength; 383 s->datacnt = s->datalength;
359 - s->fifocnt = (s->datalength + 3) >> 2;  
360 pl181_fifo_run(s); 384 pl181_fifo_run(s);
361 } 385 }
362 break; 386 break;
@@ -373,10 +397,9 @@ static void pl181_write(void *opaque, target_phys_addr_t offset, @@ -373,10 +397,9 @@ static void pl181_write(void *opaque, target_phys_addr_t offset,
373 case 0x90: case 0x94: case 0x98: case 0x9c: 397 case 0x90: case 0x94: case 0x98: case 0x9c:
374 case 0xa0: case 0xa4: case 0xa8: case 0xac: 398 case 0xa0: case 0xa4: case 0xa8: case 0xac:
375 case 0xb0: case 0xb4: case 0xb8: case 0xbc: 399 case 0xb0: case 0xb4: case 0xb8: case 0xbc:
376 - if (s->fifocnt == 0) { 400 + if (s->datacnt == 0) {
377 fprintf(stderr, "pl181: Unexpected FIFO write\n"); 401 fprintf(stderr, "pl181: Unexpected FIFO write\n");
378 } else { 402 } else {
379 - s->fifocnt--;  
380 pl181_fifo_push(s, value); 403 pl181_fifo_push(s, value);
381 pl181_fifo_run(s); 404 pl181_fifo_run(s);
382 } 405 }
@@ -418,9 +441,9 @@ static void pl181_reset(void *opaque) @@ -418,9 +441,9 @@ static void pl181_reset(void *opaque)
418 s->datactrl = 0; 441 s->datactrl = 0;
419 s->datacnt = 0; 442 s->datacnt = 0;
420 s->status = 0; 443 s->status = 0;
  444 + s->linux_hack = 0;
421 s->mask[0] = 0; 445 s->mask[0] = 0;
422 s->mask[1] = 0; 446 s->mask[1] = 0;
423 - s->fifocnt = 0;  
424 } 447 }
425 448
426 void pl181_init(uint32_t base, BlockDriverState *bd, 449 void pl181_init(uint32_t base, BlockDriverState *bd,