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

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: }

unix.superglobalmegacorp.com

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