|
|
1.1 ! root 1: /* ! 2: * Arm PrimeCell PL181 MultiMedia Card Interface ! 3: * ! 4: * Copyright (c) 2007 CodeSourcery. ! 5: * Written by Paul Brook ! 6: * ! 7: * This code is licenced under the GPL. ! 8: */ ! 9: ! 10: #include "hw.h" ! 11: #include "primecell.h" ! 12: #include "sd.h" ! 13: ! 14: //#define DEBUG_PL181 1 ! 15: ! 16: #ifdef DEBUG_PL181 ! 17: #define DPRINTF(fmt, args...) \ ! 18: do { printf("pl181: " fmt , ##args); } while (0) ! 19: #else ! 20: #define DPRINTF(fmt, args...) do {} while(0) ! 21: #endif ! 22: ! 23: #define PL181_FIFO_LEN 16 ! 24: ! 25: typedef struct { ! 26: SDState *card; ! 27: uint32_t base; ! 28: uint32_t clock; ! 29: uint32_t power; ! 30: uint32_t cmdarg; ! 31: uint32_t cmd; ! 32: uint32_t datatimer; ! 33: uint32_t datalength; ! 34: uint32_t respcmd; ! 35: uint32_t response[4]; ! 36: uint32_t datactrl; ! 37: uint32_t datacnt; ! 38: uint32_t status; ! 39: uint32_t mask[2]; ! 40: int fifo_pos; ! 41: int fifo_len; ! 42: /* The linux 2.6.21 driver is buggy, and misbehaves if new data arrives ! 43: while it is reading the FIFO. We hack around this be defering ! 44: subsequent transfers until after the driver polls the status word. ! 45: http://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=4446/1 ! 46: */ ! 47: int linux_hack; ! 48: uint32_t fifo[PL181_FIFO_LEN]; ! 49: qemu_irq irq[2]; ! 50: } pl181_state; ! 51: ! 52: #define PL181_CMD_INDEX 0x3f ! 53: #define PL181_CMD_RESPONSE (1 << 6) ! 54: #define PL181_CMD_LONGRESP (1 << 7) ! 55: #define PL181_CMD_INTERRUPT (1 << 8) ! 56: #define PL181_CMD_PENDING (1 << 9) ! 57: #define PL181_CMD_ENABLE (1 << 10) ! 58: ! 59: #define PL181_DATA_ENABLE (1 << 0) ! 60: #define PL181_DATA_DIRECTION (1 << 1) ! 61: #define PL181_DATA_MODE (1 << 2) ! 62: #define PL181_DATA_DMAENABLE (1 << 3) ! 63: ! 64: #define PL181_STATUS_CMDCRCFAIL (1 << 0) ! 65: #define PL181_STATUS_DATACRCFAIL (1 << 1) ! 66: #define PL181_STATUS_CMDTIMEOUT (1 << 2) ! 67: #define PL181_STATUS_DATATIMEOUT (1 << 3) ! 68: #define PL181_STATUS_TXUNDERRUN (1 << 4) ! 69: #define PL181_STATUS_RXOVERRUN (1 << 5) ! 70: #define PL181_STATUS_CMDRESPEND (1 << 6) ! 71: #define PL181_STATUS_CMDSENT (1 << 7) ! 72: #define PL181_STATUS_DATAEND (1 << 8) ! 73: #define PL181_STATUS_DATABLOCKEND (1 << 10) ! 74: #define PL181_STATUS_CMDACTIVE (1 << 11) ! 75: #define PL181_STATUS_TXACTIVE (1 << 12) ! 76: #define PL181_STATUS_RXACTIVE (1 << 13) ! 77: #define PL181_STATUS_TXFIFOHALFEMPTY (1 << 14) ! 78: #define PL181_STATUS_RXFIFOHALFFULL (1 << 15) ! 79: #define PL181_STATUS_TXFIFOFULL (1 << 16) ! 80: #define PL181_STATUS_RXFIFOFULL (1 << 17) ! 81: #define PL181_STATUS_TXFIFOEMPTY (1 << 18) ! 82: #define PL181_STATUS_RXFIFOEMPTY (1 << 19) ! 83: #define PL181_STATUS_TXDATAAVLBL (1 << 20) ! 84: #define PL181_STATUS_RXDATAAVLBL (1 << 21) ! 85: ! 86: #define PL181_STATUS_TX_FIFO (PL181_STATUS_TXACTIVE \ ! 87: |PL181_STATUS_TXFIFOHALFEMPTY \ ! 88: |PL181_STATUS_TXFIFOFULL \ ! 89: |PL181_STATUS_TXFIFOEMPTY \ ! 90: |PL181_STATUS_TXDATAAVLBL) ! 91: #define PL181_STATUS_RX_FIFO (PL181_STATUS_RXACTIVE \ ! 92: |PL181_STATUS_RXFIFOHALFFULL \ ! 93: |PL181_STATUS_RXFIFOFULL \ ! 94: |PL181_STATUS_RXFIFOEMPTY \ ! 95: |PL181_STATUS_RXDATAAVLBL) ! 96: ! 97: static const unsigned char pl181_id[] = ! 98: { 0x81, 0x11, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; ! 99: ! 100: static void pl181_update(pl181_state *s) ! 101: { ! 102: int i; ! 103: for (i = 0; i < 2; i++) { ! 104: qemu_set_irq(s->irq[i], (s->status & s->mask[i]) != 0); ! 105: } ! 106: } ! 107: ! 108: static void pl181_fifo_push(pl181_state *s, uint32_t value) ! 109: { ! 110: int n; ! 111: ! 112: if (s->fifo_len == PL181_FIFO_LEN) { ! 113: fprintf(stderr, "pl181: FIFO overflow\n"); ! 114: return; ! 115: } ! 116: n = (s->fifo_pos + s->fifo_len) & (PL181_FIFO_LEN - 1); ! 117: s->fifo_len++; ! 118: s->fifo[n] = value; ! 119: DPRINTF("FIFO push %08x\n", (int)value); ! 120: } ! 121: ! 122: static uint32_t pl181_fifo_pop(pl181_state *s) ! 123: { ! 124: uint32_t value; ! 125: ! 126: if (s->fifo_len == 0) { ! 127: fprintf(stderr, "pl181: FIFO underflow\n"); ! 128: return 0; ! 129: } ! 130: value = s->fifo[s->fifo_pos]; ! 131: s->fifo_len--; ! 132: s->fifo_pos = (s->fifo_pos + 1) & (PL181_FIFO_LEN - 1); ! 133: DPRINTF("FIFO pop %08x\n", (int)value); ! 134: return value; ! 135: } ! 136: ! 137: static void pl181_send_command(pl181_state *s) ! 138: { ! 139: struct sd_request_s request; ! 140: uint8_t response[16]; ! 141: int rlen; ! 142: ! 143: request.cmd = s->cmd & PL181_CMD_INDEX; ! 144: request.arg = s->cmdarg; ! 145: DPRINTF("Command %d %08x\n", request.cmd, request.arg); ! 146: rlen = sd_do_command(s->card, &request, response); ! 147: if (rlen < 0) ! 148: goto error; ! 149: if (s->cmd & PL181_CMD_RESPONSE) { ! 150: #define RWORD(n) ((response[n] << 24) | (response[n + 1] << 16) \ ! 151: | (response[n + 2] << 8) | response[n + 3]) ! 152: if (rlen == 0 || (rlen == 4 && (s->cmd & PL181_CMD_LONGRESP))) ! 153: goto error; ! 154: if (rlen != 4 && rlen != 16) ! 155: goto error; ! 156: s->response[0] = RWORD(0); ! 157: if (rlen == 4) { ! 158: s->response[1] = s->response[2] = s->response[3] = 0; ! 159: } else { ! 160: s->response[1] = RWORD(4); ! 161: s->response[2] = RWORD(8); ! 162: s->response[3] = RWORD(12) & ~1; ! 163: } ! 164: DPRINTF("Response received\n"); ! 165: s->status |= PL181_STATUS_CMDRESPEND; ! 166: #undef RWORD ! 167: } else { ! 168: DPRINTF("Command sent\n"); ! 169: s->status |= PL181_STATUS_CMDSENT; ! 170: } ! 171: return; ! 172: ! 173: error: ! 174: DPRINTF("Timeout\n"); ! 175: s->status |= PL181_STATUS_CMDTIMEOUT; ! 176: } ! 177: ! 178: /* Transfer data between the card and the FIFO. This is complicated by ! 179: the FIFO holding 32-bit words and the card taking data in single byte ! 180: chunks. FIFO bytes are transferred in little-endian order. */ ! 181: ! 182: static void pl181_fifo_run(pl181_state *s) ! 183: { ! 184: uint32_t bits; ! 185: uint32_t value; ! 186: int n; ! 187: int limit; ! 188: int is_read; ! 189: ! 190: is_read = (s->datactrl & PL181_DATA_DIRECTION) != 0; ! 191: if (s->datacnt != 0 && (!is_read || sd_data_ready(s->card)) ! 192: && !s->linux_hack) { ! 193: limit = is_read ? PL181_FIFO_LEN : 0; ! 194: n = 0; ! 195: value = 0; ! 196: while (s->datacnt && s->fifo_len != limit) { ! 197: if (is_read) { ! 198: value |= (uint32_t)sd_read_data(s->card) << (n * 8); ! 199: n++; ! 200: if (n == 4) { ! 201: pl181_fifo_push(s, value); ! 202: value = 0; ! 203: n = 0; ! 204: } ! 205: } else { ! 206: if (n == 0) { ! 207: value = pl181_fifo_pop(s); ! 208: n = 4; ! 209: } ! 210: sd_write_data(s->card, value & 0xff); ! 211: value >>= 8; ! 212: n--; ! 213: } ! 214: s->datacnt--; ! 215: } ! 216: if (n && is_read) { ! 217: pl181_fifo_push(s, value); ! 218: } ! 219: } ! 220: s->status &= ~(PL181_STATUS_RX_FIFO | PL181_STATUS_TX_FIFO); ! 221: if (s->datacnt == 0) { ! 222: s->status |= PL181_STATUS_DATAEND; ! 223: /* HACK: */ ! 224: s->status |= PL181_STATUS_DATABLOCKEND; ! 225: DPRINTF("Transfer Complete\n"); ! 226: } ! 227: if (s->datacnt == 0 && s->fifo_len == 0) { ! 228: s->datactrl &= ~PL181_DATA_ENABLE; ! 229: DPRINTF("Data engine idle\n"); ! 230: } else { ! 231: /* Update FIFO bits. */ ! 232: bits = PL181_STATUS_TXACTIVE | PL181_STATUS_RXACTIVE; ! 233: if (s->fifo_len == 0) { ! 234: bits |= PL181_STATUS_TXFIFOEMPTY; ! 235: bits |= PL181_STATUS_RXFIFOEMPTY; ! 236: } else { ! 237: bits |= PL181_STATUS_TXDATAAVLBL; ! 238: bits |= PL181_STATUS_RXDATAAVLBL; ! 239: } ! 240: if (s->fifo_len == 16) { ! 241: bits |= PL181_STATUS_TXFIFOFULL; ! 242: bits |= PL181_STATUS_RXFIFOFULL; ! 243: } ! 244: if (s->fifo_len <= 8) { ! 245: bits |= PL181_STATUS_TXFIFOHALFEMPTY; ! 246: } ! 247: if (s->fifo_len >= 8) { ! 248: bits |= PL181_STATUS_RXFIFOHALFFULL; ! 249: } ! 250: if (s->datactrl & PL181_DATA_DIRECTION) { ! 251: bits &= PL181_STATUS_RX_FIFO; ! 252: } else { ! 253: bits &= PL181_STATUS_TX_FIFO; ! 254: } ! 255: s->status |= bits; ! 256: } ! 257: } ! 258: ! 259: static uint32_t pl181_read(void *opaque, target_phys_addr_t offset) ! 260: { ! 261: pl181_state *s = (pl181_state *)opaque; ! 262: uint32_t tmp; ! 263: ! 264: offset -= s->base; ! 265: if (offset >= 0xfe0 && offset < 0x1000) { ! 266: return pl181_id[(offset - 0xfe0) >> 2]; ! 267: } ! 268: switch (offset) { ! 269: case 0x00: /* Power */ ! 270: return s->power; ! 271: case 0x04: /* Clock */ ! 272: return s->clock; ! 273: case 0x08: /* Argument */ ! 274: return s->cmdarg; ! 275: case 0x0c: /* Command */ ! 276: return s->cmd; ! 277: case 0x10: /* RespCmd */ ! 278: return s->respcmd; ! 279: case 0x14: /* Response0 */ ! 280: return s->response[0]; ! 281: case 0x18: /* Response1 */ ! 282: return s->response[1]; ! 283: case 0x1c: /* Response2 */ ! 284: return s->response[2]; ! 285: case 0x20: /* Response3 */ ! 286: return s->response[3]; ! 287: case 0x24: /* DataTimer */ ! 288: return s->datatimer; ! 289: case 0x28: /* DataLength */ ! 290: return s->datalength; ! 291: case 0x2c: /* DataCtrl */ ! 292: return s->datactrl; ! 293: case 0x30: /* DataCnt */ ! 294: return s->datacnt; ! 295: case 0x34: /* Status */ ! 296: tmp = s->status; ! 297: if (s->linux_hack) { ! 298: s->linux_hack = 0; ! 299: pl181_fifo_run(s); ! 300: pl181_update(s); ! 301: } ! 302: return tmp; ! 303: case 0x3c: /* Mask0 */ ! 304: return s->mask[0]; ! 305: case 0x40: /* Mask1 */ ! 306: return s->mask[1]; ! 307: case 0x48: /* FifoCnt */ ! 308: /* The documentation is somewhat vague about exactly what FifoCnt ! 309: does. On real hardware it appears to be when decrememnted ! 310: when a word is transfered between the FIFO and the serial ! 311: data engine. DataCnt is decremented after each byte is ! 312: transfered between the serial engine and the card. ! 313: We don't emulate this level of detail, so both can be the same. */ ! 314: tmp = (s->datacnt + 3) >> 2; ! 315: if (s->linux_hack) { ! 316: s->linux_hack = 0; ! 317: pl181_fifo_run(s); ! 318: pl181_update(s); ! 319: } ! 320: return tmp; ! 321: case 0x80: case 0x84: case 0x88: case 0x8c: /* FifoData */ ! 322: case 0x90: case 0x94: case 0x98: case 0x9c: ! 323: case 0xa0: case 0xa4: case 0xa8: case 0xac: ! 324: case 0xb0: case 0xb4: case 0xb8: case 0xbc: ! 325: if (s->fifo_len == 0) { ! 326: fprintf(stderr, "pl181: Unexpected FIFO read\n"); ! 327: return 0; ! 328: } else { ! 329: uint32_t value; ! 330: value = pl181_fifo_pop(s); ! 331: s->linux_hack = 1; ! 332: pl181_fifo_run(s); ! 333: pl181_update(s); ! 334: return value; ! 335: } ! 336: default: ! 337: cpu_abort (cpu_single_env, "pl181_read: Bad offset %x\n", (int)offset); ! 338: return 0; ! 339: } ! 340: } ! 341: ! 342: static void pl181_write(void *opaque, target_phys_addr_t offset, ! 343: uint32_t value) ! 344: { ! 345: pl181_state *s = (pl181_state *)opaque; ! 346: ! 347: offset -= s->base; ! 348: switch (offset) { ! 349: case 0x00: /* Power */ ! 350: s->power = value & 0xff; ! 351: break; ! 352: case 0x04: /* Clock */ ! 353: s->clock = value & 0xff; ! 354: break; ! 355: case 0x08: /* Argument */ ! 356: s->cmdarg = value; ! 357: break; ! 358: case 0x0c: /* Command */ ! 359: s->cmd = value; ! 360: if (s->cmd & PL181_CMD_ENABLE) { ! 361: if (s->cmd & PL181_CMD_INTERRUPT) { ! 362: fprintf(stderr, "pl181: Interrupt mode not implemented\n"); ! 363: abort(); ! 364: } if (s->cmd & PL181_CMD_PENDING) { ! 365: fprintf(stderr, "pl181: Pending commands not implemented\n"); ! 366: abort(); ! 367: } else { ! 368: pl181_send_command(s); ! 369: pl181_fifo_run(s); ! 370: } ! 371: /* The command has completed one way or the other. */ ! 372: s->cmd &= ~PL181_CMD_ENABLE; ! 373: } ! 374: break; ! 375: case 0x24: /* DataTimer */ ! 376: s->datatimer = value; ! 377: break; ! 378: case 0x28: /* DataLength */ ! 379: s->datalength = value & 0xffff; ! 380: break; ! 381: case 0x2c: /* DataCtrl */ ! 382: s->datactrl = value & 0xff; ! 383: if (value & PL181_DATA_ENABLE) { ! 384: s->datacnt = s->datalength; ! 385: pl181_fifo_run(s); ! 386: } ! 387: break; ! 388: case 0x38: /* Clear */ ! 389: s->status &= ~(value & 0x7ff); ! 390: break; ! 391: case 0x3c: /* Mask0 */ ! 392: s->mask[0] = value; ! 393: break; ! 394: case 0x40: /* Mask1 */ ! 395: s->mask[1] = value; ! 396: break; ! 397: case 0x80: case 0x84: case 0x88: case 0x8c: /* FifoData */ ! 398: case 0x90: case 0x94: case 0x98: case 0x9c: ! 399: case 0xa0: case 0xa4: case 0xa8: case 0xac: ! 400: case 0xb0: case 0xb4: case 0xb8: case 0xbc: ! 401: if (s->datacnt == 0) { ! 402: fprintf(stderr, "pl181: Unexpected FIFO write\n"); ! 403: } else { ! 404: pl181_fifo_push(s, value); ! 405: pl181_fifo_run(s); ! 406: } ! 407: break; ! 408: default: ! 409: cpu_abort (cpu_single_env, "pl181_write: Bad offset %x\n", (int)offset); ! 410: } ! 411: pl181_update(s); ! 412: } ! 413: ! 414: static CPUReadMemoryFunc *pl181_readfn[] = { ! 415: pl181_read, ! 416: pl181_read, ! 417: pl181_read ! 418: }; ! 419: ! 420: static CPUWriteMemoryFunc *pl181_writefn[] = { ! 421: pl181_write, ! 422: pl181_write, ! 423: pl181_write ! 424: }; ! 425: ! 426: static void pl181_reset(void *opaque) ! 427: { ! 428: pl181_state *s = (pl181_state *)opaque; ! 429: ! 430: s->power = 0; ! 431: s->cmdarg = 0; ! 432: s->cmd = 0; ! 433: s->datatimer = 0; ! 434: s->datalength = 0; ! 435: s->respcmd = 0; ! 436: s->response[0] = 0; ! 437: s->response[1] = 0; ! 438: s->response[2] = 0; ! 439: s->response[3] = 0; ! 440: s->datatimer = 0; ! 441: s->datalength = 0; ! 442: s->datactrl = 0; ! 443: s->datacnt = 0; ! 444: s->status = 0; ! 445: s->linux_hack = 0; ! 446: s->mask[0] = 0; ! 447: s->mask[1] = 0; ! 448: } ! 449: ! 450: void pl181_init(uint32_t base, BlockDriverState *bd, ! 451: qemu_irq irq0, qemu_irq irq1) ! 452: { ! 453: int iomemtype; ! 454: pl181_state *s; ! 455: ! 456: s = (pl181_state *)qemu_mallocz(sizeof(pl181_state)); ! 457: iomemtype = cpu_register_io_memory(0, pl181_readfn, ! 458: pl181_writefn, s); ! 459: cpu_register_physical_memory(base, 0x00001000, iomemtype); ! 460: s->base = base; ! 461: s->card = sd_init(bd, 0); ! 462: s->irq[0] = irq0; ! 463: s->irq[1] = irq1; ! 464: qemu_register_reset(pl181_reset, s); ! 465: pl181_reset(s); ! 466: /* ??? Save/restore. */ ! 467: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.