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 108ef94f0bSPeter Maydell #include "qemu/osdep.h" 1149ab747fSPaolo Bonzini #include "sysemu/blockdev.h" 1249ab747fSPaolo Bonzini #include "hw/sysbus.h" 13d6454270SMarkus Armbruster #include "migration/vmstate.h" 1464552b6bSMarkus Armbruster #include "hw/irq.h" 15e3382ef0SSai Pavan Boddu #include "hw/sd/sd.h" 1603dd024fSPaolo Bonzini #include "qemu/log.h" 170b8fa32fSMarkus Armbruster #include "qemu/module.h" 184858e256SAlistair Francis #include "qemu/error-report.h" 190d554cb0Sxiaoqiang zhao #include "qapi/error.h" 2049ab747fSPaolo Bonzini 2149ab747fSPaolo Bonzini //#define DEBUG_PL181 1 2249ab747fSPaolo Bonzini 2349ab747fSPaolo Bonzini #ifdef DEBUG_PL181 2449ab747fSPaolo Bonzini #define DPRINTF(fmt, ...) \ 2549ab747fSPaolo Bonzini do { printf("pl181: " fmt , ## __VA_ARGS__); } while (0) 2649ab747fSPaolo Bonzini #else 2749ab747fSPaolo Bonzini #define DPRINTF(fmt, ...) do {} while(0) 2849ab747fSPaolo Bonzini #endif 2949ab747fSPaolo Bonzini 3049ab747fSPaolo Bonzini #define PL181_FIFO_LEN 16 3149ab747fSPaolo Bonzini 32630f4442SAndreas Färber #define TYPE_PL181 "pl181" 33630f4442SAndreas Färber #define PL181(obj) OBJECT_CHECK(PL181State, (obj), TYPE_PL181) 34630f4442SAndreas Färber 352762eed1SPhilippe Mathieu-Daudé #define TYPE_PL181_BUS "pl181-bus" 362762eed1SPhilippe Mathieu-Daudé 371d998d93SAndreas Färber typedef struct PL181State { 38630f4442SAndreas Färber SysBusDevice parent_obj; 39630f4442SAndreas Färber 4049ab747fSPaolo Bonzini MemoryRegion iomem; 412762eed1SPhilippe Mathieu-Daudé SDBus sdbus; 4249ab747fSPaolo Bonzini uint32_t clock; 4349ab747fSPaolo Bonzini uint32_t power; 4449ab747fSPaolo Bonzini uint32_t cmdarg; 4549ab747fSPaolo Bonzini uint32_t cmd; 4649ab747fSPaolo Bonzini uint32_t datatimer; 4749ab747fSPaolo Bonzini uint32_t datalength; 4849ab747fSPaolo Bonzini uint32_t respcmd; 4949ab747fSPaolo Bonzini uint32_t response[4]; 5049ab747fSPaolo Bonzini uint32_t datactrl; 5149ab747fSPaolo Bonzini uint32_t datacnt; 5249ab747fSPaolo Bonzini uint32_t status; 5349ab747fSPaolo Bonzini uint32_t mask[2]; 5449ab747fSPaolo Bonzini int32_t fifo_pos; 5549ab747fSPaolo Bonzini int32_t fifo_len; 5649ab747fSPaolo Bonzini /* The linux 2.6.21 driver is buggy, and misbehaves if new data arrives 5767cc32ebSVeres Lajos while it is reading the FIFO. We hack around this by deferring 5849ab747fSPaolo Bonzini subsequent transfers until after the driver polls the status word. 5949ab747fSPaolo Bonzini http://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=4446/1 6049ab747fSPaolo Bonzini */ 6149ab747fSPaolo Bonzini int32_t linux_hack; 620e33730cSPhilippe Mathieu-Daudé uint32_t fifo[PL181_FIFO_LEN]; /* TODO use Fifo32 */ 6349ab747fSPaolo Bonzini qemu_irq irq[2]; 6449ab747fSPaolo Bonzini /* GPIO outputs for 'card is readonly' and 'card inserted' */ 6526c5b0f4SPhilippe Mathieu-Daudé qemu_irq card_readonly; 6626c5b0f4SPhilippe Mathieu-Daudé qemu_irq card_inserted; 671d998d93SAndreas Färber } PL181State; 6849ab747fSPaolo Bonzini 6949ab747fSPaolo Bonzini static const VMStateDescription vmstate_pl181 = { 7049ab747fSPaolo Bonzini .name = "pl181", 7149ab747fSPaolo Bonzini .version_id = 1, 7249ab747fSPaolo Bonzini .minimum_version_id = 1, 7349ab747fSPaolo Bonzini .fields = (VMStateField[]) { 741d998d93SAndreas Färber VMSTATE_UINT32(clock, PL181State), 751d998d93SAndreas Färber VMSTATE_UINT32(power, PL181State), 761d998d93SAndreas Färber VMSTATE_UINT32(cmdarg, PL181State), 771d998d93SAndreas Färber VMSTATE_UINT32(cmd, PL181State), 781d998d93SAndreas Färber VMSTATE_UINT32(datatimer, PL181State), 791d998d93SAndreas Färber VMSTATE_UINT32(datalength, PL181State), 801d998d93SAndreas Färber VMSTATE_UINT32(respcmd, PL181State), 811d998d93SAndreas Färber VMSTATE_UINT32_ARRAY(response, PL181State, 4), 821d998d93SAndreas Färber VMSTATE_UINT32(datactrl, PL181State), 831d998d93SAndreas Färber VMSTATE_UINT32(datacnt, PL181State), 841d998d93SAndreas Färber VMSTATE_UINT32(status, PL181State), 851d998d93SAndreas Färber VMSTATE_UINT32_ARRAY(mask, PL181State, 2), 861d998d93SAndreas Färber VMSTATE_INT32(fifo_pos, PL181State), 871d998d93SAndreas Färber VMSTATE_INT32(fifo_len, PL181State), 881d998d93SAndreas Färber VMSTATE_INT32(linux_hack, PL181State), 891d998d93SAndreas Färber VMSTATE_UINT32_ARRAY(fifo, PL181State, PL181_FIFO_LEN), 9049ab747fSPaolo Bonzini VMSTATE_END_OF_LIST() 9149ab747fSPaolo Bonzini } 9249ab747fSPaolo Bonzini }; 9349ab747fSPaolo Bonzini 9449ab747fSPaolo Bonzini #define PL181_CMD_INDEX 0x3f 9549ab747fSPaolo Bonzini #define PL181_CMD_RESPONSE (1 << 6) 9649ab747fSPaolo Bonzini #define PL181_CMD_LONGRESP (1 << 7) 9749ab747fSPaolo Bonzini #define PL181_CMD_INTERRUPT (1 << 8) 9849ab747fSPaolo Bonzini #define PL181_CMD_PENDING (1 << 9) 9949ab747fSPaolo Bonzini #define PL181_CMD_ENABLE (1 << 10) 10049ab747fSPaolo Bonzini 10149ab747fSPaolo Bonzini #define PL181_DATA_ENABLE (1 << 0) 10249ab747fSPaolo Bonzini #define PL181_DATA_DIRECTION (1 << 1) 10349ab747fSPaolo Bonzini #define PL181_DATA_MODE (1 << 2) 10449ab747fSPaolo Bonzini #define PL181_DATA_DMAENABLE (1 << 3) 10549ab747fSPaolo Bonzini 10649ab747fSPaolo Bonzini #define PL181_STATUS_CMDCRCFAIL (1 << 0) 10749ab747fSPaolo Bonzini #define PL181_STATUS_DATACRCFAIL (1 << 1) 10849ab747fSPaolo Bonzini #define PL181_STATUS_CMDTIMEOUT (1 << 2) 10949ab747fSPaolo Bonzini #define PL181_STATUS_DATATIMEOUT (1 << 3) 11049ab747fSPaolo Bonzini #define PL181_STATUS_TXUNDERRUN (1 << 4) 11149ab747fSPaolo Bonzini #define PL181_STATUS_RXOVERRUN (1 << 5) 11249ab747fSPaolo Bonzini #define PL181_STATUS_CMDRESPEND (1 << 6) 11349ab747fSPaolo Bonzini #define PL181_STATUS_CMDSENT (1 << 7) 11449ab747fSPaolo Bonzini #define PL181_STATUS_DATAEND (1 << 8) 11549ab747fSPaolo Bonzini #define PL181_STATUS_DATABLOCKEND (1 << 10) 11649ab747fSPaolo Bonzini #define PL181_STATUS_CMDACTIVE (1 << 11) 11749ab747fSPaolo Bonzini #define PL181_STATUS_TXACTIVE (1 << 12) 11849ab747fSPaolo Bonzini #define PL181_STATUS_RXACTIVE (1 << 13) 11949ab747fSPaolo Bonzini #define PL181_STATUS_TXFIFOHALFEMPTY (1 << 14) 12049ab747fSPaolo Bonzini #define PL181_STATUS_RXFIFOHALFFULL (1 << 15) 12149ab747fSPaolo Bonzini #define PL181_STATUS_TXFIFOFULL (1 << 16) 12249ab747fSPaolo Bonzini #define PL181_STATUS_RXFIFOFULL (1 << 17) 12349ab747fSPaolo Bonzini #define PL181_STATUS_TXFIFOEMPTY (1 << 18) 12449ab747fSPaolo Bonzini #define PL181_STATUS_RXFIFOEMPTY (1 << 19) 12549ab747fSPaolo Bonzini #define PL181_STATUS_TXDATAAVLBL (1 << 20) 12649ab747fSPaolo Bonzini #define PL181_STATUS_RXDATAAVLBL (1 << 21) 12749ab747fSPaolo Bonzini 12849ab747fSPaolo Bonzini #define PL181_STATUS_TX_FIFO (PL181_STATUS_TXACTIVE \ 12949ab747fSPaolo Bonzini |PL181_STATUS_TXFIFOHALFEMPTY \ 13049ab747fSPaolo Bonzini |PL181_STATUS_TXFIFOFULL \ 13149ab747fSPaolo Bonzini |PL181_STATUS_TXFIFOEMPTY \ 13249ab747fSPaolo Bonzini |PL181_STATUS_TXDATAAVLBL) 13349ab747fSPaolo Bonzini #define PL181_STATUS_RX_FIFO (PL181_STATUS_RXACTIVE \ 13449ab747fSPaolo Bonzini |PL181_STATUS_RXFIFOHALFFULL \ 13549ab747fSPaolo Bonzini |PL181_STATUS_RXFIFOFULL \ 13649ab747fSPaolo Bonzini |PL181_STATUS_RXFIFOEMPTY \ 13749ab747fSPaolo Bonzini |PL181_STATUS_RXDATAAVLBL) 13849ab747fSPaolo Bonzini 13949ab747fSPaolo Bonzini static const unsigned char pl181_id[] = 14049ab747fSPaolo Bonzini { 0x81, 0x11, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; 14149ab747fSPaolo Bonzini 1421d998d93SAndreas Färber static void pl181_update(PL181State *s) 14349ab747fSPaolo Bonzini { 14449ab747fSPaolo Bonzini int i; 14549ab747fSPaolo Bonzini for (i = 0; i < 2; i++) { 14649ab747fSPaolo Bonzini qemu_set_irq(s->irq[i], (s->status & s->mask[i]) != 0); 14749ab747fSPaolo Bonzini } 14849ab747fSPaolo Bonzini } 14949ab747fSPaolo Bonzini 1501d998d93SAndreas Färber static void pl181_fifo_push(PL181State *s, uint32_t value) 15149ab747fSPaolo Bonzini { 15249ab747fSPaolo Bonzini int n; 15349ab747fSPaolo Bonzini 15449ab747fSPaolo Bonzini if (s->fifo_len == PL181_FIFO_LEN) { 1554858e256SAlistair Francis error_report("%s: FIFO overflow", __func__); 15649ab747fSPaolo Bonzini return; 15749ab747fSPaolo Bonzini } 15849ab747fSPaolo Bonzini n = (s->fifo_pos + s->fifo_len) & (PL181_FIFO_LEN - 1); 15949ab747fSPaolo Bonzini s->fifo_len++; 16049ab747fSPaolo Bonzini s->fifo[n] = value; 16149ab747fSPaolo Bonzini DPRINTF("FIFO push %08x\n", (int)value); 16249ab747fSPaolo Bonzini } 16349ab747fSPaolo Bonzini 1641d998d93SAndreas Färber static uint32_t pl181_fifo_pop(PL181State *s) 16549ab747fSPaolo Bonzini { 16649ab747fSPaolo Bonzini uint32_t value; 16749ab747fSPaolo Bonzini 16849ab747fSPaolo Bonzini if (s->fifo_len == 0) { 1694858e256SAlistair Francis error_report("%s: FIFO underflow", __func__); 17049ab747fSPaolo Bonzini return 0; 17149ab747fSPaolo Bonzini } 17249ab747fSPaolo Bonzini value = s->fifo[s->fifo_pos]; 17349ab747fSPaolo Bonzini s->fifo_len--; 17449ab747fSPaolo Bonzini s->fifo_pos = (s->fifo_pos + 1) & (PL181_FIFO_LEN - 1); 17549ab747fSPaolo Bonzini DPRINTF("FIFO pop %08x\n", (int)value); 17649ab747fSPaolo Bonzini return value; 17749ab747fSPaolo Bonzini } 17849ab747fSPaolo Bonzini 179b67cd8f5SPhilippe Mathieu-Daudé static void pl181_do_command(PL181State *s) 18049ab747fSPaolo Bonzini { 18149ab747fSPaolo Bonzini SDRequest request; 18249ab747fSPaolo Bonzini uint8_t response[16]; 18349ab747fSPaolo Bonzini int rlen; 18449ab747fSPaolo Bonzini 18549ab747fSPaolo Bonzini request.cmd = s->cmd & PL181_CMD_INDEX; 18649ab747fSPaolo Bonzini request.arg = s->cmdarg; 18749ab747fSPaolo Bonzini DPRINTF("Command %d %08x\n", request.cmd, request.arg); 1882762eed1SPhilippe Mathieu-Daudé rlen = sdbus_do_command(&s->sdbus, &request, response); 18949ab747fSPaolo Bonzini if (rlen < 0) 19049ab747fSPaolo Bonzini goto error; 19149ab747fSPaolo Bonzini if (s->cmd & PL181_CMD_RESPONSE) { 19249ab747fSPaolo Bonzini if (rlen == 0 || (rlen == 4 && (s->cmd & PL181_CMD_LONGRESP))) 19349ab747fSPaolo Bonzini goto error; 19449ab747fSPaolo Bonzini if (rlen != 4 && rlen != 16) 19549ab747fSPaolo Bonzini goto error; 196b3141c06SPhilippe Mathieu-Daudé s->response[0] = ldl_be_p(&response[0]); 19749ab747fSPaolo Bonzini if (rlen == 4) { 19849ab747fSPaolo Bonzini s->response[1] = s->response[2] = s->response[3] = 0; 19949ab747fSPaolo Bonzini } else { 200b3141c06SPhilippe Mathieu-Daudé s->response[1] = ldl_be_p(&response[4]); 201b3141c06SPhilippe Mathieu-Daudé s->response[2] = ldl_be_p(&response[8]); 202b3141c06SPhilippe Mathieu-Daudé s->response[3] = ldl_be_p(&response[12]) & ~1; 20349ab747fSPaolo Bonzini } 20449ab747fSPaolo Bonzini DPRINTF("Response received\n"); 20549ab747fSPaolo Bonzini s->status |= PL181_STATUS_CMDRESPEND; 20649ab747fSPaolo Bonzini } else { 20749ab747fSPaolo Bonzini DPRINTF("Command sent\n"); 20849ab747fSPaolo Bonzini s->status |= PL181_STATUS_CMDSENT; 20949ab747fSPaolo Bonzini } 21049ab747fSPaolo Bonzini return; 21149ab747fSPaolo Bonzini 21249ab747fSPaolo Bonzini error: 21349ab747fSPaolo Bonzini DPRINTF("Timeout\n"); 21449ab747fSPaolo Bonzini s->status |= PL181_STATUS_CMDTIMEOUT; 21549ab747fSPaolo Bonzini } 21649ab747fSPaolo Bonzini 21749ab747fSPaolo Bonzini /* Transfer data between the card and the FIFO. This is complicated by 21849ab747fSPaolo Bonzini the FIFO holding 32-bit words and the card taking data in single byte 21949ab747fSPaolo Bonzini chunks. FIFO bytes are transferred in little-endian order. */ 22049ab747fSPaolo Bonzini 2211d998d93SAndreas Färber static void pl181_fifo_run(PL181State *s) 22249ab747fSPaolo Bonzini { 22349ab747fSPaolo Bonzini uint32_t bits; 22449ab747fSPaolo Bonzini uint32_t value = 0; 22549ab747fSPaolo Bonzini int n; 22649ab747fSPaolo Bonzini int is_read; 22749ab747fSPaolo Bonzini 22849ab747fSPaolo Bonzini is_read = (s->datactrl & PL181_DATA_DIRECTION) != 0; 2292762eed1SPhilippe Mathieu-Daudé if (s->datacnt != 0 && (!is_read || sdbus_data_ready(&s->sdbus)) 23049ab747fSPaolo Bonzini && !s->linux_hack) { 23149ab747fSPaolo Bonzini if (is_read) { 23249ab747fSPaolo Bonzini n = 0; 23349ab747fSPaolo Bonzini while (s->datacnt && s->fifo_len < PL181_FIFO_LEN) { 2342762eed1SPhilippe Mathieu-Daudé value |= (uint32_t)sdbus_read_data(&s->sdbus) << (n * 8); 23549ab747fSPaolo Bonzini s->datacnt--; 23649ab747fSPaolo Bonzini n++; 23749ab747fSPaolo Bonzini if (n == 4) { 23849ab747fSPaolo Bonzini pl181_fifo_push(s, value); 23949ab747fSPaolo Bonzini n = 0; 24049ab747fSPaolo Bonzini value = 0; 24149ab747fSPaolo Bonzini } 24249ab747fSPaolo Bonzini } 24349ab747fSPaolo Bonzini if (n != 0) { 24449ab747fSPaolo Bonzini pl181_fifo_push(s, value); 24549ab747fSPaolo Bonzini } 24649ab747fSPaolo Bonzini } else { /* write */ 24749ab747fSPaolo Bonzini n = 0; 24849ab747fSPaolo Bonzini while (s->datacnt > 0 && (s->fifo_len > 0 || n > 0)) { 24949ab747fSPaolo Bonzini if (n == 0) { 25049ab747fSPaolo Bonzini value = pl181_fifo_pop(s); 25149ab747fSPaolo Bonzini n = 4; 25249ab747fSPaolo Bonzini } 25349ab747fSPaolo Bonzini n--; 25449ab747fSPaolo Bonzini s->datacnt--; 2552762eed1SPhilippe Mathieu-Daudé sdbus_write_data(&s->sdbus, value & 0xff); 25649ab747fSPaolo Bonzini value >>= 8; 25749ab747fSPaolo Bonzini } 25849ab747fSPaolo Bonzini } 25949ab747fSPaolo Bonzini } 26049ab747fSPaolo Bonzini s->status &= ~(PL181_STATUS_RX_FIFO | PL181_STATUS_TX_FIFO); 26149ab747fSPaolo Bonzini if (s->datacnt == 0) { 26249ab747fSPaolo Bonzini s->status |= PL181_STATUS_DATAEND; 26349ab747fSPaolo Bonzini /* HACK: */ 26449ab747fSPaolo Bonzini s->status |= PL181_STATUS_DATABLOCKEND; 26549ab747fSPaolo Bonzini DPRINTF("Transfer Complete\n"); 26649ab747fSPaolo Bonzini } 26749ab747fSPaolo Bonzini if (s->datacnt == 0 && s->fifo_len == 0) { 26849ab747fSPaolo Bonzini s->datactrl &= ~PL181_DATA_ENABLE; 26949ab747fSPaolo Bonzini DPRINTF("Data engine idle\n"); 27049ab747fSPaolo Bonzini } else { 27149ab747fSPaolo Bonzini /* Update FIFO bits. */ 27249ab747fSPaolo Bonzini bits = PL181_STATUS_TXACTIVE | PL181_STATUS_RXACTIVE; 27349ab747fSPaolo Bonzini if (s->fifo_len == 0) { 27449ab747fSPaolo Bonzini bits |= PL181_STATUS_TXFIFOEMPTY; 27549ab747fSPaolo Bonzini bits |= PL181_STATUS_RXFIFOEMPTY; 27649ab747fSPaolo Bonzini } else { 27749ab747fSPaolo Bonzini bits |= PL181_STATUS_TXDATAAVLBL; 27849ab747fSPaolo Bonzini bits |= PL181_STATUS_RXDATAAVLBL; 27949ab747fSPaolo Bonzini } 28049ab747fSPaolo Bonzini if (s->fifo_len == 16) { 28149ab747fSPaolo Bonzini bits |= PL181_STATUS_TXFIFOFULL; 28249ab747fSPaolo Bonzini bits |= PL181_STATUS_RXFIFOFULL; 28349ab747fSPaolo Bonzini } 28449ab747fSPaolo Bonzini if (s->fifo_len <= 8) { 28549ab747fSPaolo Bonzini bits |= PL181_STATUS_TXFIFOHALFEMPTY; 28649ab747fSPaolo Bonzini } 28749ab747fSPaolo Bonzini if (s->fifo_len >= 8) { 28849ab747fSPaolo Bonzini bits |= PL181_STATUS_RXFIFOHALFFULL; 28949ab747fSPaolo Bonzini } 29049ab747fSPaolo Bonzini if (s->datactrl & PL181_DATA_DIRECTION) { 29149ab747fSPaolo Bonzini bits &= PL181_STATUS_RX_FIFO; 29249ab747fSPaolo Bonzini } else { 29349ab747fSPaolo Bonzini bits &= PL181_STATUS_TX_FIFO; 29449ab747fSPaolo Bonzini } 29549ab747fSPaolo Bonzini s->status |= bits; 29649ab747fSPaolo Bonzini } 29749ab747fSPaolo Bonzini } 29849ab747fSPaolo Bonzini 29949ab747fSPaolo Bonzini static uint64_t pl181_read(void *opaque, hwaddr offset, 30049ab747fSPaolo Bonzini unsigned size) 30149ab747fSPaolo Bonzini { 3021d998d93SAndreas Färber PL181State *s = (PL181State *)opaque; 30349ab747fSPaolo Bonzini uint32_t tmp; 30449ab747fSPaolo Bonzini 30549ab747fSPaolo Bonzini if (offset >= 0xfe0 && offset < 0x1000) { 30649ab747fSPaolo Bonzini return pl181_id[(offset - 0xfe0) >> 2]; 30749ab747fSPaolo Bonzini } 30849ab747fSPaolo Bonzini switch (offset) { 30949ab747fSPaolo Bonzini case 0x00: /* Power */ 31049ab747fSPaolo Bonzini return s->power; 31149ab747fSPaolo Bonzini case 0x04: /* Clock */ 31249ab747fSPaolo Bonzini return s->clock; 31349ab747fSPaolo Bonzini case 0x08: /* Argument */ 31449ab747fSPaolo Bonzini return s->cmdarg; 31549ab747fSPaolo Bonzini case 0x0c: /* Command */ 31649ab747fSPaolo Bonzini return s->cmd; 31749ab747fSPaolo Bonzini case 0x10: /* RespCmd */ 31849ab747fSPaolo Bonzini return s->respcmd; 31949ab747fSPaolo Bonzini case 0x14: /* Response0 */ 32049ab747fSPaolo Bonzini return s->response[0]; 32149ab747fSPaolo Bonzini case 0x18: /* Response1 */ 32249ab747fSPaolo Bonzini return s->response[1]; 32349ab747fSPaolo Bonzini case 0x1c: /* Response2 */ 32449ab747fSPaolo Bonzini return s->response[2]; 32549ab747fSPaolo Bonzini case 0x20: /* Response3 */ 32649ab747fSPaolo Bonzini return s->response[3]; 32749ab747fSPaolo Bonzini case 0x24: /* DataTimer */ 32849ab747fSPaolo Bonzini return s->datatimer; 32949ab747fSPaolo Bonzini case 0x28: /* DataLength */ 33049ab747fSPaolo Bonzini return s->datalength; 33149ab747fSPaolo Bonzini case 0x2c: /* DataCtrl */ 33249ab747fSPaolo Bonzini return s->datactrl; 33349ab747fSPaolo Bonzini case 0x30: /* DataCnt */ 33449ab747fSPaolo Bonzini return s->datacnt; 33549ab747fSPaolo Bonzini case 0x34: /* Status */ 33649ab747fSPaolo Bonzini tmp = s->status; 33749ab747fSPaolo Bonzini if (s->linux_hack) { 33849ab747fSPaolo Bonzini s->linux_hack = 0; 33949ab747fSPaolo Bonzini pl181_fifo_run(s); 34049ab747fSPaolo Bonzini pl181_update(s); 34149ab747fSPaolo Bonzini } 34249ab747fSPaolo Bonzini return tmp; 34349ab747fSPaolo Bonzini case 0x3c: /* Mask0 */ 34449ab747fSPaolo Bonzini return s->mask[0]; 34549ab747fSPaolo Bonzini case 0x40: /* Mask1 */ 34649ab747fSPaolo Bonzini return s->mask[1]; 34749ab747fSPaolo Bonzini case 0x48: /* FifoCnt */ 34849ab747fSPaolo Bonzini /* The documentation is somewhat vague about exactly what FifoCnt 34949ab747fSPaolo Bonzini does. On real hardware it appears to be when decrememnted 35049ab747fSPaolo Bonzini when a word is transferred between the FIFO and the serial 35149ab747fSPaolo Bonzini data engine. DataCnt is decremented after each byte is 35249ab747fSPaolo Bonzini transferred between the serial engine and the card. 35349ab747fSPaolo Bonzini We don't emulate this level of detail, so both can be the same. */ 35449ab747fSPaolo Bonzini tmp = (s->datacnt + 3) >> 2; 35549ab747fSPaolo Bonzini if (s->linux_hack) { 35649ab747fSPaolo Bonzini s->linux_hack = 0; 35749ab747fSPaolo Bonzini pl181_fifo_run(s); 35849ab747fSPaolo Bonzini pl181_update(s); 35949ab747fSPaolo Bonzini } 36049ab747fSPaolo Bonzini return tmp; 36149ab747fSPaolo Bonzini case 0x80: case 0x84: case 0x88: case 0x8c: /* FifoData */ 36249ab747fSPaolo Bonzini case 0x90: case 0x94: case 0x98: case 0x9c: 36349ab747fSPaolo Bonzini case 0xa0: case 0xa4: case 0xa8: case 0xac: 36449ab747fSPaolo Bonzini case 0xb0: case 0xb4: case 0xb8: case 0xbc: 36549ab747fSPaolo Bonzini if (s->fifo_len == 0) { 36649ab747fSPaolo Bonzini qemu_log_mask(LOG_GUEST_ERROR, "pl181: Unexpected FIFO read\n"); 36749ab747fSPaolo Bonzini return 0; 36849ab747fSPaolo Bonzini } else { 36949ab747fSPaolo Bonzini uint32_t value; 37049ab747fSPaolo Bonzini value = pl181_fifo_pop(s); 37149ab747fSPaolo Bonzini s->linux_hack = 1; 37249ab747fSPaolo Bonzini pl181_fifo_run(s); 37349ab747fSPaolo Bonzini pl181_update(s); 37449ab747fSPaolo Bonzini return value; 37549ab747fSPaolo Bonzini } 37649ab747fSPaolo Bonzini default: 37749ab747fSPaolo Bonzini qemu_log_mask(LOG_GUEST_ERROR, 37849ab747fSPaolo Bonzini "pl181_read: Bad offset %x\n", (int)offset); 37949ab747fSPaolo Bonzini return 0; 38049ab747fSPaolo Bonzini } 38149ab747fSPaolo Bonzini } 38249ab747fSPaolo Bonzini 38349ab747fSPaolo Bonzini static void pl181_write(void *opaque, hwaddr offset, 38449ab747fSPaolo Bonzini uint64_t value, unsigned size) 38549ab747fSPaolo Bonzini { 3861d998d93SAndreas Färber PL181State *s = (PL181State *)opaque; 38749ab747fSPaolo Bonzini 38849ab747fSPaolo Bonzini switch (offset) { 38949ab747fSPaolo Bonzini case 0x00: /* Power */ 39049ab747fSPaolo Bonzini s->power = value & 0xff; 39149ab747fSPaolo Bonzini break; 39249ab747fSPaolo Bonzini case 0x04: /* Clock */ 39349ab747fSPaolo Bonzini s->clock = value & 0xff; 39449ab747fSPaolo Bonzini break; 39549ab747fSPaolo Bonzini case 0x08: /* Argument */ 39649ab747fSPaolo Bonzini s->cmdarg = value; 39749ab747fSPaolo Bonzini break; 39849ab747fSPaolo Bonzini case 0x0c: /* Command */ 39949ab747fSPaolo Bonzini s->cmd = value; 40049ab747fSPaolo Bonzini if (s->cmd & PL181_CMD_ENABLE) { 40149ab747fSPaolo Bonzini if (s->cmd & PL181_CMD_INTERRUPT) { 40249ab747fSPaolo Bonzini qemu_log_mask(LOG_UNIMP, 40349ab747fSPaolo Bonzini "pl181: Interrupt mode not implemented\n"); 40449ab747fSPaolo Bonzini } if (s->cmd & PL181_CMD_PENDING) { 40549ab747fSPaolo Bonzini qemu_log_mask(LOG_UNIMP, 40649ab747fSPaolo Bonzini "pl181: Pending commands not implemented\n"); 40749ab747fSPaolo Bonzini } else { 408b67cd8f5SPhilippe Mathieu-Daudé pl181_do_command(s); 40949ab747fSPaolo Bonzini pl181_fifo_run(s); 41049ab747fSPaolo Bonzini } 41149ab747fSPaolo Bonzini /* The command has completed one way or the other. */ 41249ab747fSPaolo Bonzini s->cmd &= ~PL181_CMD_ENABLE; 41349ab747fSPaolo Bonzini } 41449ab747fSPaolo Bonzini break; 41549ab747fSPaolo Bonzini case 0x24: /* DataTimer */ 41649ab747fSPaolo Bonzini s->datatimer = value; 41749ab747fSPaolo Bonzini break; 41849ab747fSPaolo Bonzini case 0x28: /* DataLength */ 41949ab747fSPaolo Bonzini s->datalength = value & 0xffff; 42049ab747fSPaolo Bonzini break; 42149ab747fSPaolo Bonzini case 0x2c: /* DataCtrl */ 42249ab747fSPaolo Bonzini s->datactrl = value & 0xff; 42349ab747fSPaolo Bonzini if (value & PL181_DATA_ENABLE) { 42449ab747fSPaolo Bonzini s->datacnt = s->datalength; 42549ab747fSPaolo Bonzini pl181_fifo_run(s); 42649ab747fSPaolo Bonzini } 42749ab747fSPaolo Bonzini break; 42849ab747fSPaolo Bonzini case 0x38: /* Clear */ 42949ab747fSPaolo Bonzini s->status &= ~(value & 0x7ff); 43049ab747fSPaolo Bonzini break; 43149ab747fSPaolo Bonzini case 0x3c: /* Mask0 */ 43249ab747fSPaolo Bonzini s->mask[0] = value; 43349ab747fSPaolo Bonzini break; 43449ab747fSPaolo Bonzini case 0x40: /* Mask1 */ 43549ab747fSPaolo Bonzini s->mask[1] = value; 43649ab747fSPaolo Bonzini break; 43749ab747fSPaolo Bonzini case 0x80: case 0x84: case 0x88: case 0x8c: /* FifoData */ 43849ab747fSPaolo Bonzini case 0x90: case 0x94: case 0x98: case 0x9c: 43949ab747fSPaolo Bonzini case 0xa0: case 0xa4: case 0xa8: case 0xac: 44049ab747fSPaolo Bonzini case 0xb0: case 0xb4: case 0xb8: case 0xbc: 44149ab747fSPaolo Bonzini if (s->datacnt == 0) { 44249ab747fSPaolo Bonzini qemu_log_mask(LOG_GUEST_ERROR, "pl181: Unexpected FIFO write\n"); 44349ab747fSPaolo Bonzini } else { 44449ab747fSPaolo Bonzini pl181_fifo_push(s, value); 44549ab747fSPaolo Bonzini pl181_fifo_run(s); 44649ab747fSPaolo Bonzini } 44749ab747fSPaolo Bonzini break; 44849ab747fSPaolo Bonzini default: 44949ab747fSPaolo Bonzini qemu_log_mask(LOG_GUEST_ERROR, 45049ab747fSPaolo Bonzini "pl181_write: Bad offset %x\n", (int)offset); 45149ab747fSPaolo Bonzini } 45249ab747fSPaolo Bonzini pl181_update(s); 45349ab747fSPaolo Bonzini } 45449ab747fSPaolo Bonzini 45549ab747fSPaolo Bonzini static const MemoryRegionOps pl181_ops = { 45649ab747fSPaolo Bonzini .read = pl181_read, 45749ab747fSPaolo Bonzini .write = pl181_write, 45849ab747fSPaolo Bonzini .endianness = DEVICE_NATIVE_ENDIAN, 45949ab747fSPaolo Bonzini }; 46049ab747fSPaolo Bonzini 4612762eed1SPhilippe Mathieu-Daudé static void pl181_set_readonly(DeviceState *dev, bool level) 4622762eed1SPhilippe Mathieu-Daudé { 4632762eed1SPhilippe Mathieu-Daudé PL181State *s = (PL181State *)dev; 4642762eed1SPhilippe Mathieu-Daudé 4652762eed1SPhilippe Mathieu-Daudé qemu_set_irq(s->card_readonly, level); 4662762eed1SPhilippe Mathieu-Daudé } 4672762eed1SPhilippe Mathieu-Daudé 4682762eed1SPhilippe Mathieu-Daudé static void pl181_set_inserted(DeviceState *dev, bool level) 4692762eed1SPhilippe Mathieu-Daudé { 4702762eed1SPhilippe Mathieu-Daudé PL181State *s = (PL181State *)dev; 4712762eed1SPhilippe Mathieu-Daudé 4722762eed1SPhilippe Mathieu-Daudé qemu_set_irq(s->card_inserted, level); 4732762eed1SPhilippe Mathieu-Daudé } 4742762eed1SPhilippe Mathieu-Daudé 47549ab747fSPaolo Bonzini static void pl181_reset(DeviceState *d) 47649ab747fSPaolo Bonzini { 477630f4442SAndreas Färber PL181State *s = PL181(d); 47849ab747fSPaolo Bonzini 47949ab747fSPaolo Bonzini s->power = 0; 48049ab747fSPaolo Bonzini s->cmdarg = 0; 48149ab747fSPaolo Bonzini s->cmd = 0; 48249ab747fSPaolo Bonzini s->datatimer = 0; 48349ab747fSPaolo Bonzini s->datalength = 0; 48449ab747fSPaolo Bonzini s->respcmd = 0; 48549ab747fSPaolo Bonzini s->response[0] = 0; 48649ab747fSPaolo Bonzini s->response[1] = 0; 48749ab747fSPaolo Bonzini s->response[2] = 0; 48849ab747fSPaolo Bonzini s->response[3] = 0; 48949ab747fSPaolo Bonzini s->datatimer = 0; 49049ab747fSPaolo Bonzini s->datalength = 0; 49149ab747fSPaolo Bonzini s->datactrl = 0; 49249ab747fSPaolo Bonzini s->datacnt = 0; 49349ab747fSPaolo Bonzini s->status = 0; 49449ab747fSPaolo Bonzini s->linux_hack = 0; 49549ab747fSPaolo Bonzini s->mask[0] = 0; 49649ab747fSPaolo Bonzini s->mask[1] = 0; 49749ab747fSPaolo Bonzini 4982762eed1SPhilippe Mathieu-Daudé /* Reset other state based on current card insertion/readonly status */ 4992762eed1SPhilippe Mathieu-Daudé pl181_set_inserted(DEVICE(s), sdbus_get_inserted(&s->sdbus)); 5002762eed1SPhilippe Mathieu-Daudé pl181_set_readonly(DEVICE(s), sdbus_get_readonly(&s->sdbus)); 50149ab747fSPaolo Bonzini } 50249ab747fSPaolo Bonzini 5030d554cb0Sxiaoqiang zhao static void pl181_init(Object *obj) 50449ab747fSPaolo Bonzini { 5050d554cb0Sxiaoqiang zhao DeviceState *dev = DEVICE(obj); 5060d554cb0Sxiaoqiang zhao PL181State *s = PL181(obj); 5070d554cb0Sxiaoqiang zhao SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 50849ab747fSPaolo Bonzini 5090d554cb0Sxiaoqiang zhao memory_region_init_io(&s->iomem, obj, &pl181_ops, s, "pl181", 0x1000); 510630f4442SAndreas Färber sysbus_init_mmio(sbd, &s->iomem); 511630f4442SAndreas Färber sysbus_init_irq(sbd, &s->irq[0]); 512630f4442SAndreas Färber sysbus_init_irq(sbd, &s->irq[1]); 51326c5b0f4SPhilippe Mathieu-Daudé qdev_init_gpio_out_named(dev, &s->card_readonly, "card-read-only", 1); 51426c5b0f4SPhilippe Mathieu-Daudé qdev_init_gpio_out_named(dev, &s->card_inserted, "card-inserted", 1); 5152762eed1SPhilippe Mathieu-Daudé 5162762eed1SPhilippe Mathieu-Daudé qbus_create_inplace(&s->sdbus, sizeof(s->sdbus), 5172762eed1SPhilippe Mathieu-Daudé TYPE_PL181_BUS, dev, "sd-bus"); 5180d554cb0Sxiaoqiang zhao } 5190d554cb0Sxiaoqiang zhao 52049ab747fSPaolo Bonzini static void pl181_class_init(ObjectClass *klass, void *data) 52149ab747fSPaolo Bonzini { 52249ab747fSPaolo Bonzini DeviceClass *k = DEVICE_CLASS(klass); 52349ab747fSPaolo Bonzini 52449ab747fSPaolo Bonzini k->vmsd = &vmstate_pl181; 52549ab747fSPaolo Bonzini k->reset = pl181_reset; 526*26c607b8SPhilippe Mathieu-Daudé /* Reason: output IRQs should be wired up */ 527e90f2a8cSEduardo Habkost k->user_creatable = false; 52849ab747fSPaolo Bonzini } 52949ab747fSPaolo Bonzini 53049ab747fSPaolo Bonzini static const TypeInfo pl181_info = { 531630f4442SAndreas Färber .name = TYPE_PL181, 53249ab747fSPaolo Bonzini .parent = TYPE_SYS_BUS_DEVICE, 5331d998d93SAndreas Färber .instance_size = sizeof(PL181State), 5340d554cb0Sxiaoqiang zhao .instance_init = pl181_init, 53549ab747fSPaolo Bonzini .class_init = pl181_class_init, 53649ab747fSPaolo Bonzini }; 53749ab747fSPaolo Bonzini 5382762eed1SPhilippe Mathieu-Daudé static void pl181_bus_class_init(ObjectClass *klass, void *data) 5392762eed1SPhilippe Mathieu-Daudé { 5402762eed1SPhilippe Mathieu-Daudé SDBusClass *sbc = SD_BUS_CLASS(klass); 5412762eed1SPhilippe Mathieu-Daudé 5422762eed1SPhilippe Mathieu-Daudé sbc->set_inserted = pl181_set_inserted; 5432762eed1SPhilippe Mathieu-Daudé sbc->set_readonly = pl181_set_readonly; 5442762eed1SPhilippe Mathieu-Daudé } 5452762eed1SPhilippe Mathieu-Daudé 5462762eed1SPhilippe Mathieu-Daudé static const TypeInfo pl181_bus_info = { 5472762eed1SPhilippe Mathieu-Daudé .name = TYPE_PL181_BUS, 5482762eed1SPhilippe Mathieu-Daudé .parent = TYPE_SD_BUS, 5492762eed1SPhilippe Mathieu-Daudé .instance_size = sizeof(SDBus), 5502762eed1SPhilippe Mathieu-Daudé .class_init = pl181_bus_class_init, 5512762eed1SPhilippe Mathieu-Daudé }; 5522762eed1SPhilippe Mathieu-Daudé 55349ab747fSPaolo Bonzini static void pl181_register_types(void) 55449ab747fSPaolo Bonzini { 55549ab747fSPaolo Bonzini type_register_static(&pl181_info); 5562762eed1SPhilippe Mathieu-Daudé type_register_static(&pl181_bus_info); 55749ab747fSPaolo Bonzini } 55849ab747fSPaolo Bonzini 55949ab747fSPaolo Bonzini type_init(pl181_register_types) 560