1a1bb27b1Spbrook /* 2a1bb27b1Spbrook * Arm PrimeCell PL181 MultiMedia Card Interface 3a1bb27b1Spbrook * 4a1bb27b1Spbrook * Copyright (c) 2007 CodeSourcery. 5a1bb27b1Spbrook * Written by Paul Brook 6a1bb27b1Spbrook * 78e31bf38SMatthew Fernandez * This code is licensed under the GPL. 8a1bb27b1Spbrook */ 9a1bb27b1Spbrook 108ef94f0bSPeter Maydell #include "qemu/osdep.h" 119c17d615SPaolo Bonzini #include "sysemu/blockdev.h" 1283c9f4caSPaolo 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" 20583d09f0SPhilippe Mathieu-Daudé #include "trace.h" 21*db1015e9SEduardo Habkost #include "qom/object.h" 22a1bb27b1Spbrook 23a1bb27b1Spbrook #define PL181_FIFO_LEN 16 24a1bb27b1Spbrook 25630f4442SAndreas Färber #define TYPE_PL181 "pl181" 26*db1015e9SEduardo Habkost typedef struct PL181State PL181State; 27630f4442SAndreas Färber #define PL181(obj) OBJECT_CHECK(PL181State, (obj), TYPE_PL181) 28630f4442SAndreas Färber 292762eed1SPhilippe Mathieu-Daudé #define TYPE_PL181_BUS "pl181-bus" 302762eed1SPhilippe Mathieu-Daudé 31*db1015e9SEduardo Habkost struct PL181State { 32630f4442SAndreas Färber SysBusDevice parent_obj; 33630f4442SAndreas Färber 34ca45842aSAvi Kivity MemoryRegion iomem; 352762eed1SPhilippe Mathieu-Daudé SDBus sdbus; 36a1bb27b1Spbrook uint32_t clock; 37a1bb27b1Spbrook uint32_t power; 38a1bb27b1Spbrook uint32_t cmdarg; 39a1bb27b1Spbrook uint32_t cmd; 40a1bb27b1Spbrook uint32_t datatimer; 41a1bb27b1Spbrook uint32_t datalength; 42a1bb27b1Spbrook uint32_t respcmd; 43a1bb27b1Spbrook uint32_t response[4]; 44a1bb27b1Spbrook uint32_t datactrl; 45a1bb27b1Spbrook uint32_t datacnt; 46a1bb27b1Spbrook uint32_t status; 47a1bb27b1Spbrook uint32_t mask[2]; 48624923beSPeter Maydell int32_t fifo_pos; 49624923beSPeter Maydell int32_t fifo_len; 506361cdb6Spbrook /* The linux 2.6.21 driver is buggy, and misbehaves if new data arrives 5167cc32ebSVeres Lajos while it is reading the FIFO. We hack around this by deferring 526361cdb6Spbrook subsequent transfers until after the driver polls the status word. 536361cdb6Spbrook http://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=4446/1 546361cdb6Spbrook */ 55624923beSPeter Maydell int32_t linux_hack; 560e33730cSPhilippe Mathieu-Daudé uint32_t fifo[PL181_FIFO_LEN]; /* TODO use Fifo32 */ 57d537cf6cSpbrook qemu_irq irq[2]; 58c31a4724SPeter Maydell /* GPIO outputs for 'card is readonly' and 'card inserted' */ 5926c5b0f4SPhilippe Mathieu-Daudé qemu_irq card_readonly; 6026c5b0f4SPhilippe Mathieu-Daudé qemu_irq card_inserted; 61*db1015e9SEduardo Habkost }; 62a1bb27b1Spbrook 63624923beSPeter Maydell static const VMStateDescription vmstate_pl181 = { 64624923beSPeter Maydell .name = "pl181", 65624923beSPeter Maydell .version_id = 1, 66624923beSPeter Maydell .minimum_version_id = 1, 67624923beSPeter Maydell .fields = (VMStateField[]) { 681d998d93SAndreas Färber VMSTATE_UINT32(clock, PL181State), 691d998d93SAndreas Färber VMSTATE_UINT32(power, PL181State), 701d998d93SAndreas Färber VMSTATE_UINT32(cmdarg, PL181State), 711d998d93SAndreas Färber VMSTATE_UINT32(cmd, PL181State), 721d998d93SAndreas Färber VMSTATE_UINT32(datatimer, PL181State), 731d998d93SAndreas Färber VMSTATE_UINT32(datalength, PL181State), 741d998d93SAndreas Färber VMSTATE_UINT32(respcmd, PL181State), 751d998d93SAndreas Färber VMSTATE_UINT32_ARRAY(response, PL181State, 4), 761d998d93SAndreas Färber VMSTATE_UINT32(datactrl, PL181State), 771d998d93SAndreas Färber VMSTATE_UINT32(datacnt, PL181State), 781d998d93SAndreas Färber VMSTATE_UINT32(status, PL181State), 791d998d93SAndreas Färber VMSTATE_UINT32_ARRAY(mask, PL181State, 2), 801d998d93SAndreas Färber VMSTATE_INT32(fifo_pos, PL181State), 811d998d93SAndreas Färber VMSTATE_INT32(fifo_len, PL181State), 821d998d93SAndreas Färber VMSTATE_INT32(linux_hack, PL181State), 831d998d93SAndreas Färber VMSTATE_UINT32_ARRAY(fifo, PL181State, PL181_FIFO_LEN), 84624923beSPeter Maydell VMSTATE_END_OF_LIST() 85624923beSPeter Maydell } 86624923beSPeter Maydell }; 87624923beSPeter Maydell 88a1bb27b1Spbrook #define PL181_CMD_INDEX 0x3f 89a1bb27b1Spbrook #define PL181_CMD_RESPONSE (1 << 6) 90a1bb27b1Spbrook #define PL181_CMD_LONGRESP (1 << 7) 91a1bb27b1Spbrook #define PL181_CMD_INTERRUPT (1 << 8) 92a1bb27b1Spbrook #define PL181_CMD_PENDING (1 << 9) 93a1bb27b1Spbrook #define PL181_CMD_ENABLE (1 << 10) 94a1bb27b1Spbrook 95a1bb27b1Spbrook #define PL181_DATA_ENABLE (1 << 0) 96a1bb27b1Spbrook #define PL181_DATA_DIRECTION (1 << 1) 97a1bb27b1Spbrook #define PL181_DATA_MODE (1 << 2) 98a1bb27b1Spbrook #define PL181_DATA_DMAENABLE (1 << 3) 99a1bb27b1Spbrook 100a1bb27b1Spbrook #define PL181_STATUS_CMDCRCFAIL (1 << 0) 101a1bb27b1Spbrook #define PL181_STATUS_DATACRCFAIL (1 << 1) 102a1bb27b1Spbrook #define PL181_STATUS_CMDTIMEOUT (1 << 2) 103a1bb27b1Spbrook #define PL181_STATUS_DATATIMEOUT (1 << 3) 104a1bb27b1Spbrook #define PL181_STATUS_TXUNDERRUN (1 << 4) 105a1bb27b1Spbrook #define PL181_STATUS_RXOVERRUN (1 << 5) 106a1bb27b1Spbrook #define PL181_STATUS_CMDRESPEND (1 << 6) 107a1bb27b1Spbrook #define PL181_STATUS_CMDSENT (1 << 7) 108a1bb27b1Spbrook #define PL181_STATUS_DATAEND (1 << 8) 109a1bb27b1Spbrook #define PL181_STATUS_DATABLOCKEND (1 << 10) 110a1bb27b1Spbrook #define PL181_STATUS_CMDACTIVE (1 << 11) 111a1bb27b1Spbrook #define PL181_STATUS_TXACTIVE (1 << 12) 112a1bb27b1Spbrook #define PL181_STATUS_RXACTIVE (1 << 13) 113a1bb27b1Spbrook #define PL181_STATUS_TXFIFOHALFEMPTY (1 << 14) 114a1bb27b1Spbrook #define PL181_STATUS_RXFIFOHALFFULL (1 << 15) 115a1bb27b1Spbrook #define PL181_STATUS_TXFIFOFULL (1 << 16) 116a1bb27b1Spbrook #define PL181_STATUS_RXFIFOFULL (1 << 17) 117a1bb27b1Spbrook #define PL181_STATUS_TXFIFOEMPTY (1 << 18) 118a1bb27b1Spbrook #define PL181_STATUS_RXFIFOEMPTY (1 << 19) 119a1bb27b1Spbrook #define PL181_STATUS_TXDATAAVLBL (1 << 20) 120a1bb27b1Spbrook #define PL181_STATUS_RXDATAAVLBL (1 << 21) 121a1bb27b1Spbrook 122a1bb27b1Spbrook #define PL181_STATUS_TX_FIFO (PL181_STATUS_TXACTIVE \ 123a1bb27b1Spbrook |PL181_STATUS_TXFIFOHALFEMPTY \ 124a1bb27b1Spbrook |PL181_STATUS_TXFIFOFULL \ 125a1bb27b1Spbrook |PL181_STATUS_TXFIFOEMPTY \ 126a1bb27b1Spbrook |PL181_STATUS_TXDATAAVLBL) 127a1bb27b1Spbrook #define PL181_STATUS_RX_FIFO (PL181_STATUS_RXACTIVE \ 128a1bb27b1Spbrook |PL181_STATUS_RXFIFOHALFFULL \ 129a1bb27b1Spbrook |PL181_STATUS_RXFIFOFULL \ 130a1bb27b1Spbrook |PL181_STATUS_RXFIFOEMPTY \ 131a1bb27b1Spbrook |PL181_STATUS_RXDATAAVLBL) 132a1bb27b1Spbrook 133a1bb27b1Spbrook static const unsigned char pl181_id[] = 134a1bb27b1Spbrook { 0x81, 0x11, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; 135a1bb27b1Spbrook 1361d998d93SAndreas Färber static void pl181_update(PL181State *s) 137a1bb27b1Spbrook { 138a1bb27b1Spbrook int i; 139a1bb27b1Spbrook for (i = 0; i < 2; i++) { 140d537cf6cSpbrook qemu_set_irq(s->irq[i], (s->status & s->mask[i]) != 0); 141a1bb27b1Spbrook } 142a1bb27b1Spbrook } 143a1bb27b1Spbrook 1441d998d93SAndreas Färber static void pl181_fifo_push(PL181State *s, uint32_t value) 145a1bb27b1Spbrook { 146a1bb27b1Spbrook int n; 147a1bb27b1Spbrook 148a1bb27b1Spbrook if (s->fifo_len == PL181_FIFO_LEN) { 1494858e256SAlistair Francis error_report("%s: FIFO overflow", __func__); 150a1bb27b1Spbrook return; 151a1bb27b1Spbrook } 152a1bb27b1Spbrook n = (s->fifo_pos + s->fifo_len) & (PL181_FIFO_LEN - 1); 153a1bb27b1Spbrook s->fifo_len++; 154a1bb27b1Spbrook s->fifo[n] = value; 155583d09f0SPhilippe Mathieu-Daudé trace_pl181_fifo_push(value); 156a1bb27b1Spbrook } 157a1bb27b1Spbrook 1581d998d93SAndreas Färber static uint32_t pl181_fifo_pop(PL181State *s) 159a1bb27b1Spbrook { 160a1bb27b1Spbrook uint32_t value; 161a1bb27b1Spbrook 162a1bb27b1Spbrook if (s->fifo_len == 0) { 1634858e256SAlistair Francis error_report("%s: FIFO underflow", __func__); 164a1bb27b1Spbrook return 0; 165a1bb27b1Spbrook } 166a1bb27b1Spbrook value = s->fifo[s->fifo_pos]; 167a1bb27b1Spbrook s->fifo_len--; 168a1bb27b1Spbrook s->fifo_pos = (s->fifo_pos + 1) & (PL181_FIFO_LEN - 1); 169583d09f0SPhilippe Mathieu-Daudé trace_pl181_fifo_pop(value); 170a1bb27b1Spbrook return value; 171a1bb27b1Spbrook } 172a1bb27b1Spbrook 173b67cd8f5SPhilippe Mathieu-Daudé static void pl181_do_command(PL181State *s) 174a1bb27b1Spbrook { 175bc24a225SPaul Brook SDRequest request; 176a1bb27b1Spbrook uint8_t response[16]; 177a1bb27b1Spbrook int rlen; 178a1bb27b1Spbrook 179a1bb27b1Spbrook request.cmd = s->cmd & PL181_CMD_INDEX; 180a1bb27b1Spbrook request.arg = s->cmdarg; 181583d09f0SPhilippe Mathieu-Daudé trace_pl181_command_send(request.cmd, request.arg); 1822762eed1SPhilippe Mathieu-Daudé rlen = sdbus_do_command(&s->sdbus, &request, response); 183a1bb27b1Spbrook if (rlen < 0) 184a1bb27b1Spbrook goto error; 185a1bb27b1Spbrook if (s->cmd & PL181_CMD_RESPONSE) { 186a1bb27b1Spbrook if (rlen == 0 || (rlen == 4 && (s->cmd & PL181_CMD_LONGRESP))) 187a1bb27b1Spbrook goto error; 188a1bb27b1Spbrook if (rlen != 4 && rlen != 16) 189a1bb27b1Spbrook goto error; 190b3141c06SPhilippe Mathieu-Daudé s->response[0] = ldl_be_p(&response[0]); 191a1bb27b1Spbrook if (rlen == 4) { 192a1bb27b1Spbrook s->response[1] = s->response[2] = s->response[3] = 0; 193a1bb27b1Spbrook } else { 194b3141c06SPhilippe Mathieu-Daudé s->response[1] = ldl_be_p(&response[4]); 195b3141c06SPhilippe Mathieu-Daudé s->response[2] = ldl_be_p(&response[8]); 196b3141c06SPhilippe Mathieu-Daudé s->response[3] = ldl_be_p(&response[12]) & ~1; 197a1bb27b1Spbrook } 198583d09f0SPhilippe Mathieu-Daudé trace_pl181_command_response_pending(); 199a1bb27b1Spbrook s->status |= PL181_STATUS_CMDRESPEND; 200a1bb27b1Spbrook } else { 201583d09f0SPhilippe Mathieu-Daudé trace_pl181_command_sent(); 202a1bb27b1Spbrook s->status |= PL181_STATUS_CMDSENT; 203a1bb27b1Spbrook } 204a1bb27b1Spbrook return; 205a1bb27b1Spbrook 206a1bb27b1Spbrook error: 207583d09f0SPhilippe Mathieu-Daudé trace_pl181_command_timeout(); 208a1bb27b1Spbrook s->status |= PL181_STATUS_CMDTIMEOUT; 209a1bb27b1Spbrook } 210a1bb27b1Spbrook 211aa1f17c1Sths /* Transfer data between the card and the FIFO. This is complicated by 212a1bb27b1Spbrook the FIFO holding 32-bit words and the card taking data in single byte 213a1bb27b1Spbrook chunks. FIFO bytes are transferred in little-endian order. */ 214a1bb27b1Spbrook 2151d998d93SAndreas Färber static void pl181_fifo_run(PL181State *s) 216a1bb27b1Spbrook { 217a1bb27b1Spbrook uint32_t bits; 218f21126dfSBlue Swirl uint32_t value = 0; 219a1bb27b1Spbrook int n; 220a1bb27b1Spbrook int is_read; 221a1bb27b1Spbrook 222a1bb27b1Spbrook is_read = (s->datactrl & PL181_DATA_DIRECTION) != 0; 2232762eed1SPhilippe Mathieu-Daudé if (s->datacnt != 0 && (!is_read || sdbus_data_ready(&s->sdbus)) 2246361cdb6Spbrook && !s->linux_hack) { 225bc3b26f5SPaul Brook if (is_read) { 226a1bb27b1Spbrook n = 0; 227bc3b26f5SPaul Brook while (s->datacnt && s->fifo_len < PL181_FIFO_LEN) { 2288467f622SPhilippe Mathieu-Daudé value |= (uint32_t)sdbus_read_byte(&s->sdbus) << (n * 8); 229bc3b26f5SPaul Brook s->datacnt--; 230a1bb27b1Spbrook n++; 231a1bb27b1Spbrook if (n == 4) { 232a1bb27b1Spbrook pl181_fifo_push(s, value); 233a1bb27b1Spbrook n = 0; 234bc3b26f5SPaul Brook value = 0; 235a1bb27b1Spbrook } 236bc3b26f5SPaul Brook } 237bc3b26f5SPaul Brook if (n != 0) { 238bc3b26f5SPaul Brook pl181_fifo_push(s, value); 239bc3b26f5SPaul Brook } 240bc3b26f5SPaul Brook } else { /* write */ 241bc3b26f5SPaul Brook n = 0; 242bc3b26f5SPaul Brook while (s->datacnt > 0 && (s->fifo_len > 0 || n > 0)) { 243a1bb27b1Spbrook if (n == 0) { 244a1bb27b1Spbrook value = pl181_fifo_pop(s); 245a1bb27b1Spbrook n = 4; 246a1bb27b1Spbrook } 247bc3b26f5SPaul Brook n--; 248bc3b26f5SPaul Brook s->datacnt--; 24939017143SPhilippe Mathieu-Daudé sdbus_write_byte(&s->sdbus, value & 0xff); 250a1bb27b1Spbrook value >>= 8; 251a1bb27b1Spbrook } 252a1bb27b1Spbrook } 253a1bb27b1Spbrook } 254a1bb27b1Spbrook s->status &= ~(PL181_STATUS_RX_FIFO | PL181_STATUS_TX_FIFO); 255a1bb27b1Spbrook if (s->datacnt == 0) { 256a1bb27b1Spbrook s->status |= PL181_STATUS_DATAEND; 257a1bb27b1Spbrook /* HACK: */ 258a1bb27b1Spbrook s->status |= PL181_STATUS_DATABLOCKEND; 259583d09f0SPhilippe Mathieu-Daudé trace_pl181_fifo_transfer_complete(); 260a1bb27b1Spbrook } 2616361cdb6Spbrook if (s->datacnt == 0 && s->fifo_len == 0) { 262a1bb27b1Spbrook s->datactrl &= ~PL181_DATA_ENABLE; 263583d09f0SPhilippe Mathieu-Daudé trace_pl181_data_engine_idle(); 264a1bb27b1Spbrook } else { 265a1bb27b1Spbrook /* Update FIFO bits. */ 266a1bb27b1Spbrook bits = PL181_STATUS_TXACTIVE | PL181_STATUS_RXACTIVE; 267a1bb27b1Spbrook if (s->fifo_len == 0) { 268a1bb27b1Spbrook bits |= PL181_STATUS_TXFIFOEMPTY; 269a1bb27b1Spbrook bits |= PL181_STATUS_RXFIFOEMPTY; 270a1bb27b1Spbrook } else { 271a1bb27b1Spbrook bits |= PL181_STATUS_TXDATAAVLBL; 272a1bb27b1Spbrook bits |= PL181_STATUS_RXDATAAVLBL; 273a1bb27b1Spbrook } 274a1bb27b1Spbrook if (s->fifo_len == 16) { 275a1bb27b1Spbrook bits |= PL181_STATUS_TXFIFOFULL; 276a1bb27b1Spbrook bits |= PL181_STATUS_RXFIFOFULL; 277a1bb27b1Spbrook } 278a1bb27b1Spbrook if (s->fifo_len <= 8) { 279a1bb27b1Spbrook bits |= PL181_STATUS_TXFIFOHALFEMPTY; 280a1bb27b1Spbrook } 281a1bb27b1Spbrook if (s->fifo_len >= 8) { 282a1bb27b1Spbrook bits |= PL181_STATUS_RXFIFOHALFFULL; 283a1bb27b1Spbrook } 284a1bb27b1Spbrook if (s->datactrl & PL181_DATA_DIRECTION) { 285a1bb27b1Spbrook bits &= PL181_STATUS_RX_FIFO; 286a1bb27b1Spbrook } else { 287a1bb27b1Spbrook bits &= PL181_STATUS_TX_FIFO; 288a1bb27b1Spbrook } 289a1bb27b1Spbrook s->status |= bits; 290a1bb27b1Spbrook } 291a1bb27b1Spbrook } 292a1bb27b1Spbrook 293a8170e5eSAvi Kivity static uint64_t pl181_read(void *opaque, hwaddr offset, 294ca45842aSAvi Kivity unsigned size) 295a1bb27b1Spbrook { 2961d998d93SAndreas Färber PL181State *s = (PL181State *)opaque; 2976361cdb6Spbrook uint32_t tmp; 298a1bb27b1Spbrook 299a1bb27b1Spbrook if (offset >= 0xfe0 && offset < 0x1000) { 300a1bb27b1Spbrook return pl181_id[(offset - 0xfe0) >> 2]; 301a1bb27b1Spbrook } 302a1bb27b1Spbrook switch (offset) { 303a1bb27b1Spbrook case 0x00: /* Power */ 304a1bb27b1Spbrook return s->power; 305a1bb27b1Spbrook case 0x04: /* Clock */ 306a1bb27b1Spbrook return s->clock; 307a1bb27b1Spbrook case 0x08: /* Argument */ 308a1bb27b1Spbrook return s->cmdarg; 309a1bb27b1Spbrook case 0x0c: /* Command */ 310a1bb27b1Spbrook return s->cmd; 311a1bb27b1Spbrook case 0x10: /* RespCmd */ 312a1bb27b1Spbrook return s->respcmd; 313a1bb27b1Spbrook case 0x14: /* Response0 */ 314a1bb27b1Spbrook return s->response[0]; 315a1bb27b1Spbrook case 0x18: /* Response1 */ 316a1bb27b1Spbrook return s->response[1]; 317a1bb27b1Spbrook case 0x1c: /* Response2 */ 318a1bb27b1Spbrook return s->response[2]; 319a1bb27b1Spbrook case 0x20: /* Response3 */ 320a1bb27b1Spbrook return s->response[3]; 321a1bb27b1Spbrook case 0x24: /* DataTimer */ 322a1bb27b1Spbrook return s->datatimer; 323a1bb27b1Spbrook case 0x28: /* DataLength */ 324a1bb27b1Spbrook return s->datalength; 325a1bb27b1Spbrook case 0x2c: /* DataCtrl */ 326a1bb27b1Spbrook return s->datactrl; 327a1bb27b1Spbrook case 0x30: /* DataCnt */ 328a1bb27b1Spbrook return s->datacnt; 329a1bb27b1Spbrook case 0x34: /* Status */ 3306361cdb6Spbrook tmp = s->status; 3316361cdb6Spbrook if (s->linux_hack) { 3326361cdb6Spbrook s->linux_hack = 0; 3336361cdb6Spbrook pl181_fifo_run(s); 3346361cdb6Spbrook pl181_update(s); 3356361cdb6Spbrook } 3366361cdb6Spbrook return tmp; 337a1bb27b1Spbrook case 0x3c: /* Mask0 */ 338a1bb27b1Spbrook return s->mask[0]; 339a1bb27b1Spbrook case 0x40: /* Mask1 */ 340a1bb27b1Spbrook return s->mask[1]; 341a1bb27b1Spbrook case 0x48: /* FifoCnt */ 3426361cdb6Spbrook /* The documentation is somewhat vague about exactly what FifoCnt 3436361cdb6Spbrook does. On real hardware it appears to be when decrememnted 34466a0a2cbSDong Xu Wang when a word is transferred between the FIFO and the serial 3456361cdb6Spbrook data engine. DataCnt is decremented after each byte is 34666a0a2cbSDong Xu Wang transferred between the serial engine and the card. 3476361cdb6Spbrook We don't emulate this level of detail, so both can be the same. */ 3486361cdb6Spbrook tmp = (s->datacnt + 3) >> 2; 3496361cdb6Spbrook if (s->linux_hack) { 3506361cdb6Spbrook s->linux_hack = 0; 3516361cdb6Spbrook pl181_fifo_run(s); 3526361cdb6Spbrook pl181_update(s); 3536361cdb6Spbrook } 3546361cdb6Spbrook return tmp; 355a1bb27b1Spbrook case 0x80: case 0x84: case 0x88: case 0x8c: /* FifoData */ 356a1bb27b1Spbrook case 0x90: case 0x94: case 0x98: case 0x9c: 357a1bb27b1Spbrook case 0xa0: case 0xa4: case 0xa8: case 0xac: 358a1bb27b1Spbrook case 0xb0: case 0xb4: case 0xb8: case 0xbc: 3596361cdb6Spbrook if (s->fifo_len == 0) { 3609351d708SPeter Maydell qemu_log_mask(LOG_GUEST_ERROR, "pl181: Unexpected FIFO read\n"); 361a1bb27b1Spbrook return 0; 362a1bb27b1Spbrook } else { 363a1bb27b1Spbrook uint32_t value; 364a1bb27b1Spbrook value = pl181_fifo_pop(s); 3656361cdb6Spbrook s->linux_hack = 1; 366a1bb27b1Spbrook pl181_fifo_run(s); 367a1bb27b1Spbrook pl181_update(s); 368a1bb27b1Spbrook return value; 369a1bb27b1Spbrook } 370a1bb27b1Spbrook default: 3719351d708SPeter Maydell qemu_log_mask(LOG_GUEST_ERROR, 3729351d708SPeter Maydell "pl181_read: Bad offset %x\n", (int)offset); 373a1bb27b1Spbrook return 0; 374a1bb27b1Spbrook } 375a1bb27b1Spbrook } 376a1bb27b1Spbrook 377a8170e5eSAvi Kivity static void pl181_write(void *opaque, hwaddr offset, 378ca45842aSAvi Kivity uint64_t value, unsigned size) 379a1bb27b1Spbrook { 3801d998d93SAndreas Färber PL181State *s = (PL181State *)opaque; 381a1bb27b1Spbrook 382a1bb27b1Spbrook switch (offset) { 383a1bb27b1Spbrook case 0x00: /* Power */ 384a1bb27b1Spbrook s->power = value & 0xff; 385a1bb27b1Spbrook break; 386a1bb27b1Spbrook case 0x04: /* Clock */ 387a1bb27b1Spbrook s->clock = value & 0xff; 388a1bb27b1Spbrook break; 389a1bb27b1Spbrook case 0x08: /* Argument */ 390a1bb27b1Spbrook s->cmdarg = value; 391a1bb27b1Spbrook break; 392a1bb27b1Spbrook case 0x0c: /* Command */ 393a1bb27b1Spbrook s->cmd = value; 394a1bb27b1Spbrook if (s->cmd & PL181_CMD_ENABLE) { 395a1bb27b1Spbrook if (s->cmd & PL181_CMD_INTERRUPT) { 3969351d708SPeter Maydell qemu_log_mask(LOG_UNIMP, 3979351d708SPeter Maydell "pl181: Interrupt mode not implemented\n"); 398a1bb27b1Spbrook } if (s->cmd & PL181_CMD_PENDING) { 3999351d708SPeter Maydell qemu_log_mask(LOG_UNIMP, 4009351d708SPeter Maydell "pl181: Pending commands not implemented\n"); 401a1bb27b1Spbrook } else { 402b67cd8f5SPhilippe Mathieu-Daudé pl181_do_command(s); 403a1bb27b1Spbrook pl181_fifo_run(s); 404a1bb27b1Spbrook } 405a1bb27b1Spbrook /* The command has completed one way or the other. */ 406a1bb27b1Spbrook s->cmd &= ~PL181_CMD_ENABLE; 407a1bb27b1Spbrook } 408a1bb27b1Spbrook break; 409a1bb27b1Spbrook case 0x24: /* DataTimer */ 410a1bb27b1Spbrook s->datatimer = value; 411a1bb27b1Spbrook break; 412a1bb27b1Spbrook case 0x28: /* DataLength */ 413a1bb27b1Spbrook s->datalength = value & 0xffff; 414a1bb27b1Spbrook break; 415a1bb27b1Spbrook case 0x2c: /* DataCtrl */ 416a1bb27b1Spbrook s->datactrl = value & 0xff; 417a1bb27b1Spbrook if (value & PL181_DATA_ENABLE) { 418a1bb27b1Spbrook s->datacnt = s->datalength; 419a1bb27b1Spbrook pl181_fifo_run(s); 420a1bb27b1Spbrook } 421a1bb27b1Spbrook break; 422a1bb27b1Spbrook case 0x38: /* Clear */ 423a1bb27b1Spbrook s->status &= ~(value & 0x7ff); 424a1bb27b1Spbrook break; 425a1bb27b1Spbrook case 0x3c: /* Mask0 */ 426a1bb27b1Spbrook s->mask[0] = value; 427a1bb27b1Spbrook break; 428a1bb27b1Spbrook case 0x40: /* Mask1 */ 429a1bb27b1Spbrook s->mask[1] = value; 430a1bb27b1Spbrook break; 431a1bb27b1Spbrook case 0x80: case 0x84: case 0x88: case 0x8c: /* FifoData */ 432a1bb27b1Spbrook case 0x90: case 0x94: case 0x98: case 0x9c: 433a1bb27b1Spbrook case 0xa0: case 0xa4: case 0xa8: case 0xac: 434a1bb27b1Spbrook case 0xb0: case 0xb4: case 0xb8: case 0xbc: 4356361cdb6Spbrook if (s->datacnt == 0) { 4369351d708SPeter Maydell qemu_log_mask(LOG_GUEST_ERROR, "pl181: Unexpected FIFO write\n"); 437a1bb27b1Spbrook } else { 438a1bb27b1Spbrook pl181_fifo_push(s, value); 439a1bb27b1Spbrook pl181_fifo_run(s); 440a1bb27b1Spbrook } 441a1bb27b1Spbrook break; 442a1bb27b1Spbrook default: 4439351d708SPeter Maydell qemu_log_mask(LOG_GUEST_ERROR, 4449351d708SPeter Maydell "pl181_write: Bad offset %x\n", (int)offset); 445a1bb27b1Spbrook } 446a1bb27b1Spbrook pl181_update(s); 447a1bb27b1Spbrook } 448a1bb27b1Spbrook 449ca45842aSAvi Kivity static const MemoryRegionOps pl181_ops = { 450ca45842aSAvi Kivity .read = pl181_read, 451ca45842aSAvi Kivity .write = pl181_write, 452ca45842aSAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 453a1bb27b1Spbrook }; 454a1bb27b1Spbrook 4552762eed1SPhilippe Mathieu-Daudé static void pl181_set_readonly(DeviceState *dev, bool level) 4562762eed1SPhilippe Mathieu-Daudé { 4572762eed1SPhilippe Mathieu-Daudé PL181State *s = (PL181State *)dev; 4582762eed1SPhilippe Mathieu-Daudé 4592762eed1SPhilippe Mathieu-Daudé qemu_set_irq(s->card_readonly, level); 4602762eed1SPhilippe Mathieu-Daudé } 4612762eed1SPhilippe Mathieu-Daudé 4622762eed1SPhilippe Mathieu-Daudé static void pl181_set_inserted(DeviceState *dev, bool level) 4632762eed1SPhilippe Mathieu-Daudé { 4642762eed1SPhilippe Mathieu-Daudé PL181State *s = (PL181State *)dev; 4652762eed1SPhilippe Mathieu-Daudé 4662762eed1SPhilippe Mathieu-Daudé qemu_set_irq(s->card_inserted, level); 4672762eed1SPhilippe Mathieu-Daudé } 4682762eed1SPhilippe Mathieu-Daudé 469624923beSPeter Maydell static void pl181_reset(DeviceState *d) 470a1bb27b1Spbrook { 471630f4442SAndreas Färber PL181State *s = PL181(d); 472a1bb27b1Spbrook 473a1bb27b1Spbrook s->power = 0; 474a1bb27b1Spbrook s->cmdarg = 0; 475a1bb27b1Spbrook s->cmd = 0; 476a1bb27b1Spbrook s->datatimer = 0; 477a1bb27b1Spbrook s->datalength = 0; 478a1bb27b1Spbrook s->respcmd = 0; 479a1bb27b1Spbrook s->response[0] = 0; 480a1bb27b1Spbrook s->response[1] = 0; 481a1bb27b1Spbrook s->response[2] = 0; 482a1bb27b1Spbrook s->response[3] = 0; 483a1bb27b1Spbrook s->datatimer = 0; 484a1bb27b1Spbrook s->datalength = 0; 485a1bb27b1Spbrook s->datactrl = 0; 486a1bb27b1Spbrook s->datacnt = 0; 487a1bb27b1Spbrook s->status = 0; 4886361cdb6Spbrook s->linux_hack = 0; 489a1bb27b1Spbrook s->mask[0] = 0; 490a1bb27b1Spbrook s->mask[1] = 0; 491c31a4724SPeter Maydell 4922762eed1SPhilippe Mathieu-Daudé /* Reset other state based on current card insertion/readonly status */ 4932762eed1SPhilippe Mathieu-Daudé pl181_set_inserted(DEVICE(s), sdbus_get_inserted(&s->sdbus)); 4942762eed1SPhilippe Mathieu-Daudé pl181_set_readonly(DEVICE(s), sdbus_get_readonly(&s->sdbus)); 495a1bb27b1Spbrook } 496a1bb27b1Spbrook 4970d554cb0Sxiaoqiang zhao static void pl181_init(Object *obj) 498a1bb27b1Spbrook { 4990d554cb0Sxiaoqiang zhao DeviceState *dev = DEVICE(obj); 5000d554cb0Sxiaoqiang zhao PL181State *s = PL181(obj); 5010d554cb0Sxiaoqiang zhao SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 502a1bb27b1Spbrook 5030d554cb0Sxiaoqiang zhao memory_region_init_io(&s->iomem, obj, &pl181_ops, s, "pl181", 0x1000); 504630f4442SAndreas Färber sysbus_init_mmio(sbd, &s->iomem); 505630f4442SAndreas Färber sysbus_init_irq(sbd, &s->irq[0]); 506630f4442SAndreas Färber sysbus_init_irq(sbd, &s->irq[1]); 50726c5b0f4SPhilippe Mathieu-Daudé qdev_init_gpio_out_named(dev, &s->card_readonly, "card-read-only", 1); 50826c5b0f4SPhilippe Mathieu-Daudé qdev_init_gpio_out_named(dev, &s->card_inserted, "card-inserted", 1); 5092762eed1SPhilippe Mathieu-Daudé 5102762eed1SPhilippe Mathieu-Daudé qbus_create_inplace(&s->sdbus, sizeof(s->sdbus), 5112762eed1SPhilippe Mathieu-Daudé TYPE_PL181_BUS, dev, "sd-bus"); 5120d554cb0Sxiaoqiang zhao } 5130d554cb0Sxiaoqiang zhao 514999e12bbSAnthony Liguori static void pl181_class_init(ObjectClass *klass, void *data) 515999e12bbSAnthony Liguori { 51639bffca2SAnthony Liguori DeviceClass *k = DEVICE_CLASS(klass); 517999e12bbSAnthony Liguori 51839bffca2SAnthony Liguori k->vmsd = &vmstate_pl181; 51939bffca2SAnthony Liguori k->reset = pl181_reset; 52026c607b8SPhilippe Mathieu-Daudé /* Reason: output IRQs should be wired up */ 521e90f2a8cSEduardo Habkost k->user_creatable = false; 522999e12bbSAnthony Liguori } 523999e12bbSAnthony Liguori 5248c43a6f0SAndreas Färber static const TypeInfo pl181_info = { 525630f4442SAndreas Färber .name = TYPE_PL181, 52639bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 5271d998d93SAndreas Färber .instance_size = sizeof(PL181State), 5280d554cb0Sxiaoqiang zhao .instance_init = pl181_init, 529999e12bbSAnthony Liguori .class_init = pl181_class_init, 530624923beSPeter Maydell }; 531624923beSPeter Maydell 5322762eed1SPhilippe Mathieu-Daudé static void pl181_bus_class_init(ObjectClass *klass, void *data) 5332762eed1SPhilippe Mathieu-Daudé { 5342762eed1SPhilippe Mathieu-Daudé SDBusClass *sbc = SD_BUS_CLASS(klass); 5352762eed1SPhilippe Mathieu-Daudé 5362762eed1SPhilippe Mathieu-Daudé sbc->set_inserted = pl181_set_inserted; 5372762eed1SPhilippe Mathieu-Daudé sbc->set_readonly = pl181_set_readonly; 5382762eed1SPhilippe Mathieu-Daudé } 5392762eed1SPhilippe Mathieu-Daudé 5402762eed1SPhilippe Mathieu-Daudé static const TypeInfo pl181_bus_info = { 5412762eed1SPhilippe Mathieu-Daudé .name = TYPE_PL181_BUS, 5422762eed1SPhilippe Mathieu-Daudé .parent = TYPE_SD_BUS, 5432762eed1SPhilippe Mathieu-Daudé .instance_size = sizeof(SDBus), 5442762eed1SPhilippe Mathieu-Daudé .class_init = pl181_bus_class_init, 5452762eed1SPhilippe Mathieu-Daudé }; 5462762eed1SPhilippe Mathieu-Daudé 54783f7d43aSAndreas Färber static void pl181_register_types(void) 548aa9311d8SPaul Brook { 54939bffca2SAnthony Liguori type_register_static(&pl181_info); 5502762eed1SPhilippe Mathieu-Daudé type_register_static(&pl181_bus_info); 551aa9311d8SPaul Brook } 552aa9311d8SPaul Brook 55383f7d43aSAndreas Färber type_init(pl181_register_types) 554