X-Git-Url: https://vcs.maemo.org/git/?a=blobdiff_plain;f=hw%2Fpl181.c;h=a905cbb2100c4a2adf9e7c5033473c2c32841eca;hb=cd346349b45ef056f138a184f660b8c34c3213cc;hp=7912c280b544285d2eb4a68bd6eeb4cfc9cbbcf4;hpb=a1bb27b1e98a372545f37a599c0f9ea785502554;p=qemu diff --git a/hw/pl181.c b/hw/pl181.c index 7912c28..a905cbb 100644 --- a/hw/pl181.c +++ b/hw/pl181.c @@ -1,4 +1,4 @@ -/* +/* * Arm PrimeCell PL181 MultiMedia Card Interface * * Copyright (c) 2007 CodeSourcery. @@ -22,7 +22,7 @@ do { printf("pl181: " fmt , ##args); } while (0) #define PL181_FIFO_LEN 16 typedef struct { - struct sd_state_s *card; + SDState *card; uint32_t base; uint32_t clock; uint32_t power; @@ -36,12 +36,16 @@ typedef struct { uint32_t datacnt; uint32_t status; uint32_t mask[2]; - uint32_t fifocnt; int fifo_pos; int fifo_len; + /* The linux 2.6.21 driver is buggy, and misbehaves if new data arrives + while it is reading the FIFO. We hack around this be defering + subsequent transfers until after the driver polls the status word. + http://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=4446/1 + */ + int linux_hack; uint32_t fifo[PL181_FIFO_LEN]; - void *pic; - int irq[2]; + qemu_irq irq[2]; } pl181_state; #define PL181_CMD_INDEX 0x3f @@ -96,7 +100,7 @@ static void pl181_update(pl181_state *s) { int i; for (i = 0; i < 2; i++) { - pic_set_irq_new(s->pic, s->irq[i], (s->status & s->mask[i]) != 0); + qemu_set_irq(s->irq[i], (s->status & s->mask[i]) != 0); } } @@ -156,7 +160,7 @@ static void pl181_send_command(pl181_state *s) s->response[2] = RWORD(8); s->response[3] = RWORD(12) & ~1; } - DPRINTF("Response recieved\n"); + DPRINTF("Response received\n"); s->status |= PL181_STATUS_CMDRESPEND; #undef RWORD } else { @@ -170,10 +174,10 @@ error: s->status |= PL181_STATUS_CMDTIMEOUT; } -/* Transfer data between teh card and the FIFO. This is complicated by +/* Transfer data between the card and the FIFO. This is complicated by the FIFO holding 32-bit words and the card taking data in single byte chunks. FIFO bytes are transferred in little-endian order. */ - + static void pl181_fifo_run(pl181_state *s) { uint32_t bits; @@ -183,7 +187,8 @@ static void pl181_fifo_run(pl181_state *s) int is_read; is_read = (s->datactrl & PL181_DATA_DIRECTION) != 0; - if (s->datacnt != 0 && (!is_read || sd_data_ready(s->card))) { + if (s->datacnt != 0 && (!is_read || sd_data_ready(s->card)) + && !s->linux_hack) { limit = is_read ? PL181_FIFO_LEN : 0; n = 0; value = 0; @@ -218,7 +223,7 @@ static void pl181_fifo_run(pl181_state *s) s->status |= PL181_STATUS_DATABLOCKEND; DPRINTF("Transfer Complete\n"); } - if (s->datacnt == 0 && s->fifocnt == 0) { + if (s->datacnt == 0 && s->fifo_len == 0) { s->datactrl &= ~PL181_DATA_ENABLE; DPRINTF("Data engine idle\n"); } else { @@ -253,6 +258,7 @@ static void pl181_fifo_run(pl181_state *s) static uint32_t pl181_read(void *opaque, target_phys_addr_t offset) { pl181_state *s = (pl181_state *)opaque; + uint32_t tmp; offset -= s->base; if (offset >= 0xfe0 && offset < 0x1000) { @@ -286,24 +292,42 @@ static uint32_t pl181_read(void *opaque, target_phys_addr_t offset) case 0x30: /* DataCnt */ return s->datacnt; case 0x34: /* Status */ - return s->status; + tmp = s->status; + if (s->linux_hack) { + s->linux_hack = 0; + pl181_fifo_run(s); + pl181_update(s); + } + return tmp; case 0x3c: /* Mask0 */ return s->mask[0]; case 0x40: /* Mask1 */ return s->mask[1]; case 0x48: /* FifoCnt */ - return s->fifocnt; + /* The documentation is somewhat vague about exactly what FifoCnt + does. On real hardware it appears to be when decrememnted + when a word is transfered between the FIFO and the serial + data engine. DataCnt is decremented after each byte is + transfered between the serial engine and the card. + We don't emulate this level of detail, so both can be the same. */ + tmp = (s->datacnt + 3) >> 2; + if (s->linux_hack) { + s->linux_hack = 0; + pl181_fifo_run(s); + pl181_update(s); + } + return tmp; case 0x80: case 0x84: case 0x88: case 0x8c: /* FifoData */ case 0x90: case 0x94: case 0x98: case 0x9c: case 0xa0: case 0xa4: case 0xa8: case 0xac: case 0xb0: case 0xb4: case 0xb8: case 0xbc: - if (s->fifocnt == 0) { + if (s->fifo_len == 0) { fprintf(stderr, "pl181: Unexpected FIFO read\n"); return 0; } else { uint32_t value; - s->fifocnt--; value = pl181_fifo_pop(s); + s->linux_hack = 1; pl181_fifo_run(s); pl181_update(s); return value; @@ -357,7 +381,6 @@ static void pl181_write(void *opaque, target_phys_addr_t offset, s->datactrl = value & 0xff; if (value & PL181_DATA_ENABLE) { s->datacnt = s->datalength; - s->fifocnt = (s->datalength + 3) >> 2; pl181_fifo_run(s); } break; @@ -374,10 +397,9 @@ static void pl181_write(void *opaque, target_phys_addr_t offset, case 0x90: case 0x94: case 0x98: case 0x9c: case 0xa0: case 0xa4: case 0xa8: case 0xac: case 0xb0: case 0xb4: case 0xb8: case 0xbc: - if (s->fifocnt == 0) { + if (s->datacnt == 0) { fprintf(stderr, "pl181: Unexpected FIFO write\n"); } else { - s->fifocnt--; pl181_fifo_push(s, value); pl181_fifo_run(s); } @@ -419,13 +441,13 @@ static void pl181_reset(void *opaque) s->datactrl = 0; s->datacnt = 0; s->status = 0; + s->linux_hack = 0; s->mask[0] = 0; s->mask[1] = 0; - s->fifocnt = 0; } void pl181_init(uint32_t base, BlockDriverState *bd, - void *pic, int irq0, int irq1) + qemu_irq irq0, qemu_irq irq1) { int iomemtype; pl181_state *s; @@ -433,10 +455,9 @@ void pl181_init(uint32_t base, BlockDriverState *bd, s = (pl181_state *)qemu_mallocz(sizeof(pl181_state)); iomemtype = cpu_register_io_memory(0, pl181_readfn, pl181_writefn, s); - cpu_register_physical_memory(base, 0x00000fff, iomemtype); + cpu_register_physical_memory(base, 0x00001000, iomemtype); s->base = base; s->card = sd_init(bd); - s->pic = pic; s->irq[0] = irq0; s->irq[1] = irq1; qemu_register_reset(pl181_reset, s);