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" 11fa1d36dfSMarkus Armbruster #include "sysemu/block-backend.h" 129c17d615SPaolo Bonzini #include "sysemu/blockdev.h" 1383c9f4caSPaolo Bonzini #include "hw/sysbus.h" 14e3382ef0SSai Pavan Boddu #include "hw/sd/sd.h" 1503dd024fSPaolo Bonzini #include "qemu/log.h" 160d554cb0Sxiaoqiang zhao #include "qapi/error.h" 17a1bb27b1Spbrook 18a1bb27b1Spbrook //#define DEBUG_PL181 1 19a1bb27b1Spbrook 20a1bb27b1Spbrook #ifdef DEBUG_PL181 21001faf32SBlue Swirl #define DPRINTF(fmt, ...) \ 22001faf32SBlue Swirl do { printf("pl181: " fmt , ## __VA_ARGS__); } while (0) 23a1bb27b1Spbrook #else 24001faf32SBlue Swirl #define DPRINTF(fmt, ...) do {} while(0) 25a1bb27b1Spbrook #endif 26a1bb27b1Spbrook 27a1bb27b1Spbrook #define PL181_FIFO_LEN 16 28a1bb27b1Spbrook 29630f4442SAndreas Färber #define TYPE_PL181 "pl181" 30630f4442SAndreas Färber #define PL181(obj) OBJECT_CHECK(PL181State, (obj), TYPE_PL181) 31630f4442SAndreas Färber 321d998d93SAndreas Färber typedef struct PL181State { 33630f4442SAndreas Färber SysBusDevice parent_obj; 34630f4442SAndreas Färber 35ca45842aSAvi Kivity MemoryRegion iomem; 3642a10898Spbrook SDState *card; 37a1bb27b1Spbrook uint32_t clock; 38a1bb27b1Spbrook uint32_t power; 39a1bb27b1Spbrook uint32_t cmdarg; 40a1bb27b1Spbrook uint32_t cmd; 41a1bb27b1Spbrook uint32_t datatimer; 42a1bb27b1Spbrook uint32_t datalength; 43a1bb27b1Spbrook uint32_t respcmd; 44a1bb27b1Spbrook uint32_t response[4]; 45a1bb27b1Spbrook uint32_t datactrl; 46a1bb27b1Spbrook uint32_t datacnt; 47a1bb27b1Spbrook uint32_t status; 48a1bb27b1Spbrook uint32_t mask[2]; 49624923beSPeter Maydell int32_t fifo_pos; 50624923beSPeter Maydell int32_t fifo_len; 516361cdb6Spbrook /* The linux 2.6.21 driver is buggy, and misbehaves if new data arrives 5267cc32ebSVeres Lajos while it is reading the FIFO. We hack around this by deferring 536361cdb6Spbrook subsequent transfers until after the driver polls the status word. 546361cdb6Spbrook http://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=4446/1 556361cdb6Spbrook */ 56624923beSPeter Maydell int32_t linux_hack; 57a1bb27b1Spbrook uint32_t fifo[PL181_FIFO_LEN]; 58d537cf6cSpbrook qemu_irq irq[2]; 59c31a4724SPeter Maydell /* GPIO outputs for 'card is readonly' and 'card inserted' */ 60c31a4724SPeter Maydell qemu_irq cardstatus[2]; 611d998d93SAndreas Färber } PL181State; 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) { 149a1bb27b1Spbrook fprintf(stderr, "pl181: FIFO overflow\n"); 150a1bb27b1Spbrook return; 151a1bb27b1Spbrook } 152a1bb27b1Spbrook n = (s->fifo_pos + s->fifo_len) & (PL181_FIFO_LEN - 1); 153a1bb27b1Spbrook s->fifo_len++; 154a1bb27b1Spbrook s->fifo[n] = value; 155a1bb27b1Spbrook DPRINTF("FIFO push %08x\n", (int)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) { 163a1bb27b1Spbrook fprintf(stderr, "pl181: FIFO underflow\n"); 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); 169a1bb27b1Spbrook DPRINTF("FIFO pop %08x\n", (int)value); 170a1bb27b1Spbrook return value; 171a1bb27b1Spbrook } 172a1bb27b1Spbrook 1731d998d93SAndreas Färber static void pl181_send_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; 181a1bb27b1Spbrook DPRINTF("Command %d %08x\n", request.cmd, request.arg); 182a1bb27b1Spbrook rlen = sd_do_command(s->card, &request, response); 183a1bb27b1Spbrook if (rlen < 0) 184a1bb27b1Spbrook goto error; 185a1bb27b1Spbrook if (s->cmd & PL181_CMD_RESPONSE) { 1868827b0fbSPeter Maydell #define RWORD(n) (((uint32_t)response[n] << 24) | (response[n + 1] << 16) \ 187a1bb27b1Spbrook | (response[n + 2] << 8) | response[n + 3]) 188a1bb27b1Spbrook if (rlen == 0 || (rlen == 4 && (s->cmd & PL181_CMD_LONGRESP))) 189a1bb27b1Spbrook goto error; 190a1bb27b1Spbrook if (rlen != 4 && rlen != 16) 191a1bb27b1Spbrook goto error; 192a1bb27b1Spbrook s->response[0] = RWORD(0); 193a1bb27b1Spbrook if (rlen == 4) { 194a1bb27b1Spbrook s->response[1] = s->response[2] = s->response[3] = 0; 195a1bb27b1Spbrook } else { 196a1bb27b1Spbrook s->response[1] = RWORD(4); 197a1bb27b1Spbrook s->response[2] = RWORD(8); 198a1bb27b1Spbrook s->response[3] = RWORD(12) & ~1; 199a1bb27b1Spbrook } 200aa1f17c1Sths DPRINTF("Response received\n"); 201a1bb27b1Spbrook s->status |= PL181_STATUS_CMDRESPEND; 202a1bb27b1Spbrook #undef RWORD 203a1bb27b1Spbrook } else { 204a1bb27b1Spbrook DPRINTF("Command sent\n"); 205a1bb27b1Spbrook s->status |= PL181_STATUS_CMDSENT; 206a1bb27b1Spbrook } 207a1bb27b1Spbrook return; 208a1bb27b1Spbrook 209a1bb27b1Spbrook error: 210a1bb27b1Spbrook DPRINTF("Timeout\n"); 211a1bb27b1Spbrook s->status |= PL181_STATUS_CMDTIMEOUT; 212a1bb27b1Spbrook } 213a1bb27b1Spbrook 214aa1f17c1Sths /* Transfer data between the card and the FIFO. This is complicated by 215a1bb27b1Spbrook the FIFO holding 32-bit words and the card taking data in single byte 216a1bb27b1Spbrook chunks. FIFO bytes are transferred in little-endian order. */ 217a1bb27b1Spbrook 2181d998d93SAndreas Färber static void pl181_fifo_run(PL181State *s) 219a1bb27b1Spbrook { 220a1bb27b1Spbrook uint32_t bits; 221f21126dfSBlue Swirl uint32_t value = 0; 222a1bb27b1Spbrook int n; 223a1bb27b1Spbrook int is_read; 224a1bb27b1Spbrook 225a1bb27b1Spbrook is_read = (s->datactrl & PL181_DATA_DIRECTION) != 0; 2266361cdb6Spbrook if (s->datacnt != 0 && (!is_read || sd_data_ready(s->card)) 2276361cdb6Spbrook && !s->linux_hack) { 228bc3b26f5SPaul Brook if (is_read) { 229a1bb27b1Spbrook n = 0; 230bc3b26f5SPaul Brook while (s->datacnt && s->fifo_len < PL181_FIFO_LEN) { 231a1bb27b1Spbrook value |= (uint32_t)sd_read_data(s->card) << (n * 8); 232bc3b26f5SPaul Brook s->datacnt--; 233a1bb27b1Spbrook n++; 234a1bb27b1Spbrook if (n == 4) { 235a1bb27b1Spbrook pl181_fifo_push(s, value); 236a1bb27b1Spbrook n = 0; 237bc3b26f5SPaul Brook value = 0; 238a1bb27b1Spbrook } 239bc3b26f5SPaul Brook } 240bc3b26f5SPaul Brook if (n != 0) { 241bc3b26f5SPaul Brook pl181_fifo_push(s, value); 242bc3b26f5SPaul Brook } 243bc3b26f5SPaul Brook } else { /* write */ 244bc3b26f5SPaul Brook n = 0; 245bc3b26f5SPaul Brook while (s->datacnt > 0 && (s->fifo_len > 0 || n > 0)) { 246a1bb27b1Spbrook if (n == 0) { 247a1bb27b1Spbrook value = pl181_fifo_pop(s); 248a1bb27b1Spbrook n = 4; 249a1bb27b1Spbrook } 250bc3b26f5SPaul Brook n--; 251bc3b26f5SPaul Brook s->datacnt--; 252a1bb27b1Spbrook sd_write_data(s->card, value & 0xff); 253a1bb27b1Spbrook value >>= 8; 254a1bb27b1Spbrook } 255a1bb27b1Spbrook } 256a1bb27b1Spbrook } 257a1bb27b1Spbrook s->status &= ~(PL181_STATUS_RX_FIFO | PL181_STATUS_TX_FIFO); 258a1bb27b1Spbrook if (s->datacnt == 0) { 259a1bb27b1Spbrook s->status |= PL181_STATUS_DATAEND; 260a1bb27b1Spbrook /* HACK: */ 261a1bb27b1Spbrook s->status |= PL181_STATUS_DATABLOCKEND; 262a1bb27b1Spbrook DPRINTF("Transfer Complete\n"); 263a1bb27b1Spbrook } 2646361cdb6Spbrook if (s->datacnt == 0 && s->fifo_len == 0) { 265a1bb27b1Spbrook s->datactrl &= ~PL181_DATA_ENABLE; 266a1bb27b1Spbrook DPRINTF("Data engine idle\n"); 267a1bb27b1Spbrook } else { 268a1bb27b1Spbrook /* Update FIFO bits. */ 269a1bb27b1Spbrook bits = PL181_STATUS_TXACTIVE | PL181_STATUS_RXACTIVE; 270a1bb27b1Spbrook if (s->fifo_len == 0) { 271a1bb27b1Spbrook bits |= PL181_STATUS_TXFIFOEMPTY; 272a1bb27b1Spbrook bits |= PL181_STATUS_RXFIFOEMPTY; 273a1bb27b1Spbrook } else { 274a1bb27b1Spbrook bits |= PL181_STATUS_TXDATAAVLBL; 275a1bb27b1Spbrook bits |= PL181_STATUS_RXDATAAVLBL; 276a1bb27b1Spbrook } 277a1bb27b1Spbrook if (s->fifo_len == 16) { 278a1bb27b1Spbrook bits |= PL181_STATUS_TXFIFOFULL; 279a1bb27b1Spbrook bits |= PL181_STATUS_RXFIFOFULL; 280a1bb27b1Spbrook } 281a1bb27b1Spbrook if (s->fifo_len <= 8) { 282a1bb27b1Spbrook bits |= PL181_STATUS_TXFIFOHALFEMPTY; 283a1bb27b1Spbrook } 284a1bb27b1Spbrook if (s->fifo_len >= 8) { 285a1bb27b1Spbrook bits |= PL181_STATUS_RXFIFOHALFFULL; 286a1bb27b1Spbrook } 287a1bb27b1Spbrook if (s->datactrl & PL181_DATA_DIRECTION) { 288a1bb27b1Spbrook bits &= PL181_STATUS_RX_FIFO; 289a1bb27b1Spbrook } else { 290a1bb27b1Spbrook bits &= PL181_STATUS_TX_FIFO; 291a1bb27b1Spbrook } 292a1bb27b1Spbrook s->status |= bits; 293a1bb27b1Spbrook } 294a1bb27b1Spbrook } 295a1bb27b1Spbrook 296a8170e5eSAvi Kivity static uint64_t pl181_read(void *opaque, hwaddr offset, 297ca45842aSAvi Kivity unsigned size) 298a1bb27b1Spbrook { 2991d998d93SAndreas Färber PL181State *s = (PL181State *)opaque; 3006361cdb6Spbrook uint32_t tmp; 301a1bb27b1Spbrook 302a1bb27b1Spbrook if (offset >= 0xfe0 && offset < 0x1000) { 303a1bb27b1Spbrook return pl181_id[(offset - 0xfe0) >> 2]; 304a1bb27b1Spbrook } 305a1bb27b1Spbrook switch (offset) { 306a1bb27b1Spbrook case 0x00: /* Power */ 307a1bb27b1Spbrook return s->power; 308a1bb27b1Spbrook case 0x04: /* Clock */ 309a1bb27b1Spbrook return s->clock; 310a1bb27b1Spbrook case 0x08: /* Argument */ 311a1bb27b1Spbrook return s->cmdarg; 312a1bb27b1Spbrook case 0x0c: /* Command */ 313a1bb27b1Spbrook return s->cmd; 314a1bb27b1Spbrook case 0x10: /* RespCmd */ 315a1bb27b1Spbrook return s->respcmd; 316a1bb27b1Spbrook case 0x14: /* Response0 */ 317a1bb27b1Spbrook return s->response[0]; 318a1bb27b1Spbrook case 0x18: /* Response1 */ 319a1bb27b1Spbrook return s->response[1]; 320a1bb27b1Spbrook case 0x1c: /* Response2 */ 321a1bb27b1Spbrook return s->response[2]; 322a1bb27b1Spbrook case 0x20: /* Response3 */ 323a1bb27b1Spbrook return s->response[3]; 324a1bb27b1Spbrook case 0x24: /* DataTimer */ 325a1bb27b1Spbrook return s->datatimer; 326a1bb27b1Spbrook case 0x28: /* DataLength */ 327a1bb27b1Spbrook return s->datalength; 328a1bb27b1Spbrook case 0x2c: /* DataCtrl */ 329a1bb27b1Spbrook return s->datactrl; 330a1bb27b1Spbrook case 0x30: /* DataCnt */ 331a1bb27b1Spbrook return s->datacnt; 332a1bb27b1Spbrook case 0x34: /* Status */ 3336361cdb6Spbrook tmp = s->status; 3346361cdb6Spbrook if (s->linux_hack) { 3356361cdb6Spbrook s->linux_hack = 0; 3366361cdb6Spbrook pl181_fifo_run(s); 3376361cdb6Spbrook pl181_update(s); 3386361cdb6Spbrook } 3396361cdb6Spbrook return tmp; 340a1bb27b1Spbrook case 0x3c: /* Mask0 */ 341a1bb27b1Spbrook return s->mask[0]; 342a1bb27b1Spbrook case 0x40: /* Mask1 */ 343a1bb27b1Spbrook return s->mask[1]; 344a1bb27b1Spbrook case 0x48: /* FifoCnt */ 3456361cdb6Spbrook /* The documentation is somewhat vague about exactly what FifoCnt 3466361cdb6Spbrook does. On real hardware it appears to be when decrememnted 34766a0a2cbSDong Xu Wang when a word is transferred between the FIFO and the serial 3486361cdb6Spbrook data engine. DataCnt is decremented after each byte is 34966a0a2cbSDong Xu Wang transferred between the serial engine and the card. 3506361cdb6Spbrook We don't emulate this level of detail, so both can be the same. */ 3516361cdb6Spbrook tmp = (s->datacnt + 3) >> 2; 3526361cdb6Spbrook if (s->linux_hack) { 3536361cdb6Spbrook s->linux_hack = 0; 3546361cdb6Spbrook pl181_fifo_run(s); 3556361cdb6Spbrook pl181_update(s); 3566361cdb6Spbrook } 3576361cdb6Spbrook return tmp; 358a1bb27b1Spbrook case 0x80: case 0x84: case 0x88: case 0x8c: /* FifoData */ 359a1bb27b1Spbrook case 0x90: case 0x94: case 0x98: case 0x9c: 360a1bb27b1Spbrook case 0xa0: case 0xa4: case 0xa8: case 0xac: 361a1bb27b1Spbrook case 0xb0: case 0xb4: case 0xb8: case 0xbc: 3626361cdb6Spbrook if (s->fifo_len == 0) { 3639351d708SPeter Maydell qemu_log_mask(LOG_GUEST_ERROR, "pl181: Unexpected FIFO read\n"); 364a1bb27b1Spbrook return 0; 365a1bb27b1Spbrook } else { 366a1bb27b1Spbrook uint32_t value; 367a1bb27b1Spbrook value = pl181_fifo_pop(s); 3686361cdb6Spbrook s->linux_hack = 1; 369a1bb27b1Spbrook pl181_fifo_run(s); 370a1bb27b1Spbrook pl181_update(s); 371a1bb27b1Spbrook return value; 372a1bb27b1Spbrook } 373a1bb27b1Spbrook default: 3749351d708SPeter Maydell qemu_log_mask(LOG_GUEST_ERROR, 3759351d708SPeter Maydell "pl181_read: Bad offset %x\n", (int)offset); 376a1bb27b1Spbrook return 0; 377a1bb27b1Spbrook } 378a1bb27b1Spbrook } 379a1bb27b1Spbrook 380a8170e5eSAvi Kivity static void pl181_write(void *opaque, hwaddr offset, 381ca45842aSAvi Kivity uint64_t value, unsigned size) 382a1bb27b1Spbrook { 3831d998d93SAndreas Färber PL181State *s = (PL181State *)opaque; 384a1bb27b1Spbrook 385a1bb27b1Spbrook switch (offset) { 386a1bb27b1Spbrook case 0x00: /* Power */ 387a1bb27b1Spbrook s->power = value & 0xff; 388a1bb27b1Spbrook break; 389a1bb27b1Spbrook case 0x04: /* Clock */ 390a1bb27b1Spbrook s->clock = value & 0xff; 391a1bb27b1Spbrook break; 392a1bb27b1Spbrook case 0x08: /* Argument */ 393a1bb27b1Spbrook s->cmdarg = value; 394a1bb27b1Spbrook break; 395a1bb27b1Spbrook case 0x0c: /* Command */ 396a1bb27b1Spbrook s->cmd = value; 397a1bb27b1Spbrook if (s->cmd & PL181_CMD_ENABLE) { 398a1bb27b1Spbrook if (s->cmd & PL181_CMD_INTERRUPT) { 3999351d708SPeter Maydell qemu_log_mask(LOG_UNIMP, 4009351d708SPeter Maydell "pl181: Interrupt mode not implemented\n"); 401a1bb27b1Spbrook } if (s->cmd & PL181_CMD_PENDING) { 4029351d708SPeter Maydell qemu_log_mask(LOG_UNIMP, 4039351d708SPeter Maydell "pl181: Pending commands not implemented\n"); 404a1bb27b1Spbrook } else { 405a1bb27b1Spbrook pl181_send_command(s); 406a1bb27b1Spbrook pl181_fifo_run(s); 407a1bb27b1Spbrook } 408a1bb27b1Spbrook /* The command has completed one way or the other. */ 409a1bb27b1Spbrook s->cmd &= ~PL181_CMD_ENABLE; 410a1bb27b1Spbrook } 411a1bb27b1Spbrook break; 412a1bb27b1Spbrook case 0x24: /* DataTimer */ 413a1bb27b1Spbrook s->datatimer = value; 414a1bb27b1Spbrook break; 415a1bb27b1Spbrook case 0x28: /* DataLength */ 416a1bb27b1Spbrook s->datalength = value & 0xffff; 417a1bb27b1Spbrook break; 418a1bb27b1Spbrook case 0x2c: /* DataCtrl */ 419a1bb27b1Spbrook s->datactrl = value & 0xff; 420a1bb27b1Spbrook if (value & PL181_DATA_ENABLE) { 421a1bb27b1Spbrook s->datacnt = s->datalength; 422a1bb27b1Spbrook pl181_fifo_run(s); 423a1bb27b1Spbrook } 424a1bb27b1Spbrook break; 425a1bb27b1Spbrook case 0x38: /* Clear */ 426a1bb27b1Spbrook s->status &= ~(value & 0x7ff); 427a1bb27b1Spbrook break; 428a1bb27b1Spbrook case 0x3c: /* Mask0 */ 429a1bb27b1Spbrook s->mask[0] = value; 430a1bb27b1Spbrook break; 431a1bb27b1Spbrook case 0x40: /* Mask1 */ 432a1bb27b1Spbrook s->mask[1] = value; 433a1bb27b1Spbrook break; 434a1bb27b1Spbrook case 0x80: case 0x84: case 0x88: case 0x8c: /* FifoData */ 435a1bb27b1Spbrook case 0x90: case 0x94: case 0x98: case 0x9c: 436a1bb27b1Spbrook case 0xa0: case 0xa4: case 0xa8: case 0xac: 437a1bb27b1Spbrook case 0xb0: case 0xb4: case 0xb8: case 0xbc: 4386361cdb6Spbrook if (s->datacnt == 0) { 4399351d708SPeter Maydell qemu_log_mask(LOG_GUEST_ERROR, "pl181: Unexpected FIFO write\n"); 440a1bb27b1Spbrook } else { 441a1bb27b1Spbrook pl181_fifo_push(s, value); 442a1bb27b1Spbrook pl181_fifo_run(s); 443a1bb27b1Spbrook } 444a1bb27b1Spbrook break; 445a1bb27b1Spbrook default: 4469351d708SPeter Maydell qemu_log_mask(LOG_GUEST_ERROR, 4479351d708SPeter Maydell "pl181_write: Bad offset %x\n", (int)offset); 448a1bb27b1Spbrook } 449a1bb27b1Spbrook pl181_update(s); 450a1bb27b1Spbrook } 451a1bb27b1Spbrook 452ca45842aSAvi Kivity static const MemoryRegionOps pl181_ops = { 453ca45842aSAvi Kivity .read = pl181_read, 454ca45842aSAvi Kivity .write = pl181_write, 455ca45842aSAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 456a1bb27b1Spbrook }; 457a1bb27b1Spbrook 458624923beSPeter Maydell static void pl181_reset(DeviceState *d) 459a1bb27b1Spbrook { 460630f4442SAndreas Färber PL181State *s = PL181(d); 461a1bb27b1Spbrook 462a1bb27b1Spbrook s->power = 0; 463a1bb27b1Spbrook s->cmdarg = 0; 464a1bb27b1Spbrook s->cmd = 0; 465a1bb27b1Spbrook s->datatimer = 0; 466a1bb27b1Spbrook s->datalength = 0; 467a1bb27b1Spbrook s->respcmd = 0; 468a1bb27b1Spbrook s->response[0] = 0; 469a1bb27b1Spbrook s->response[1] = 0; 470a1bb27b1Spbrook s->response[2] = 0; 471a1bb27b1Spbrook s->response[3] = 0; 472a1bb27b1Spbrook s->datatimer = 0; 473a1bb27b1Spbrook s->datalength = 0; 474a1bb27b1Spbrook s->datactrl = 0; 475a1bb27b1Spbrook s->datacnt = 0; 476a1bb27b1Spbrook s->status = 0; 4776361cdb6Spbrook s->linux_hack = 0; 478a1bb27b1Spbrook s->mask[0] = 0; 479a1bb27b1Spbrook s->mask[1] = 0; 480c31a4724SPeter Maydell 481c31a4724SPeter Maydell /* We can assume our GPIO outputs have been wired up now */ 482c31a4724SPeter Maydell sd_set_cb(s->card, s->cardstatus[0], s->cardstatus[1]); 483a1bb27b1Spbrook } 484a1bb27b1Spbrook 4850d554cb0Sxiaoqiang zhao static void pl181_init(Object *obj) 486a1bb27b1Spbrook { 4870d554cb0Sxiaoqiang zhao DeviceState *dev = DEVICE(obj); 4880d554cb0Sxiaoqiang zhao PL181State *s = PL181(obj); 4890d554cb0Sxiaoqiang zhao SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 490a1bb27b1Spbrook 4910d554cb0Sxiaoqiang zhao memory_region_init_io(&s->iomem, obj, &pl181_ops, s, "pl181", 0x1000); 492630f4442SAndreas Färber sysbus_init_mmio(sbd, &s->iomem); 493630f4442SAndreas Färber sysbus_init_irq(sbd, &s->irq[0]); 494630f4442SAndreas Färber sysbus_init_irq(sbd, &s->irq[1]); 495630f4442SAndreas Färber qdev_init_gpio_out(dev, s->cardstatus, 2); 4960d554cb0Sxiaoqiang zhao } 4970d554cb0Sxiaoqiang zhao 4980d554cb0Sxiaoqiang zhao static void pl181_realize(DeviceState *dev, Error **errp) 4990d554cb0Sxiaoqiang zhao { 5000d554cb0Sxiaoqiang zhao PL181State *s = PL181(dev); 5010d554cb0Sxiaoqiang zhao DriveInfo *dinfo; 5020d554cb0Sxiaoqiang zhao 503af9e40aaSMarkus Armbruster /* FIXME use a qdev drive property instead of drive_get_next() */ 50413839974SMarkus Armbruster dinfo = drive_get_next(IF_SD); 5054be74634SMarkus Armbruster s->card = sd_init(dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, false); 5064f8a066bSKevin Wolf if (s->card == NULL) { 5070d554cb0Sxiaoqiang zhao error_setg(errp, "sd_init failed"); 5084f8a066bSKevin Wolf } 509a1bb27b1Spbrook } 510aa9311d8SPaul Brook 511999e12bbSAnthony Liguori static void pl181_class_init(ObjectClass *klass, void *data) 512999e12bbSAnthony Liguori { 51339bffca2SAnthony Liguori DeviceClass *k = DEVICE_CLASS(klass); 514999e12bbSAnthony Liguori 51539bffca2SAnthony Liguori k->vmsd = &vmstate_pl181; 51639bffca2SAnthony Liguori k->reset = pl181_reset; 5179f9bdf43SMarkus Armbruster /* Reason: init() method uses drive_get_next() */ 518*e90f2a8cSEduardo Habkost k->user_creatable = false; 5190d554cb0Sxiaoqiang zhao k->realize = pl181_realize; 520999e12bbSAnthony Liguori } 521999e12bbSAnthony Liguori 5228c43a6f0SAndreas Färber static const TypeInfo pl181_info = { 523630f4442SAndreas Färber .name = TYPE_PL181, 52439bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 5251d998d93SAndreas Färber .instance_size = sizeof(PL181State), 5260d554cb0Sxiaoqiang zhao .instance_init = pl181_init, 527999e12bbSAnthony Liguori .class_init = pl181_class_init, 528624923beSPeter Maydell }; 529624923beSPeter Maydell 53083f7d43aSAndreas Färber static void pl181_register_types(void) 531aa9311d8SPaul Brook { 53239bffca2SAnthony Liguori type_register_static(&pl181_info); 533aa9311d8SPaul Brook } 534aa9311d8SPaul Brook 53583f7d43aSAndreas Färber type_init(pl181_register_types) 536