149ab747fSPaolo Bonzini /* 249ab747fSPaolo Bonzini * Arm PrimeCell PL181 MultiMedia Card Interface 349ab747fSPaolo Bonzini * 449ab747fSPaolo Bonzini * Copyright (c) 2007 CodeSourcery. 549ab747fSPaolo Bonzini * Written by Paul Brook 649ab747fSPaolo Bonzini * 749ab747fSPaolo Bonzini * This code is licensed under the GPL. 849ab747fSPaolo Bonzini */ 949ab747fSPaolo Bonzini 10*8ef94f0bSPeter Maydell #include "qemu/osdep.h" 11fa1d36dfSMarkus Armbruster #include "sysemu/block-backend.h" 1249ab747fSPaolo Bonzini #include "sysemu/blockdev.h" 1349ab747fSPaolo Bonzini #include "hw/sysbus.h" 14e3382ef0SSai Pavan Boddu #include "hw/sd/sd.h" 1549ab747fSPaolo Bonzini 1649ab747fSPaolo Bonzini //#define DEBUG_PL181 1 1749ab747fSPaolo Bonzini 1849ab747fSPaolo Bonzini #ifdef DEBUG_PL181 1949ab747fSPaolo Bonzini #define DPRINTF(fmt, ...) \ 2049ab747fSPaolo Bonzini do { printf("pl181: " fmt , ## __VA_ARGS__); } while (0) 2149ab747fSPaolo Bonzini #else 2249ab747fSPaolo Bonzini #define DPRINTF(fmt, ...) do {} while(0) 2349ab747fSPaolo Bonzini #endif 2449ab747fSPaolo Bonzini 2549ab747fSPaolo Bonzini #define PL181_FIFO_LEN 16 2649ab747fSPaolo Bonzini 27630f4442SAndreas Färber #define TYPE_PL181 "pl181" 28630f4442SAndreas Färber #define PL181(obj) OBJECT_CHECK(PL181State, (obj), TYPE_PL181) 29630f4442SAndreas Färber 301d998d93SAndreas Färber typedef struct PL181State { 31630f4442SAndreas Färber SysBusDevice parent_obj; 32630f4442SAndreas Färber 3349ab747fSPaolo Bonzini MemoryRegion iomem; 3449ab747fSPaolo Bonzini SDState *card; 3549ab747fSPaolo Bonzini uint32_t clock; 3649ab747fSPaolo Bonzini uint32_t power; 3749ab747fSPaolo Bonzini uint32_t cmdarg; 3849ab747fSPaolo Bonzini uint32_t cmd; 3949ab747fSPaolo Bonzini uint32_t datatimer; 4049ab747fSPaolo Bonzini uint32_t datalength; 4149ab747fSPaolo Bonzini uint32_t respcmd; 4249ab747fSPaolo Bonzini uint32_t response[4]; 4349ab747fSPaolo Bonzini uint32_t datactrl; 4449ab747fSPaolo Bonzini uint32_t datacnt; 4549ab747fSPaolo Bonzini uint32_t status; 4649ab747fSPaolo Bonzini uint32_t mask[2]; 4749ab747fSPaolo Bonzini int32_t fifo_pos; 4849ab747fSPaolo Bonzini int32_t fifo_len; 4949ab747fSPaolo Bonzini /* The linux 2.6.21 driver is buggy, and misbehaves if new data arrives 5067cc32ebSVeres Lajos while it is reading the FIFO. We hack around this by deferring 5149ab747fSPaolo Bonzini subsequent transfers until after the driver polls the status word. 5249ab747fSPaolo Bonzini http://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=4446/1 5349ab747fSPaolo Bonzini */ 5449ab747fSPaolo Bonzini int32_t linux_hack; 5549ab747fSPaolo Bonzini uint32_t fifo[PL181_FIFO_LEN]; 5649ab747fSPaolo Bonzini qemu_irq irq[2]; 5749ab747fSPaolo Bonzini /* GPIO outputs for 'card is readonly' and 'card inserted' */ 5849ab747fSPaolo Bonzini qemu_irq cardstatus[2]; 591d998d93SAndreas Färber } PL181State; 6049ab747fSPaolo Bonzini 6149ab747fSPaolo Bonzini static const VMStateDescription vmstate_pl181 = { 6249ab747fSPaolo Bonzini .name = "pl181", 6349ab747fSPaolo Bonzini .version_id = 1, 6449ab747fSPaolo Bonzini .minimum_version_id = 1, 6549ab747fSPaolo Bonzini .fields = (VMStateField[]) { 661d998d93SAndreas Färber VMSTATE_UINT32(clock, PL181State), 671d998d93SAndreas Färber VMSTATE_UINT32(power, PL181State), 681d998d93SAndreas Färber VMSTATE_UINT32(cmdarg, PL181State), 691d998d93SAndreas Färber VMSTATE_UINT32(cmd, PL181State), 701d998d93SAndreas Färber VMSTATE_UINT32(datatimer, PL181State), 711d998d93SAndreas Färber VMSTATE_UINT32(datalength, PL181State), 721d998d93SAndreas Färber VMSTATE_UINT32(respcmd, PL181State), 731d998d93SAndreas Färber VMSTATE_UINT32_ARRAY(response, PL181State, 4), 741d998d93SAndreas Färber VMSTATE_UINT32(datactrl, PL181State), 751d998d93SAndreas Färber VMSTATE_UINT32(datacnt, PL181State), 761d998d93SAndreas Färber VMSTATE_UINT32(status, PL181State), 771d998d93SAndreas Färber VMSTATE_UINT32_ARRAY(mask, PL181State, 2), 781d998d93SAndreas Färber VMSTATE_INT32(fifo_pos, PL181State), 791d998d93SAndreas Färber VMSTATE_INT32(fifo_len, PL181State), 801d998d93SAndreas Färber VMSTATE_INT32(linux_hack, PL181State), 811d998d93SAndreas Färber VMSTATE_UINT32_ARRAY(fifo, PL181State, PL181_FIFO_LEN), 8249ab747fSPaolo Bonzini VMSTATE_END_OF_LIST() 8349ab747fSPaolo Bonzini } 8449ab747fSPaolo Bonzini }; 8549ab747fSPaolo Bonzini 8649ab747fSPaolo Bonzini #define PL181_CMD_INDEX 0x3f 8749ab747fSPaolo Bonzini #define PL181_CMD_RESPONSE (1 << 6) 8849ab747fSPaolo Bonzini #define PL181_CMD_LONGRESP (1 << 7) 8949ab747fSPaolo Bonzini #define PL181_CMD_INTERRUPT (1 << 8) 9049ab747fSPaolo Bonzini #define PL181_CMD_PENDING (1 << 9) 9149ab747fSPaolo Bonzini #define PL181_CMD_ENABLE (1 << 10) 9249ab747fSPaolo Bonzini 9349ab747fSPaolo Bonzini #define PL181_DATA_ENABLE (1 << 0) 9449ab747fSPaolo Bonzini #define PL181_DATA_DIRECTION (1 << 1) 9549ab747fSPaolo Bonzini #define PL181_DATA_MODE (1 << 2) 9649ab747fSPaolo Bonzini #define PL181_DATA_DMAENABLE (1 << 3) 9749ab747fSPaolo Bonzini 9849ab747fSPaolo Bonzini #define PL181_STATUS_CMDCRCFAIL (1 << 0) 9949ab747fSPaolo Bonzini #define PL181_STATUS_DATACRCFAIL (1 << 1) 10049ab747fSPaolo Bonzini #define PL181_STATUS_CMDTIMEOUT (1 << 2) 10149ab747fSPaolo Bonzini #define PL181_STATUS_DATATIMEOUT (1 << 3) 10249ab747fSPaolo Bonzini #define PL181_STATUS_TXUNDERRUN (1 << 4) 10349ab747fSPaolo Bonzini #define PL181_STATUS_RXOVERRUN (1 << 5) 10449ab747fSPaolo Bonzini #define PL181_STATUS_CMDRESPEND (1 << 6) 10549ab747fSPaolo Bonzini #define PL181_STATUS_CMDSENT (1 << 7) 10649ab747fSPaolo Bonzini #define PL181_STATUS_DATAEND (1 << 8) 10749ab747fSPaolo Bonzini #define PL181_STATUS_DATABLOCKEND (1 << 10) 10849ab747fSPaolo Bonzini #define PL181_STATUS_CMDACTIVE (1 << 11) 10949ab747fSPaolo Bonzini #define PL181_STATUS_TXACTIVE (1 << 12) 11049ab747fSPaolo Bonzini #define PL181_STATUS_RXACTIVE (1 << 13) 11149ab747fSPaolo Bonzini #define PL181_STATUS_TXFIFOHALFEMPTY (1 << 14) 11249ab747fSPaolo Bonzini #define PL181_STATUS_RXFIFOHALFFULL (1 << 15) 11349ab747fSPaolo Bonzini #define PL181_STATUS_TXFIFOFULL (1 << 16) 11449ab747fSPaolo Bonzini #define PL181_STATUS_RXFIFOFULL (1 << 17) 11549ab747fSPaolo Bonzini #define PL181_STATUS_TXFIFOEMPTY (1 << 18) 11649ab747fSPaolo Bonzini #define PL181_STATUS_RXFIFOEMPTY (1 << 19) 11749ab747fSPaolo Bonzini #define PL181_STATUS_TXDATAAVLBL (1 << 20) 11849ab747fSPaolo Bonzini #define PL181_STATUS_RXDATAAVLBL (1 << 21) 11949ab747fSPaolo Bonzini 12049ab747fSPaolo Bonzini #define PL181_STATUS_TX_FIFO (PL181_STATUS_TXACTIVE \ 12149ab747fSPaolo Bonzini |PL181_STATUS_TXFIFOHALFEMPTY \ 12249ab747fSPaolo Bonzini |PL181_STATUS_TXFIFOFULL \ 12349ab747fSPaolo Bonzini |PL181_STATUS_TXFIFOEMPTY \ 12449ab747fSPaolo Bonzini |PL181_STATUS_TXDATAAVLBL) 12549ab747fSPaolo Bonzini #define PL181_STATUS_RX_FIFO (PL181_STATUS_RXACTIVE \ 12649ab747fSPaolo Bonzini |PL181_STATUS_RXFIFOHALFFULL \ 12749ab747fSPaolo Bonzini |PL181_STATUS_RXFIFOFULL \ 12849ab747fSPaolo Bonzini |PL181_STATUS_RXFIFOEMPTY \ 12949ab747fSPaolo Bonzini |PL181_STATUS_RXDATAAVLBL) 13049ab747fSPaolo Bonzini 13149ab747fSPaolo Bonzini static const unsigned char pl181_id[] = 13249ab747fSPaolo Bonzini { 0x81, 0x11, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; 13349ab747fSPaolo Bonzini 1341d998d93SAndreas Färber static void pl181_update(PL181State *s) 13549ab747fSPaolo Bonzini { 13649ab747fSPaolo Bonzini int i; 13749ab747fSPaolo Bonzini for (i = 0; i < 2; i++) { 13849ab747fSPaolo Bonzini qemu_set_irq(s->irq[i], (s->status & s->mask[i]) != 0); 13949ab747fSPaolo Bonzini } 14049ab747fSPaolo Bonzini } 14149ab747fSPaolo Bonzini 1421d998d93SAndreas Färber static void pl181_fifo_push(PL181State *s, uint32_t value) 14349ab747fSPaolo Bonzini { 14449ab747fSPaolo Bonzini int n; 14549ab747fSPaolo Bonzini 14649ab747fSPaolo Bonzini if (s->fifo_len == PL181_FIFO_LEN) { 14749ab747fSPaolo Bonzini fprintf(stderr, "pl181: FIFO overflow\n"); 14849ab747fSPaolo Bonzini return; 14949ab747fSPaolo Bonzini } 15049ab747fSPaolo Bonzini n = (s->fifo_pos + s->fifo_len) & (PL181_FIFO_LEN - 1); 15149ab747fSPaolo Bonzini s->fifo_len++; 15249ab747fSPaolo Bonzini s->fifo[n] = value; 15349ab747fSPaolo Bonzini DPRINTF("FIFO push %08x\n", (int)value); 15449ab747fSPaolo Bonzini } 15549ab747fSPaolo Bonzini 1561d998d93SAndreas Färber static uint32_t pl181_fifo_pop(PL181State *s) 15749ab747fSPaolo Bonzini { 15849ab747fSPaolo Bonzini uint32_t value; 15949ab747fSPaolo Bonzini 16049ab747fSPaolo Bonzini if (s->fifo_len == 0) { 16149ab747fSPaolo Bonzini fprintf(stderr, "pl181: FIFO underflow\n"); 16249ab747fSPaolo Bonzini return 0; 16349ab747fSPaolo Bonzini } 16449ab747fSPaolo Bonzini value = s->fifo[s->fifo_pos]; 16549ab747fSPaolo Bonzini s->fifo_len--; 16649ab747fSPaolo Bonzini s->fifo_pos = (s->fifo_pos + 1) & (PL181_FIFO_LEN - 1); 16749ab747fSPaolo Bonzini DPRINTF("FIFO pop %08x\n", (int)value); 16849ab747fSPaolo Bonzini return value; 16949ab747fSPaolo Bonzini } 17049ab747fSPaolo Bonzini 1711d998d93SAndreas Färber static void pl181_send_command(PL181State *s) 17249ab747fSPaolo Bonzini { 17349ab747fSPaolo Bonzini SDRequest request; 17449ab747fSPaolo Bonzini uint8_t response[16]; 17549ab747fSPaolo Bonzini int rlen; 17649ab747fSPaolo Bonzini 17749ab747fSPaolo Bonzini request.cmd = s->cmd & PL181_CMD_INDEX; 17849ab747fSPaolo Bonzini request.arg = s->cmdarg; 17949ab747fSPaolo Bonzini DPRINTF("Command %d %08x\n", request.cmd, request.arg); 18049ab747fSPaolo Bonzini rlen = sd_do_command(s->card, &request, response); 18149ab747fSPaolo Bonzini if (rlen < 0) 18249ab747fSPaolo Bonzini goto error; 18349ab747fSPaolo Bonzini if (s->cmd & PL181_CMD_RESPONSE) { 1848827b0fbSPeter Maydell #define RWORD(n) (((uint32_t)response[n] << 24) | (response[n + 1] << 16) \ 18549ab747fSPaolo Bonzini | (response[n + 2] << 8) | response[n + 3]) 18649ab747fSPaolo Bonzini if (rlen == 0 || (rlen == 4 && (s->cmd & PL181_CMD_LONGRESP))) 18749ab747fSPaolo Bonzini goto error; 18849ab747fSPaolo Bonzini if (rlen != 4 && rlen != 16) 18949ab747fSPaolo Bonzini goto error; 19049ab747fSPaolo Bonzini s->response[0] = RWORD(0); 19149ab747fSPaolo Bonzini if (rlen == 4) { 19249ab747fSPaolo Bonzini s->response[1] = s->response[2] = s->response[3] = 0; 19349ab747fSPaolo Bonzini } else { 19449ab747fSPaolo Bonzini s->response[1] = RWORD(4); 19549ab747fSPaolo Bonzini s->response[2] = RWORD(8); 19649ab747fSPaolo Bonzini s->response[3] = RWORD(12) & ~1; 19749ab747fSPaolo Bonzini } 19849ab747fSPaolo Bonzini DPRINTF("Response received\n"); 19949ab747fSPaolo Bonzini s->status |= PL181_STATUS_CMDRESPEND; 20049ab747fSPaolo Bonzini #undef RWORD 20149ab747fSPaolo Bonzini } else { 20249ab747fSPaolo Bonzini DPRINTF("Command sent\n"); 20349ab747fSPaolo Bonzini s->status |= PL181_STATUS_CMDSENT; 20449ab747fSPaolo Bonzini } 20549ab747fSPaolo Bonzini return; 20649ab747fSPaolo Bonzini 20749ab747fSPaolo Bonzini error: 20849ab747fSPaolo Bonzini DPRINTF("Timeout\n"); 20949ab747fSPaolo Bonzini s->status |= PL181_STATUS_CMDTIMEOUT; 21049ab747fSPaolo Bonzini } 21149ab747fSPaolo Bonzini 21249ab747fSPaolo Bonzini /* Transfer data between the card and the FIFO. This is complicated by 21349ab747fSPaolo Bonzini the FIFO holding 32-bit words and the card taking data in single byte 21449ab747fSPaolo Bonzini chunks. FIFO bytes are transferred in little-endian order. */ 21549ab747fSPaolo Bonzini 2161d998d93SAndreas Färber static void pl181_fifo_run(PL181State *s) 21749ab747fSPaolo Bonzini { 21849ab747fSPaolo Bonzini uint32_t bits; 21949ab747fSPaolo Bonzini uint32_t value = 0; 22049ab747fSPaolo Bonzini int n; 22149ab747fSPaolo Bonzini int is_read; 22249ab747fSPaolo Bonzini 22349ab747fSPaolo Bonzini is_read = (s->datactrl & PL181_DATA_DIRECTION) != 0; 22449ab747fSPaolo Bonzini if (s->datacnt != 0 && (!is_read || sd_data_ready(s->card)) 22549ab747fSPaolo Bonzini && !s->linux_hack) { 22649ab747fSPaolo Bonzini if (is_read) { 22749ab747fSPaolo Bonzini n = 0; 22849ab747fSPaolo Bonzini while (s->datacnt && s->fifo_len < PL181_FIFO_LEN) { 22949ab747fSPaolo Bonzini value |= (uint32_t)sd_read_data(s->card) << (n * 8); 23049ab747fSPaolo Bonzini s->datacnt--; 23149ab747fSPaolo Bonzini n++; 23249ab747fSPaolo Bonzini if (n == 4) { 23349ab747fSPaolo Bonzini pl181_fifo_push(s, value); 23449ab747fSPaolo Bonzini n = 0; 23549ab747fSPaolo Bonzini value = 0; 23649ab747fSPaolo Bonzini } 23749ab747fSPaolo Bonzini } 23849ab747fSPaolo Bonzini if (n != 0) { 23949ab747fSPaolo Bonzini pl181_fifo_push(s, value); 24049ab747fSPaolo Bonzini } 24149ab747fSPaolo Bonzini } else { /* write */ 24249ab747fSPaolo Bonzini n = 0; 24349ab747fSPaolo Bonzini while (s->datacnt > 0 && (s->fifo_len > 0 || n > 0)) { 24449ab747fSPaolo Bonzini if (n == 0) { 24549ab747fSPaolo Bonzini value = pl181_fifo_pop(s); 24649ab747fSPaolo Bonzini n = 4; 24749ab747fSPaolo Bonzini } 24849ab747fSPaolo Bonzini n--; 24949ab747fSPaolo Bonzini s->datacnt--; 25049ab747fSPaolo Bonzini sd_write_data(s->card, value & 0xff); 25149ab747fSPaolo Bonzini value >>= 8; 25249ab747fSPaolo Bonzini } 25349ab747fSPaolo Bonzini } 25449ab747fSPaolo Bonzini } 25549ab747fSPaolo Bonzini s->status &= ~(PL181_STATUS_RX_FIFO | PL181_STATUS_TX_FIFO); 25649ab747fSPaolo Bonzini if (s->datacnt == 0) { 25749ab747fSPaolo Bonzini s->status |= PL181_STATUS_DATAEND; 25849ab747fSPaolo Bonzini /* HACK: */ 25949ab747fSPaolo Bonzini s->status |= PL181_STATUS_DATABLOCKEND; 26049ab747fSPaolo Bonzini DPRINTF("Transfer Complete\n"); 26149ab747fSPaolo Bonzini } 26249ab747fSPaolo Bonzini if (s->datacnt == 0 && s->fifo_len == 0) { 26349ab747fSPaolo Bonzini s->datactrl &= ~PL181_DATA_ENABLE; 26449ab747fSPaolo Bonzini DPRINTF("Data engine idle\n"); 26549ab747fSPaolo Bonzini } else { 26649ab747fSPaolo Bonzini /* Update FIFO bits. */ 26749ab747fSPaolo Bonzini bits = PL181_STATUS_TXACTIVE | PL181_STATUS_RXACTIVE; 26849ab747fSPaolo Bonzini if (s->fifo_len == 0) { 26949ab747fSPaolo Bonzini bits |= PL181_STATUS_TXFIFOEMPTY; 27049ab747fSPaolo Bonzini bits |= PL181_STATUS_RXFIFOEMPTY; 27149ab747fSPaolo Bonzini } else { 27249ab747fSPaolo Bonzini bits |= PL181_STATUS_TXDATAAVLBL; 27349ab747fSPaolo Bonzini bits |= PL181_STATUS_RXDATAAVLBL; 27449ab747fSPaolo Bonzini } 27549ab747fSPaolo Bonzini if (s->fifo_len == 16) { 27649ab747fSPaolo Bonzini bits |= PL181_STATUS_TXFIFOFULL; 27749ab747fSPaolo Bonzini bits |= PL181_STATUS_RXFIFOFULL; 27849ab747fSPaolo Bonzini } 27949ab747fSPaolo Bonzini if (s->fifo_len <= 8) { 28049ab747fSPaolo Bonzini bits |= PL181_STATUS_TXFIFOHALFEMPTY; 28149ab747fSPaolo Bonzini } 28249ab747fSPaolo Bonzini if (s->fifo_len >= 8) { 28349ab747fSPaolo Bonzini bits |= PL181_STATUS_RXFIFOHALFFULL; 28449ab747fSPaolo Bonzini } 28549ab747fSPaolo Bonzini if (s->datactrl & PL181_DATA_DIRECTION) { 28649ab747fSPaolo Bonzini bits &= PL181_STATUS_RX_FIFO; 28749ab747fSPaolo Bonzini } else { 28849ab747fSPaolo Bonzini bits &= PL181_STATUS_TX_FIFO; 28949ab747fSPaolo Bonzini } 29049ab747fSPaolo Bonzini s->status |= bits; 29149ab747fSPaolo Bonzini } 29249ab747fSPaolo Bonzini } 29349ab747fSPaolo Bonzini 29449ab747fSPaolo Bonzini static uint64_t pl181_read(void *opaque, hwaddr offset, 29549ab747fSPaolo Bonzini unsigned size) 29649ab747fSPaolo Bonzini { 2971d998d93SAndreas Färber PL181State *s = (PL181State *)opaque; 29849ab747fSPaolo Bonzini uint32_t tmp; 29949ab747fSPaolo Bonzini 30049ab747fSPaolo Bonzini if (offset >= 0xfe0 && offset < 0x1000) { 30149ab747fSPaolo Bonzini return pl181_id[(offset - 0xfe0) >> 2]; 30249ab747fSPaolo Bonzini } 30349ab747fSPaolo Bonzini switch (offset) { 30449ab747fSPaolo Bonzini case 0x00: /* Power */ 30549ab747fSPaolo Bonzini return s->power; 30649ab747fSPaolo Bonzini case 0x04: /* Clock */ 30749ab747fSPaolo Bonzini return s->clock; 30849ab747fSPaolo Bonzini case 0x08: /* Argument */ 30949ab747fSPaolo Bonzini return s->cmdarg; 31049ab747fSPaolo Bonzini case 0x0c: /* Command */ 31149ab747fSPaolo Bonzini return s->cmd; 31249ab747fSPaolo Bonzini case 0x10: /* RespCmd */ 31349ab747fSPaolo Bonzini return s->respcmd; 31449ab747fSPaolo Bonzini case 0x14: /* Response0 */ 31549ab747fSPaolo Bonzini return s->response[0]; 31649ab747fSPaolo Bonzini case 0x18: /* Response1 */ 31749ab747fSPaolo Bonzini return s->response[1]; 31849ab747fSPaolo Bonzini case 0x1c: /* Response2 */ 31949ab747fSPaolo Bonzini return s->response[2]; 32049ab747fSPaolo Bonzini case 0x20: /* Response3 */ 32149ab747fSPaolo Bonzini return s->response[3]; 32249ab747fSPaolo Bonzini case 0x24: /* DataTimer */ 32349ab747fSPaolo Bonzini return s->datatimer; 32449ab747fSPaolo Bonzini case 0x28: /* DataLength */ 32549ab747fSPaolo Bonzini return s->datalength; 32649ab747fSPaolo Bonzini case 0x2c: /* DataCtrl */ 32749ab747fSPaolo Bonzini return s->datactrl; 32849ab747fSPaolo Bonzini case 0x30: /* DataCnt */ 32949ab747fSPaolo Bonzini return s->datacnt; 33049ab747fSPaolo Bonzini case 0x34: /* Status */ 33149ab747fSPaolo Bonzini tmp = s->status; 33249ab747fSPaolo Bonzini if (s->linux_hack) { 33349ab747fSPaolo Bonzini s->linux_hack = 0; 33449ab747fSPaolo Bonzini pl181_fifo_run(s); 33549ab747fSPaolo Bonzini pl181_update(s); 33649ab747fSPaolo Bonzini } 33749ab747fSPaolo Bonzini return tmp; 33849ab747fSPaolo Bonzini case 0x3c: /* Mask0 */ 33949ab747fSPaolo Bonzini return s->mask[0]; 34049ab747fSPaolo Bonzini case 0x40: /* Mask1 */ 34149ab747fSPaolo Bonzini return s->mask[1]; 34249ab747fSPaolo Bonzini case 0x48: /* FifoCnt */ 34349ab747fSPaolo Bonzini /* The documentation is somewhat vague about exactly what FifoCnt 34449ab747fSPaolo Bonzini does. On real hardware it appears to be when decrememnted 34549ab747fSPaolo Bonzini when a word is transferred between the FIFO and the serial 34649ab747fSPaolo Bonzini data engine. DataCnt is decremented after each byte is 34749ab747fSPaolo Bonzini transferred between the serial engine and the card. 34849ab747fSPaolo Bonzini We don't emulate this level of detail, so both can be the same. */ 34949ab747fSPaolo Bonzini tmp = (s->datacnt + 3) >> 2; 35049ab747fSPaolo Bonzini if (s->linux_hack) { 35149ab747fSPaolo Bonzini s->linux_hack = 0; 35249ab747fSPaolo Bonzini pl181_fifo_run(s); 35349ab747fSPaolo Bonzini pl181_update(s); 35449ab747fSPaolo Bonzini } 35549ab747fSPaolo Bonzini return tmp; 35649ab747fSPaolo Bonzini case 0x80: case 0x84: case 0x88: case 0x8c: /* FifoData */ 35749ab747fSPaolo Bonzini case 0x90: case 0x94: case 0x98: case 0x9c: 35849ab747fSPaolo Bonzini case 0xa0: case 0xa4: case 0xa8: case 0xac: 35949ab747fSPaolo Bonzini case 0xb0: case 0xb4: case 0xb8: case 0xbc: 36049ab747fSPaolo Bonzini if (s->fifo_len == 0) { 36149ab747fSPaolo Bonzini qemu_log_mask(LOG_GUEST_ERROR, "pl181: Unexpected FIFO read\n"); 36249ab747fSPaolo Bonzini return 0; 36349ab747fSPaolo Bonzini } else { 36449ab747fSPaolo Bonzini uint32_t value; 36549ab747fSPaolo Bonzini value = pl181_fifo_pop(s); 36649ab747fSPaolo Bonzini s->linux_hack = 1; 36749ab747fSPaolo Bonzini pl181_fifo_run(s); 36849ab747fSPaolo Bonzini pl181_update(s); 36949ab747fSPaolo Bonzini return value; 37049ab747fSPaolo Bonzini } 37149ab747fSPaolo Bonzini default: 37249ab747fSPaolo Bonzini qemu_log_mask(LOG_GUEST_ERROR, 37349ab747fSPaolo Bonzini "pl181_read: Bad offset %x\n", (int)offset); 37449ab747fSPaolo Bonzini return 0; 37549ab747fSPaolo Bonzini } 37649ab747fSPaolo Bonzini } 37749ab747fSPaolo Bonzini 37849ab747fSPaolo Bonzini static void pl181_write(void *opaque, hwaddr offset, 37949ab747fSPaolo Bonzini uint64_t value, unsigned size) 38049ab747fSPaolo Bonzini { 3811d998d93SAndreas Färber PL181State *s = (PL181State *)opaque; 38249ab747fSPaolo Bonzini 38349ab747fSPaolo Bonzini switch (offset) { 38449ab747fSPaolo Bonzini case 0x00: /* Power */ 38549ab747fSPaolo Bonzini s->power = value & 0xff; 38649ab747fSPaolo Bonzini break; 38749ab747fSPaolo Bonzini case 0x04: /* Clock */ 38849ab747fSPaolo Bonzini s->clock = value & 0xff; 38949ab747fSPaolo Bonzini break; 39049ab747fSPaolo Bonzini case 0x08: /* Argument */ 39149ab747fSPaolo Bonzini s->cmdarg = value; 39249ab747fSPaolo Bonzini break; 39349ab747fSPaolo Bonzini case 0x0c: /* Command */ 39449ab747fSPaolo Bonzini s->cmd = value; 39549ab747fSPaolo Bonzini if (s->cmd & PL181_CMD_ENABLE) { 39649ab747fSPaolo Bonzini if (s->cmd & PL181_CMD_INTERRUPT) { 39749ab747fSPaolo Bonzini qemu_log_mask(LOG_UNIMP, 39849ab747fSPaolo Bonzini "pl181: Interrupt mode not implemented\n"); 39949ab747fSPaolo Bonzini } if (s->cmd & PL181_CMD_PENDING) { 40049ab747fSPaolo Bonzini qemu_log_mask(LOG_UNIMP, 40149ab747fSPaolo Bonzini "pl181: Pending commands not implemented\n"); 40249ab747fSPaolo Bonzini } else { 40349ab747fSPaolo Bonzini pl181_send_command(s); 40449ab747fSPaolo Bonzini pl181_fifo_run(s); 40549ab747fSPaolo Bonzini } 40649ab747fSPaolo Bonzini /* The command has completed one way or the other. */ 40749ab747fSPaolo Bonzini s->cmd &= ~PL181_CMD_ENABLE; 40849ab747fSPaolo Bonzini } 40949ab747fSPaolo Bonzini break; 41049ab747fSPaolo Bonzini case 0x24: /* DataTimer */ 41149ab747fSPaolo Bonzini s->datatimer = value; 41249ab747fSPaolo Bonzini break; 41349ab747fSPaolo Bonzini case 0x28: /* DataLength */ 41449ab747fSPaolo Bonzini s->datalength = value & 0xffff; 41549ab747fSPaolo Bonzini break; 41649ab747fSPaolo Bonzini case 0x2c: /* DataCtrl */ 41749ab747fSPaolo Bonzini s->datactrl = value & 0xff; 41849ab747fSPaolo Bonzini if (value & PL181_DATA_ENABLE) { 41949ab747fSPaolo Bonzini s->datacnt = s->datalength; 42049ab747fSPaolo Bonzini pl181_fifo_run(s); 42149ab747fSPaolo Bonzini } 42249ab747fSPaolo Bonzini break; 42349ab747fSPaolo Bonzini case 0x38: /* Clear */ 42449ab747fSPaolo Bonzini s->status &= ~(value & 0x7ff); 42549ab747fSPaolo Bonzini break; 42649ab747fSPaolo Bonzini case 0x3c: /* Mask0 */ 42749ab747fSPaolo Bonzini s->mask[0] = value; 42849ab747fSPaolo Bonzini break; 42949ab747fSPaolo Bonzini case 0x40: /* Mask1 */ 43049ab747fSPaolo Bonzini s->mask[1] = value; 43149ab747fSPaolo Bonzini break; 43249ab747fSPaolo Bonzini case 0x80: case 0x84: case 0x88: case 0x8c: /* FifoData */ 43349ab747fSPaolo Bonzini case 0x90: case 0x94: case 0x98: case 0x9c: 43449ab747fSPaolo Bonzini case 0xa0: case 0xa4: case 0xa8: case 0xac: 43549ab747fSPaolo Bonzini case 0xb0: case 0xb4: case 0xb8: case 0xbc: 43649ab747fSPaolo Bonzini if (s->datacnt == 0) { 43749ab747fSPaolo Bonzini qemu_log_mask(LOG_GUEST_ERROR, "pl181: Unexpected FIFO write\n"); 43849ab747fSPaolo Bonzini } else { 43949ab747fSPaolo Bonzini pl181_fifo_push(s, value); 44049ab747fSPaolo Bonzini pl181_fifo_run(s); 44149ab747fSPaolo Bonzini } 44249ab747fSPaolo Bonzini break; 44349ab747fSPaolo Bonzini default: 44449ab747fSPaolo Bonzini qemu_log_mask(LOG_GUEST_ERROR, 44549ab747fSPaolo Bonzini "pl181_write: Bad offset %x\n", (int)offset); 44649ab747fSPaolo Bonzini } 44749ab747fSPaolo Bonzini pl181_update(s); 44849ab747fSPaolo Bonzini } 44949ab747fSPaolo Bonzini 45049ab747fSPaolo Bonzini static const MemoryRegionOps pl181_ops = { 45149ab747fSPaolo Bonzini .read = pl181_read, 45249ab747fSPaolo Bonzini .write = pl181_write, 45349ab747fSPaolo Bonzini .endianness = DEVICE_NATIVE_ENDIAN, 45449ab747fSPaolo Bonzini }; 45549ab747fSPaolo Bonzini 45649ab747fSPaolo Bonzini static void pl181_reset(DeviceState *d) 45749ab747fSPaolo Bonzini { 458630f4442SAndreas Färber PL181State *s = PL181(d); 45949ab747fSPaolo Bonzini 46049ab747fSPaolo Bonzini s->power = 0; 46149ab747fSPaolo Bonzini s->cmdarg = 0; 46249ab747fSPaolo Bonzini s->cmd = 0; 46349ab747fSPaolo Bonzini s->datatimer = 0; 46449ab747fSPaolo Bonzini s->datalength = 0; 46549ab747fSPaolo Bonzini s->respcmd = 0; 46649ab747fSPaolo Bonzini s->response[0] = 0; 46749ab747fSPaolo Bonzini s->response[1] = 0; 46849ab747fSPaolo Bonzini s->response[2] = 0; 46949ab747fSPaolo Bonzini s->response[3] = 0; 47049ab747fSPaolo Bonzini s->datatimer = 0; 47149ab747fSPaolo Bonzini s->datalength = 0; 47249ab747fSPaolo Bonzini s->datactrl = 0; 47349ab747fSPaolo Bonzini s->datacnt = 0; 47449ab747fSPaolo Bonzini s->status = 0; 47549ab747fSPaolo Bonzini s->linux_hack = 0; 47649ab747fSPaolo Bonzini s->mask[0] = 0; 47749ab747fSPaolo Bonzini s->mask[1] = 0; 47849ab747fSPaolo Bonzini 47949ab747fSPaolo Bonzini /* We can assume our GPIO outputs have been wired up now */ 48049ab747fSPaolo Bonzini sd_set_cb(s->card, s->cardstatus[0], s->cardstatus[1]); 48149ab747fSPaolo Bonzini } 48249ab747fSPaolo Bonzini 483630f4442SAndreas Färber static int pl181_init(SysBusDevice *sbd) 48449ab747fSPaolo Bonzini { 485630f4442SAndreas Färber DeviceState *dev = DEVICE(sbd); 486630f4442SAndreas Färber PL181State *s = PL181(dev); 48749ab747fSPaolo Bonzini DriveInfo *dinfo; 48849ab747fSPaolo Bonzini 48929776739SPaolo Bonzini memory_region_init_io(&s->iomem, OBJECT(s), &pl181_ops, s, "pl181", 0x1000); 490630f4442SAndreas Färber sysbus_init_mmio(sbd, &s->iomem); 491630f4442SAndreas Färber sysbus_init_irq(sbd, &s->irq[0]); 492630f4442SAndreas Färber sysbus_init_irq(sbd, &s->irq[1]); 493630f4442SAndreas Färber qdev_init_gpio_out(dev, s->cardstatus, 2); 494af9e40aaSMarkus Armbruster /* FIXME use a qdev drive property instead of drive_get_next() */ 49549ab747fSPaolo Bonzini dinfo = drive_get_next(IF_SD); 4964be74634SMarkus Armbruster s->card = sd_init(dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, false); 4974f8a066bSKevin Wolf if (s->card == NULL) { 4984f8a066bSKevin Wolf return -1; 4994f8a066bSKevin Wolf } 5004f8a066bSKevin Wolf 50149ab747fSPaolo Bonzini return 0; 50249ab747fSPaolo Bonzini } 50349ab747fSPaolo Bonzini 50449ab747fSPaolo Bonzini static void pl181_class_init(ObjectClass *klass, void *data) 50549ab747fSPaolo Bonzini { 50649ab747fSPaolo Bonzini SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); 50749ab747fSPaolo Bonzini DeviceClass *k = DEVICE_CLASS(klass); 50849ab747fSPaolo Bonzini 50949ab747fSPaolo Bonzini sdc->init = pl181_init; 51049ab747fSPaolo Bonzini k->vmsd = &vmstate_pl181; 51149ab747fSPaolo Bonzini k->reset = pl181_reset; 5129f9bdf43SMarkus Armbruster /* Reason: init() method uses drive_get_next() */ 5139f9bdf43SMarkus Armbruster k->cannot_instantiate_with_device_add_yet = true; 51449ab747fSPaolo Bonzini } 51549ab747fSPaolo Bonzini 51649ab747fSPaolo Bonzini static const TypeInfo pl181_info = { 517630f4442SAndreas Färber .name = TYPE_PL181, 51849ab747fSPaolo Bonzini .parent = TYPE_SYS_BUS_DEVICE, 5191d998d93SAndreas Färber .instance_size = sizeof(PL181State), 52049ab747fSPaolo Bonzini .class_init = pl181_class_init, 52149ab747fSPaolo Bonzini }; 52249ab747fSPaolo Bonzini 52349ab747fSPaolo Bonzini static void pl181_register_types(void) 52449ab747fSPaolo Bonzini { 52549ab747fSPaolo Bonzini type_register_static(&pl181_info); 52649ab747fSPaolo Bonzini } 52749ab747fSPaolo Bonzini 52849ab747fSPaolo Bonzini type_init(pl181_register_types) 529