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 109c17d615SPaolo Bonzini #include "sysemu/blockdev.h" 1183c9f4caSPaolo Bonzini #include "hw/sysbus.h" 1283c9f4caSPaolo Bonzini #include "hw/sd.h" 13a1bb27b1Spbrook 14a1bb27b1Spbrook //#define DEBUG_PL181 1 15a1bb27b1Spbrook 16a1bb27b1Spbrook #ifdef DEBUG_PL181 17001faf32SBlue Swirl #define DPRINTF(fmt, ...) \ 18001faf32SBlue Swirl do { printf("pl181: " fmt , ## __VA_ARGS__); } while (0) 19a1bb27b1Spbrook #else 20001faf32SBlue Swirl #define DPRINTF(fmt, ...) do {} while(0) 21a1bb27b1Spbrook #endif 22a1bb27b1Spbrook 23a1bb27b1Spbrook #define PL181_FIFO_LEN 16 24a1bb27b1Spbrook 25630f4442SAndreas Färber #define TYPE_PL181 "pl181" 26630f4442SAndreas Färber #define PL181(obj) OBJECT_CHECK(PL181State, (obj), TYPE_PL181) 27630f4442SAndreas Färber 281d998d93SAndreas Färber typedef struct PL181State { 29630f4442SAndreas Färber SysBusDevice parent_obj; 30630f4442SAndreas Färber 31ca45842aSAvi Kivity MemoryRegion iomem; 3242a10898Spbrook SDState *card; 33a1bb27b1Spbrook uint32_t clock; 34a1bb27b1Spbrook uint32_t power; 35a1bb27b1Spbrook uint32_t cmdarg; 36a1bb27b1Spbrook uint32_t cmd; 37a1bb27b1Spbrook uint32_t datatimer; 38a1bb27b1Spbrook uint32_t datalength; 39a1bb27b1Spbrook uint32_t respcmd; 40a1bb27b1Spbrook uint32_t response[4]; 41a1bb27b1Spbrook uint32_t datactrl; 42a1bb27b1Spbrook uint32_t datacnt; 43a1bb27b1Spbrook uint32_t status; 44a1bb27b1Spbrook uint32_t mask[2]; 45624923beSPeter Maydell int32_t fifo_pos; 46624923beSPeter Maydell int32_t fifo_len; 476361cdb6Spbrook /* The linux 2.6.21 driver is buggy, and misbehaves if new data arrives 486361cdb6Spbrook while it is reading the FIFO. We hack around this be defering 496361cdb6Spbrook subsequent transfers until after the driver polls the status word. 506361cdb6Spbrook http://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=4446/1 516361cdb6Spbrook */ 52624923beSPeter Maydell int32_t linux_hack; 53a1bb27b1Spbrook uint32_t fifo[PL181_FIFO_LEN]; 54d537cf6cSpbrook qemu_irq irq[2]; 55c31a4724SPeter Maydell /* GPIO outputs for 'card is readonly' and 'card inserted' */ 56c31a4724SPeter Maydell qemu_irq cardstatus[2]; 571d998d93SAndreas Färber } PL181State; 58a1bb27b1Spbrook 59624923beSPeter Maydell static const VMStateDescription vmstate_pl181 = { 60624923beSPeter Maydell .name = "pl181", 61624923beSPeter Maydell .version_id = 1, 62624923beSPeter Maydell .minimum_version_id = 1, 63624923beSPeter Maydell .fields = (VMStateField[]) { 641d998d93SAndreas Färber VMSTATE_UINT32(clock, PL181State), 651d998d93SAndreas Färber VMSTATE_UINT32(power, PL181State), 661d998d93SAndreas Färber VMSTATE_UINT32(cmdarg, PL181State), 671d998d93SAndreas Färber VMSTATE_UINT32(cmd, PL181State), 681d998d93SAndreas Färber VMSTATE_UINT32(datatimer, PL181State), 691d998d93SAndreas Färber VMSTATE_UINT32(datalength, PL181State), 701d998d93SAndreas Färber VMSTATE_UINT32(respcmd, PL181State), 711d998d93SAndreas Färber VMSTATE_UINT32_ARRAY(response, PL181State, 4), 721d998d93SAndreas Färber VMSTATE_UINT32(datactrl, PL181State), 731d998d93SAndreas Färber VMSTATE_UINT32(datacnt, PL181State), 741d998d93SAndreas Färber VMSTATE_UINT32(status, PL181State), 751d998d93SAndreas Färber VMSTATE_UINT32_ARRAY(mask, PL181State, 2), 761d998d93SAndreas Färber VMSTATE_INT32(fifo_pos, PL181State), 771d998d93SAndreas Färber VMSTATE_INT32(fifo_len, PL181State), 781d998d93SAndreas Färber VMSTATE_INT32(linux_hack, PL181State), 791d998d93SAndreas Färber VMSTATE_UINT32_ARRAY(fifo, PL181State, PL181_FIFO_LEN), 80624923beSPeter Maydell VMSTATE_END_OF_LIST() 81624923beSPeter Maydell } 82624923beSPeter Maydell }; 83624923beSPeter Maydell 84a1bb27b1Spbrook #define PL181_CMD_INDEX 0x3f 85a1bb27b1Spbrook #define PL181_CMD_RESPONSE (1 << 6) 86a1bb27b1Spbrook #define PL181_CMD_LONGRESP (1 << 7) 87a1bb27b1Spbrook #define PL181_CMD_INTERRUPT (1 << 8) 88a1bb27b1Spbrook #define PL181_CMD_PENDING (1 << 9) 89a1bb27b1Spbrook #define PL181_CMD_ENABLE (1 << 10) 90a1bb27b1Spbrook 91a1bb27b1Spbrook #define PL181_DATA_ENABLE (1 << 0) 92a1bb27b1Spbrook #define PL181_DATA_DIRECTION (1 << 1) 93a1bb27b1Spbrook #define PL181_DATA_MODE (1 << 2) 94a1bb27b1Spbrook #define PL181_DATA_DMAENABLE (1 << 3) 95a1bb27b1Spbrook 96a1bb27b1Spbrook #define PL181_STATUS_CMDCRCFAIL (1 << 0) 97a1bb27b1Spbrook #define PL181_STATUS_DATACRCFAIL (1 << 1) 98a1bb27b1Spbrook #define PL181_STATUS_CMDTIMEOUT (1 << 2) 99a1bb27b1Spbrook #define PL181_STATUS_DATATIMEOUT (1 << 3) 100a1bb27b1Spbrook #define PL181_STATUS_TXUNDERRUN (1 << 4) 101a1bb27b1Spbrook #define PL181_STATUS_RXOVERRUN (1 << 5) 102a1bb27b1Spbrook #define PL181_STATUS_CMDRESPEND (1 << 6) 103a1bb27b1Spbrook #define PL181_STATUS_CMDSENT (1 << 7) 104a1bb27b1Spbrook #define PL181_STATUS_DATAEND (1 << 8) 105a1bb27b1Spbrook #define PL181_STATUS_DATABLOCKEND (1 << 10) 106a1bb27b1Spbrook #define PL181_STATUS_CMDACTIVE (1 << 11) 107a1bb27b1Spbrook #define PL181_STATUS_TXACTIVE (1 << 12) 108a1bb27b1Spbrook #define PL181_STATUS_RXACTIVE (1 << 13) 109a1bb27b1Spbrook #define PL181_STATUS_TXFIFOHALFEMPTY (1 << 14) 110a1bb27b1Spbrook #define PL181_STATUS_RXFIFOHALFFULL (1 << 15) 111a1bb27b1Spbrook #define PL181_STATUS_TXFIFOFULL (1 << 16) 112a1bb27b1Spbrook #define PL181_STATUS_RXFIFOFULL (1 << 17) 113a1bb27b1Spbrook #define PL181_STATUS_TXFIFOEMPTY (1 << 18) 114a1bb27b1Spbrook #define PL181_STATUS_RXFIFOEMPTY (1 << 19) 115a1bb27b1Spbrook #define PL181_STATUS_TXDATAAVLBL (1 << 20) 116a1bb27b1Spbrook #define PL181_STATUS_RXDATAAVLBL (1 << 21) 117a1bb27b1Spbrook 118a1bb27b1Spbrook #define PL181_STATUS_TX_FIFO (PL181_STATUS_TXACTIVE \ 119a1bb27b1Spbrook |PL181_STATUS_TXFIFOHALFEMPTY \ 120a1bb27b1Spbrook |PL181_STATUS_TXFIFOFULL \ 121a1bb27b1Spbrook |PL181_STATUS_TXFIFOEMPTY \ 122a1bb27b1Spbrook |PL181_STATUS_TXDATAAVLBL) 123a1bb27b1Spbrook #define PL181_STATUS_RX_FIFO (PL181_STATUS_RXACTIVE \ 124a1bb27b1Spbrook |PL181_STATUS_RXFIFOHALFFULL \ 125a1bb27b1Spbrook |PL181_STATUS_RXFIFOFULL \ 126a1bb27b1Spbrook |PL181_STATUS_RXFIFOEMPTY \ 127a1bb27b1Spbrook |PL181_STATUS_RXDATAAVLBL) 128a1bb27b1Spbrook 129a1bb27b1Spbrook static const unsigned char pl181_id[] = 130a1bb27b1Spbrook { 0x81, 0x11, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; 131a1bb27b1Spbrook 1321d998d93SAndreas Färber static void pl181_update(PL181State *s) 133a1bb27b1Spbrook { 134a1bb27b1Spbrook int i; 135a1bb27b1Spbrook for (i = 0; i < 2; i++) { 136d537cf6cSpbrook qemu_set_irq(s->irq[i], (s->status & s->mask[i]) != 0); 137a1bb27b1Spbrook } 138a1bb27b1Spbrook } 139a1bb27b1Spbrook 1401d998d93SAndreas Färber static void pl181_fifo_push(PL181State *s, uint32_t value) 141a1bb27b1Spbrook { 142a1bb27b1Spbrook int n; 143a1bb27b1Spbrook 144a1bb27b1Spbrook if (s->fifo_len == PL181_FIFO_LEN) { 145a1bb27b1Spbrook fprintf(stderr, "pl181: FIFO overflow\n"); 146a1bb27b1Spbrook return; 147a1bb27b1Spbrook } 148a1bb27b1Spbrook n = (s->fifo_pos + s->fifo_len) & (PL181_FIFO_LEN - 1); 149a1bb27b1Spbrook s->fifo_len++; 150a1bb27b1Spbrook s->fifo[n] = value; 151a1bb27b1Spbrook DPRINTF("FIFO push %08x\n", (int)value); 152a1bb27b1Spbrook } 153a1bb27b1Spbrook 1541d998d93SAndreas Färber static uint32_t pl181_fifo_pop(PL181State *s) 155a1bb27b1Spbrook { 156a1bb27b1Spbrook uint32_t value; 157a1bb27b1Spbrook 158a1bb27b1Spbrook if (s->fifo_len == 0) { 159a1bb27b1Spbrook fprintf(stderr, "pl181: FIFO underflow\n"); 160a1bb27b1Spbrook return 0; 161a1bb27b1Spbrook } 162a1bb27b1Spbrook value = s->fifo[s->fifo_pos]; 163a1bb27b1Spbrook s->fifo_len--; 164a1bb27b1Spbrook s->fifo_pos = (s->fifo_pos + 1) & (PL181_FIFO_LEN - 1); 165a1bb27b1Spbrook DPRINTF("FIFO pop %08x\n", (int)value); 166a1bb27b1Spbrook return value; 167a1bb27b1Spbrook } 168a1bb27b1Spbrook 1691d998d93SAndreas Färber static void pl181_send_command(PL181State *s) 170a1bb27b1Spbrook { 171bc24a225SPaul Brook SDRequest request; 172a1bb27b1Spbrook uint8_t response[16]; 173a1bb27b1Spbrook int rlen; 174a1bb27b1Spbrook 175a1bb27b1Spbrook request.cmd = s->cmd & PL181_CMD_INDEX; 176a1bb27b1Spbrook request.arg = s->cmdarg; 177a1bb27b1Spbrook DPRINTF("Command %d %08x\n", request.cmd, request.arg); 178a1bb27b1Spbrook rlen = sd_do_command(s->card, &request, response); 179a1bb27b1Spbrook if (rlen < 0) 180a1bb27b1Spbrook goto error; 181a1bb27b1Spbrook if (s->cmd & PL181_CMD_RESPONSE) { 1828827b0fbSPeter Maydell #define RWORD(n) (((uint32_t)response[n] << 24) | (response[n + 1] << 16) \ 183a1bb27b1Spbrook | (response[n + 2] << 8) | response[n + 3]) 184a1bb27b1Spbrook if (rlen == 0 || (rlen == 4 && (s->cmd & PL181_CMD_LONGRESP))) 185a1bb27b1Spbrook goto error; 186a1bb27b1Spbrook if (rlen != 4 && rlen != 16) 187a1bb27b1Spbrook goto error; 188a1bb27b1Spbrook s->response[0] = RWORD(0); 189a1bb27b1Spbrook if (rlen == 4) { 190a1bb27b1Spbrook s->response[1] = s->response[2] = s->response[3] = 0; 191a1bb27b1Spbrook } else { 192a1bb27b1Spbrook s->response[1] = RWORD(4); 193a1bb27b1Spbrook s->response[2] = RWORD(8); 194a1bb27b1Spbrook s->response[3] = RWORD(12) & ~1; 195a1bb27b1Spbrook } 196aa1f17c1Sths DPRINTF("Response received\n"); 197a1bb27b1Spbrook s->status |= PL181_STATUS_CMDRESPEND; 198a1bb27b1Spbrook #undef RWORD 199a1bb27b1Spbrook } else { 200a1bb27b1Spbrook DPRINTF("Command sent\n"); 201a1bb27b1Spbrook s->status |= PL181_STATUS_CMDSENT; 202a1bb27b1Spbrook } 203a1bb27b1Spbrook return; 204a1bb27b1Spbrook 205a1bb27b1Spbrook error: 206a1bb27b1Spbrook DPRINTF("Timeout\n"); 207a1bb27b1Spbrook s->status |= PL181_STATUS_CMDTIMEOUT; 208a1bb27b1Spbrook } 209a1bb27b1Spbrook 210aa1f17c1Sths /* Transfer data between the card and the FIFO. This is complicated by 211a1bb27b1Spbrook the FIFO holding 32-bit words and the card taking data in single byte 212a1bb27b1Spbrook chunks. FIFO bytes are transferred in little-endian order. */ 213a1bb27b1Spbrook 2141d998d93SAndreas Färber static void pl181_fifo_run(PL181State *s) 215a1bb27b1Spbrook { 216a1bb27b1Spbrook uint32_t bits; 217f21126dfSBlue Swirl uint32_t value = 0; 218a1bb27b1Spbrook int n; 219a1bb27b1Spbrook int is_read; 220a1bb27b1Spbrook 221a1bb27b1Spbrook is_read = (s->datactrl & PL181_DATA_DIRECTION) != 0; 2226361cdb6Spbrook if (s->datacnt != 0 && (!is_read || sd_data_ready(s->card)) 2236361cdb6Spbrook && !s->linux_hack) { 224bc3b26f5SPaul Brook if (is_read) { 225a1bb27b1Spbrook n = 0; 226bc3b26f5SPaul Brook while (s->datacnt && s->fifo_len < PL181_FIFO_LEN) { 227a1bb27b1Spbrook value |= (uint32_t)sd_read_data(s->card) << (n * 8); 228bc3b26f5SPaul Brook s->datacnt--; 229a1bb27b1Spbrook n++; 230a1bb27b1Spbrook if (n == 4) { 231a1bb27b1Spbrook pl181_fifo_push(s, value); 232a1bb27b1Spbrook n = 0; 233bc3b26f5SPaul Brook value = 0; 234a1bb27b1Spbrook } 235bc3b26f5SPaul Brook } 236bc3b26f5SPaul Brook if (n != 0) { 237bc3b26f5SPaul Brook pl181_fifo_push(s, value); 238bc3b26f5SPaul Brook } 239bc3b26f5SPaul Brook } else { /* write */ 240bc3b26f5SPaul Brook n = 0; 241bc3b26f5SPaul Brook while (s->datacnt > 0 && (s->fifo_len > 0 || n > 0)) { 242a1bb27b1Spbrook if (n == 0) { 243a1bb27b1Spbrook value = pl181_fifo_pop(s); 244a1bb27b1Spbrook n = 4; 245a1bb27b1Spbrook } 246bc3b26f5SPaul Brook n--; 247bc3b26f5SPaul Brook s->datacnt--; 248a1bb27b1Spbrook sd_write_data(s->card, value & 0xff); 249a1bb27b1Spbrook value >>= 8; 250a1bb27b1Spbrook } 251a1bb27b1Spbrook } 252a1bb27b1Spbrook } 253a1bb27b1Spbrook s->status &= ~(PL181_STATUS_RX_FIFO | PL181_STATUS_TX_FIFO); 254a1bb27b1Spbrook if (s->datacnt == 0) { 255a1bb27b1Spbrook s->status |= PL181_STATUS_DATAEND; 256a1bb27b1Spbrook /* HACK: */ 257a1bb27b1Spbrook s->status |= PL181_STATUS_DATABLOCKEND; 258a1bb27b1Spbrook DPRINTF("Transfer Complete\n"); 259a1bb27b1Spbrook } 2606361cdb6Spbrook if (s->datacnt == 0 && s->fifo_len == 0) { 261a1bb27b1Spbrook s->datactrl &= ~PL181_DATA_ENABLE; 262a1bb27b1Spbrook DPRINTF("Data engine idle\n"); 263a1bb27b1Spbrook } else { 264a1bb27b1Spbrook /* Update FIFO bits. */ 265a1bb27b1Spbrook bits = PL181_STATUS_TXACTIVE | PL181_STATUS_RXACTIVE; 266a1bb27b1Spbrook if (s->fifo_len == 0) { 267a1bb27b1Spbrook bits |= PL181_STATUS_TXFIFOEMPTY; 268a1bb27b1Spbrook bits |= PL181_STATUS_RXFIFOEMPTY; 269a1bb27b1Spbrook } else { 270a1bb27b1Spbrook bits |= PL181_STATUS_TXDATAAVLBL; 271a1bb27b1Spbrook bits |= PL181_STATUS_RXDATAAVLBL; 272a1bb27b1Spbrook } 273a1bb27b1Spbrook if (s->fifo_len == 16) { 274a1bb27b1Spbrook bits |= PL181_STATUS_TXFIFOFULL; 275a1bb27b1Spbrook bits |= PL181_STATUS_RXFIFOFULL; 276a1bb27b1Spbrook } 277a1bb27b1Spbrook if (s->fifo_len <= 8) { 278a1bb27b1Spbrook bits |= PL181_STATUS_TXFIFOHALFEMPTY; 279a1bb27b1Spbrook } 280a1bb27b1Spbrook if (s->fifo_len >= 8) { 281a1bb27b1Spbrook bits |= PL181_STATUS_RXFIFOHALFFULL; 282a1bb27b1Spbrook } 283a1bb27b1Spbrook if (s->datactrl & PL181_DATA_DIRECTION) { 284a1bb27b1Spbrook bits &= PL181_STATUS_RX_FIFO; 285a1bb27b1Spbrook } else { 286a1bb27b1Spbrook bits &= PL181_STATUS_TX_FIFO; 287a1bb27b1Spbrook } 288a1bb27b1Spbrook s->status |= bits; 289a1bb27b1Spbrook } 290a1bb27b1Spbrook } 291a1bb27b1Spbrook 292a8170e5eSAvi Kivity static uint64_t pl181_read(void *opaque, hwaddr offset, 293ca45842aSAvi Kivity unsigned size) 294a1bb27b1Spbrook { 2951d998d93SAndreas Färber PL181State *s = (PL181State *)opaque; 2966361cdb6Spbrook uint32_t tmp; 297a1bb27b1Spbrook 298a1bb27b1Spbrook if (offset >= 0xfe0 && offset < 0x1000) { 299a1bb27b1Spbrook return pl181_id[(offset - 0xfe0) >> 2]; 300a1bb27b1Spbrook } 301a1bb27b1Spbrook switch (offset) { 302a1bb27b1Spbrook case 0x00: /* Power */ 303a1bb27b1Spbrook return s->power; 304a1bb27b1Spbrook case 0x04: /* Clock */ 305a1bb27b1Spbrook return s->clock; 306a1bb27b1Spbrook case 0x08: /* Argument */ 307a1bb27b1Spbrook return s->cmdarg; 308a1bb27b1Spbrook case 0x0c: /* Command */ 309a1bb27b1Spbrook return s->cmd; 310a1bb27b1Spbrook case 0x10: /* RespCmd */ 311a1bb27b1Spbrook return s->respcmd; 312a1bb27b1Spbrook case 0x14: /* Response0 */ 313a1bb27b1Spbrook return s->response[0]; 314a1bb27b1Spbrook case 0x18: /* Response1 */ 315a1bb27b1Spbrook return s->response[1]; 316a1bb27b1Spbrook case 0x1c: /* Response2 */ 317a1bb27b1Spbrook return s->response[2]; 318a1bb27b1Spbrook case 0x20: /* Response3 */ 319a1bb27b1Spbrook return s->response[3]; 320a1bb27b1Spbrook case 0x24: /* DataTimer */ 321a1bb27b1Spbrook return s->datatimer; 322a1bb27b1Spbrook case 0x28: /* DataLength */ 323a1bb27b1Spbrook return s->datalength; 324a1bb27b1Spbrook case 0x2c: /* DataCtrl */ 325a1bb27b1Spbrook return s->datactrl; 326a1bb27b1Spbrook case 0x30: /* DataCnt */ 327a1bb27b1Spbrook return s->datacnt; 328a1bb27b1Spbrook case 0x34: /* Status */ 3296361cdb6Spbrook tmp = s->status; 3306361cdb6Spbrook if (s->linux_hack) { 3316361cdb6Spbrook s->linux_hack = 0; 3326361cdb6Spbrook pl181_fifo_run(s); 3336361cdb6Spbrook pl181_update(s); 3346361cdb6Spbrook } 3356361cdb6Spbrook return tmp; 336a1bb27b1Spbrook case 0x3c: /* Mask0 */ 337a1bb27b1Spbrook return s->mask[0]; 338a1bb27b1Spbrook case 0x40: /* Mask1 */ 339a1bb27b1Spbrook return s->mask[1]; 340a1bb27b1Spbrook case 0x48: /* FifoCnt */ 3416361cdb6Spbrook /* The documentation is somewhat vague about exactly what FifoCnt 3426361cdb6Spbrook does. On real hardware it appears to be when decrememnted 34366a0a2cbSDong Xu Wang when a word is transferred between the FIFO and the serial 3446361cdb6Spbrook data engine. DataCnt is decremented after each byte is 34566a0a2cbSDong Xu Wang transferred between the serial engine and the card. 3466361cdb6Spbrook We don't emulate this level of detail, so both can be the same. */ 3476361cdb6Spbrook tmp = (s->datacnt + 3) >> 2; 3486361cdb6Spbrook if (s->linux_hack) { 3496361cdb6Spbrook s->linux_hack = 0; 3506361cdb6Spbrook pl181_fifo_run(s); 3516361cdb6Spbrook pl181_update(s); 3526361cdb6Spbrook } 3536361cdb6Spbrook return tmp; 354a1bb27b1Spbrook case 0x80: case 0x84: case 0x88: case 0x8c: /* FifoData */ 355a1bb27b1Spbrook case 0x90: case 0x94: case 0x98: case 0x9c: 356a1bb27b1Spbrook case 0xa0: case 0xa4: case 0xa8: case 0xac: 357a1bb27b1Spbrook case 0xb0: case 0xb4: case 0xb8: case 0xbc: 3586361cdb6Spbrook if (s->fifo_len == 0) { 3599351d708SPeter Maydell qemu_log_mask(LOG_GUEST_ERROR, "pl181: Unexpected FIFO read\n"); 360a1bb27b1Spbrook return 0; 361a1bb27b1Spbrook } else { 362a1bb27b1Spbrook uint32_t value; 363a1bb27b1Spbrook value = pl181_fifo_pop(s); 3646361cdb6Spbrook s->linux_hack = 1; 365a1bb27b1Spbrook pl181_fifo_run(s); 366a1bb27b1Spbrook pl181_update(s); 367a1bb27b1Spbrook return value; 368a1bb27b1Spbrook } 369a1bb27b1Spbrook default: 3709351d708SPeter Maydell qemu_log_mask(LOG_GUEST_ERROR, 3719351d708SPeter Maydell "pl181_read: Bad offset %x\n", (int)offset); 372a1bb27b1Spbrook return 0; 373a1bb27b1Spbrook } 374a1bb27b1Spbrook } 375a1bb27b1Spbrook 376a8170e5eSAvi Kivity static void pl181_write(void *opaque, hwaddr offset, 377ca45842aSAvi Kivity uint64_t value, unsigned size) 378a1bb27b1Spbrook { 3791d998d93SAndreas Färber PL181State *s = (PL181State *)opaque; 380a1bb27b1Spbrook 381a1bb27b1Spbrook switch (offset) { 382a1bb27b1Spbrook case 0x00: /* Power */ 383a1bb27b1Spbrook s->power = value & 0xff; 384a1bb27b1Spbrook break; 385a1bb27b1Spbrook case 0x04: /* Clock */ 386a1bb27b1Spbrook s->clock = value & 0xff; 387a1bb27b1Spbrook break; 388a1bb27b1Spbrook case 0x08: /* Argument */ 389a1bb27b1Spbrook s->cmdarg = value; 390a1bb27b1Spbrook break; 391a1bb27b1Spbrook case 0x0c: /* Command */ 392a1bb27b1Spbrook s->cmd = value; 393a1bb27b1Spbrook if (s->cmd & PL181_CMD_ENABLE) { 394a1bb27b1Spbrook if (s->cmd & PL181_CMD_INTERRUPT) { 3959351d708SPeter Maydell qemu_log_mask(LOG_UNIMP, 3969351d708SPeter Maydell "pl181: Interrupt mode not implemented\n"); 397a1bb27b1Spbrook } if (s->cmd & PL181_CMD_PENDING) { 3989351d708SPeter Maydell qemu_log_mask(LOG_UNIMP, 3999351d708SPeter Maydell "pl181: Pending commands not implemented\n"); 400a1bb27b1Spbrook } else { 401a1bb27b1Spbrook pl181_send_command(s); 402a1bb27b1Spbrook pl181_fifo_run(s); 403a1bb27b1Spbrook } 404a1bb27b1Spbrook /* The command has completed one way or the other. */ 405a1bb27b1Spbrook s->cmd &= ~PL181_CMD_ENABLE; 406a1bb27b1Spbrook } 407a1bb27b1Spbrook break; 408a1bb27b1Spbrook case 0x24: /* DataTimer */ 409a1bb27b1Spbrook s->datatimer = value; 410a1bb27b1Spbrook break; 411a1bb27b1Spbrook case 0x28: /* DataLength */ 412a1bb27b1Spbrook s->datalength = value & 0xffff; 413a1bb27b1Spbrook break; 414a1bb27b1Spbrook case 0x2c: /* DataCtrl */ 415a1bb27b1Spbrook s->datactrl = value & 0xff; 416a1bb27b1Spbrook if (value & PL181_DATA_ENABLE) { 417a1bb27b1Spbrook s->datacnt = s->datalength; 418a1bb27b1Spbrook pl181_fifo_run(s); 419a1bb27b1Spbrook } 420a1bb27b1Spbrook break; 421a1bb27b1Spbrook case 0x38: /* Clear */ 422a1bb27b1Spbrook s->status &= ~(value & 0x7ff); 423a1bb27b1Spbrook break; 424a1bb27b1Spbrook case 0x3c: /* Mask0 */ 425a1bb27b1Spbrook s->mask[0] = value; 426a1bb27b1Spbrook break; 427a1bb27b1Spbrook case 0x40: /* Mask1 */ 428a1bb27b1Spbrook s->mask[1] = value; 429a1bb27b1Spbrook break; 430a1bb27b1Spbrook case 0x80: case 0x84: case 0x88: case 0x8c: /* FifoData */ 431a1bb27b1Spbrook case 0x90: case 0x94: case 0x98: case 0x9c: 432a1bb27b1Spbrook case 0xa0: case 0xa4: case 0xa8: case 0xac: 433a1bb27b1Spbrook case 0xb0: case 0xb4: case 0xb8: case 0xbc: 4346361cdb6Spbrook if (s->datacnt == 0) { 4359351d708SPeter Maydell qemu_log_mask(LOG_GUEST_ERROR, "pl181: Unexpected FIFO write\n"); 436a1bb27b1Spbrook } else { 437a1bb27b1Spbrook pl181_fifo_push(s, value); 438a1bb27b1Spbrook pl181_fifo_run(s); 439a1bb27b1Spbrook } 440a1bb27b1Spbrook break; 441a1bb27b1Spbrook default: 4429351d708SPeter Maydell qemu_log_mask(LOG_GUEST_ERROR, 4439351d708SPeter Maydell "pl181_write: Bad offset %x\n", (int)offset); 444a1bb27b1Spbrook } 445a1bb27b1Spbrook pl181_update(s); 446a1bb27b1Spbrook } 447a1bb27b1Spbrook 448ca45842aSAvi Kivity static const MemoryRegionOps pl181_ops = { 449ca45842aSAvi Kivity .read = pl181_read, 450ca45842aSAvi Kivity .write = pl181_write, 451ca45842aSAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 452a1bb27b1Spbrook }; 453a1bb27b1Spbrook 454624923beSPeter Maydell static void pl181_reset(DeviceState *d) 455a1bb27b1Spbrook { 456630f4442SAndreas Färber PL181State *s = PL181(d); 457a1bb27b1Spbrook 458a1bb27b1Spbrook s->power = 0; 459a1bb27b1Spbrook s->cmdarg = 0; 460a1bb27b1Spbrook s->cmd = 0; 461a1bb27b1Spbrook s->datatimer = 0; 462a1bb27b1Spbrook s->datalength = 0; 463a1bb27b1Spbrook s->respcmd = 0; 464a1bb27b1Spbrook s->response[0] = 0; 465a1bb27b1Spbrook s->response[1] = 0; 466a1bb27b1Spbrook s->response[2] = 0; 467a1bb27b1Spbrook s->response[3] = 0; 468a1bb27b1Spbrook s->datatimer = 0; 469a1bb27b1Spbrook s->datalength = 0; 470a1bb27b1Spbrook s->datactrl = 0; 471a1bb27b1Spbrook s->datacnt = 0; 472a1bb27b1Spbrook s->status = 0; 4736361cdb6Spbrook s->linux_hack = 0; 474a1bb27b1Spbrook s->mask[0] = 0; 475a1bb27b1Spbrook s->mask[1] = 0; 476c31a4724SPeter Maydell 477c31a4724SPeter Maydell /* We can assume our GPIO outputs have been wired up now */ 478c31a4724SPeter Maydell sd_set_cb(s->card, s->cardstatus[0], s->cardstatus[1]); 479a1bb27b1Spbrook } 480a1bb27b1Spbrook 481630f4442SAndreas Färber static int pl181_init(SysBusDevice *sbd) 482a1bb27b1Spbrook { 483630f4442SAndreas Färber DeviceState *dev = DEVICE(sbd); 484630f4442SAndreas Färber PL181State *s = PL181(dev); 48513839974SMarkus Armbruster DriveInfo *dinfo; 486a1bb27b1Spbrook 48729776739SPaolo Bonzini memory_region_init_io(&s->iomem, OBJECT(s), &pl181_ops, s, "pl181", 0x1000); 488630f4442SAndreas Färber sysbus_init_mmio(sbd, &s->iomem); 489630f4442SAndreas Färber sysbus_init_irq(sbd, &s->irq[0]); 490630f4442SAndreas Färber sysbus_init_irq(sbd, &s->irq[1]); 491630f4442SAndreas Färber qdev_init_gpio_out(dev, s->cardstatus, 2); 49213839974SMarkus Armbruster dinfo = drive_get_next(IF_SD); 4936790f59dSliguang s->card = sd_init(dinfo ? dinfo->bdrv : NULL, false); 494*4f8a066bSKevin Wolf if (s->card == NULL) { 495*4f8a066bSKevin Wolf return -1; 496*4f8a066bSKevin Wolf } 497*4f8a066bSKevin Wolf 49881a322d4SGerd Hoffmann return 0; 499a1bb27b1Spbrook } 500aa9311d8SPaul Brook 501999e12bbSAnthony Liguori static void pl181_class_init(ObjectClass *klass, void *data) 502999e12bbSAnthony Liguori { 503999e12bbSAnthony Liguori SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); 50439bffca2SAnthony Liguori DeviceClass *k = DEVICE_CLASS(klass); 505999e12bbSAnthony Liguori 506999e12bbSAnthony Liguori sdc->init = pl181_init; 50739bffca2SAnthony Liguori k->vmsd = &vmstate_pl181; 50839bffca2SAnthony Liguori k->reset = pl181_reset; 50939bffca2SAnthony Liguori k->no_user = 1; 510999e12bbSAnthony Liguori } 511999e12bbSAnthony Liguori 5128c43a6f0SAndreas Färber static const TypeInfo pl181_info = { 513630f4442SAndreas Färber .name = TYPE_PL181, 51439bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 5151d998d93SAndreas Färber .instance_size = sizeof(PL181State), 516999e12bbSAnthony Liguori .class_init = pl181_class_init, 517624923beSPeter Maydell }; 518624923beSPeter Maydell 51983f7d43aSAndreas Färber static void pl181_register_types(void) 520aa9311d8SPaul Brook { 52139bffca2SAnthony Liguori type_register_static(&pl181_info); 522aa9311d8SPaul Brook } 523aa9311d8SPaul Brook 52483f7d43aSAndreas Färber type_init(pl181_register_types) 525