Annotation of qemu/hw/pl181.c, revision 1.1.1.5

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

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.