Annotation of qemu/hw/pl022.c, revision 1.1.1.2

1.1       root        1: /*
                      2:  * Arm PrimeCell PL022 Synchronous Serial Port
                      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: 
                     13: //#define DEBUG_PL022 1
                     14: 
                     15: #ifdef DEBUG_PL022
                     16: #define DPRINTF(fmt, args...) \
                     17: do { printf("pl022: " fmt , ##args); } while (0)
                     18: #define BADF(fmt, args...) \
                     19: do { fprintf(stderr, "pl022: error: " fmt , ##args); exit(1);} while (0)
                     20: #else
                     21: #define DPRINTF(fmt, args...) do {} while(0)
                     22: #define BADF(fmt, args...) \
                     23: do { fprintf(stderr, "pl022: error: " fmt , ##args);} while (0)
                     24: #endif
                     25: 
                     26: #define PL022_CR1_LBM 0x01
                     27: #define PL022_CR1_SSE 0x02
                     28: #define PL022_CR1_MS  0x04
                     29: #define PL022_CR1_SDO 0x08
                     30: 
                     31: #define PL022_SR_TFE  0x01
                     32: #define PL022_SR_TNF  0x02
                     33: #define PL022_SR_RNE  0x04
                     34: #define PL022_SR_RFF  0x08
                     35: #define PL022_SR_BSY  0x10
                     36: 
                     37: #define PL022_INT_ROR 0x01
                     38: #define PL022_INT_RT  0x04
                     39: #define PL022_INT_RX  0x04
                     40: #define PL022_INT_TX  0x08
                     41: 
                     42: typedef struct {
                     43:     uint32_t cr0;
                     44:     uint32_t cr1;
                     45:     uint32_t bitmask;
                     46:     uint32_t sr;
                     47:     uint32_t cpsr;
                     48:     uint32_t is;
                     49:     uint32_t im;
                     50:     /* The FIFO head points to the next empty entry.  */
                     51:     int tx_fifo_head;
                     52:     int rx_fifo_head;
                     53:     int tx_fifo_len;
                     54:     int rx_fifo_len;
                     55:     uint16_t tx_fifo[8];
                     56:     uint16_t rx_fifo[8];
                     57:     qemu_irq irq;
                     58:     int (*xfer_cb)(void *, int);
                     59:     void *opaque;
                     60: } pl022_state;
                     61: 
                     62: static const unsigned char pl022_id[8] =
                     63:   { 0x22, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
                     64: 
                     65: static void pl022_update(pl022_state *s)
                     66: {
                     67:     s->sr = 0;
                     68:     if (s->tx_fifo_len == 0)
                     69:         s->sr |= PL022_SR_TFE;
                     70:     if (s->tx_fifo_len != 8)
                     71:         s->sr |= PL022_SR_TNF;
                     72:     if (s->rx_fifo_len != 0)
                     73:         s->sr |= PL022_SR_RNE;
                     74:     if (s->rx_fifo_len == 8)
                     75:         s->sr |= PL022_SR_RFF;
                     76:     if (s->tx_fifo_len)
                     77:         s->sr |= PL022_SR_BSY;
                     78:     s->is = 0;
                     79:     if (s->rx_fifo_len >= 4)
                     80:         s->is |= PL022_INT_RX;
                     81:     if (s->tx_fifo_len <= 4)
                     82:         s->is |= PL022_INT_TX;
                     83: 
                     84:     qemu_set_irq(s->irq, (s->is & s->im) != 0);
                     85: }
                     86: 
                     87: static void pl022_xfer(pl022_state *s)
                     88: {
                     89:     int i;
                     90:     int o;
                     91:     int val;
                     92: 
                     93:     if ((s->cr1 & PL022_CR1_SSE) == 0) {
                     94:         pl022_update(s);
                     95:         DPRINTF("Disabled\n");
                     96:         return;
                     97:     }
                     98: 
                     99:     DPRINTF("Maybe xfer %d/%d\n", s->tx_fifo_len, s->rx_fifo_len);
                    100:     i = (s->tx_fifo_head - s->tx_fifo_len) & 7;
                    101:     o = s->rx_fifo_head;
                    102:     /* ??? We do not emulate the line speed.
                    103:        This may break some applications.  The are two problematic cases:
                    104:         (a) A driver feeds data into the TX FIFO until it is full,
                    105:          and only then drains the RX FIFO.  On real hardware the CPU can
                    106:          feed data fast enough that the RX fifo never gets chance to overflow.
                    107:         (b) A driver transmits data, deliberately allowing the RX FIFO to
                    108:          overflow because it ignores the RX data anyway.
                    109: 
                    110:        We choose to support (a) by stalling the transmit engine if it would
                    111:        cause the RX FIFO to overflow.  In practice much transmit-only code
                    112:        falls into (a) because it flushes the RX FIFO to determine when
                    113:        the transfer has completed.  */
                    114:     while (s->tx_fifo_len && s->rx_fifo_len < 8) {
                    115:         DPRINTF("xfer\n");
                    116:         val = s->tx_fifo[i];
                    117:         if (s->cr1 & PL022_CR1_LBM) {
                    118:             /* Loopback mode.  */
                    119:         } else if (s->xfer_cb) {
                    120:             val = s->xfer_cb(s->opaque, val);
                    121:         } else {
                    122:             val = 0;
                    123:         }
                    124:         s->rx_fifo[o] = val & s->bitmask;
                    125:         i = (i + 1) & 7;
                    126:         o = (o + 1) & 7;
                    127:         s->tx_fifo_len--;
                    128:         s->rx_fifo_len++;
                    129:     }
                    130:     s->rx_fifo_head = o;
                    131:     pl022_update(s);
                    132: }
                    133: 
                    134: static uint32_t pl022_read(void *opaque, target_phys_addr_t offset)
                    135: {
                    136:     pl022_state *s = (pl022_state *)opaque;
                    137:     int val;
                    138: 
                    139:     if (offset >= 0xfe0 && offset < 0x1000) {
                    140:         return pl022_id[(offset - 0xfe0) >> 2];
                    141:     }
                    142:     switch (offset) {
                    143:     case 0x00: /* CR0 */
                    144:       return s->cr0;
                    145:     case 0x04: /* CR1 */
                    146:       return s->cr1;
                    147:     case 0x08: /* DR */
                    148:         if (s->rx_fifo_len) {
                    149:             val = s->rx_fifo[(s->rx_fifo_head - s->rx_fifo_len) & 7];
                    150:             DPRINTF("RX %02x\n", val);
                    151:             s->rx_fifo_len--;
                    152:             pl022_xfer(s);
                    153:         } else {
                    154:             val = 0;
                    155:         }
                    156:         return val;
                    157:     case 0x0c: /* SR */
                    158:         return s->sr;
                    159:     case 0x10: /* CPSR */
                    160:         return s->cpsr;
                    161:     case 0x14: /* IMSC */
                    162:         return s->im;
                    163:     case 0x18: /* RIS */
                    164:         return s->is;
                    165:     case 0x1c: /* MIS */
                    166:         return s->im & s->is;
                    167:     case 0x20: /* DMACR */
                    168:         /* Not implemented.  */
                    169:         return 0;
                    170:     default:
                    171:         cpu_abort (cpu_single_env, "pl022_read: Bad offset %x\n",
                    172:                    (int)offset);
                    173:         return 0;
                    174:     }
                    175: }
                    176: 
                    177: static void pl022_write(void *opaque, target_phys_addr_t offset,
                    178:                         uint32_t value)
                    179: {
                    180:     pl022_state *s = (pl022_state *)opaque;
                    181: 
                    182:     switch (offset) {
                    183:     case 0x00: /* CR0 */
                    184:         s->cr0 = value;
                    185:         /* Clock rate and format are ignored.  */
                    186:         s->bitmask = (1 << ((value & 15) + 1)) - 1;
                    187:         break;
                    188:     case 0x04: /* CR1 */
                    189:         s->cr1 = value;
                    190:         if ((s->cr1 & (PL022_CR1_MS | PL022_CR1_SSE))
                    191:                    == (PL022_CR1_MS | PL022_CR1_SSE)) {
                    192:             BADF("SPI slave mode not implemented\n");
                    193:         }
                    194:         pl022_xfer(s);
                    195:         break;
                    196:     case 0x08: /* DR */
                    197:         if (s->tx_fifo_len < 8) {
                    198:             DPRINTF("TX %02x\n", value);
                    199:             s->tx_fifo[s->tx_fifo_head] = value & s->bitmask;
                    200:             s->tx_fifo_head = (s->tx_fifo_head + 1) & 7;
                    201:             s->tx_fifo_len++;
                    202:             pl022_xfer(s);
                    203:         }
                    204:         break;
                    205:     case 0x10: /* CPSR */
                    206:         /* Prescaler.  Ignored.  */
                    207:         s->cpsr = value & 0xff;
                    208:         break;
                    209:     case 0x14: /* IMSC */
                    210:         s->im = value;
                    211:         pl022_update(s);
                    212:         break;
                    213:     case 0x20: /* DMACR */
                    214:         if (value)
                    215:             cpu_abort (cpu_single_env, "pl022: DMA not implemented\n");
                    216:         break;
                    217:     default:
                    218:         cpu_abort (cpu_single_env, "pl022_write: Bad offset %x\n",
                    219:                    (int)offset);
                    220:     }
                    221: }
                    222: 
                    223: static void pl022_reset(pl022_state *s)
                    224: {
                    225:     s->rx_fifo_len = 0;
                    226:     s->tx_fifo_len = 0;
                    227:     s->im = 0;
                    228:     s->is = PL022_INT_TX;
                    229:     s->sr = PL022_SR_TFE | PL022_SR_TNF;
                    230: }
                    231: 
                    232: static CPUReadMemoryFunc *pl022_readfn[] = {
                    233:    pl022_read,
                    234:    pl022_read,
                    235:    pl022_read
                    236: };
                    237: 
                    238: static CPUWriteMemoryFunc *pl022_writefn[] = {
                    239:    pl022_write,
                    240:    pl022_write,
                    241:    pl022_write
                    242: };
                    243: 
1.1.1.2 ! root      244: static void pl022_save(QEMUFile *f, void *opaque)
        !           245: {
        !           246:     pl022_state *s = (pl022_state *)opaque;
        !           247:     int i;
        !           248: 
        !           249:     qemu_put_be32(f, s->cr0);
        !           250:     qemu_put_be32(f, s->cr1);
        !           251:     qemu_put_be32(f, s->bitmask);
        !           252:     qemu_put_be32(f, s->sr);
        !           253:     qemu_put_be32(f, s->cpsr);
        !           254:     qemu_put_be32(f, s->is);
        !           255:     qemu_put_be32(f, s->im);
        !           256:     qemu_put_be32(f, s->tx_fifo_head);
        !           257:     qemu_put_be32(f, s->rx_fifo_head);
        !           258:     qemu_put_be32(f, s->tx_fifo_len);
        !           259:     qemu_put_be32(f, s->rx_fifo_len);
        !           260:     for (i = 0; i < 8; i++) {
        !           261:         qemu_put_be16(f, s->tx_fifo[i]);
        !           262:         qemu_put_be16(f, s->rx_fifo[i]);
        !           263:     }
        !           264: }
        !           265: 
        !           266: static int pl022_load(QEMUFile *f, void *opaque, int version_id)
        !           267: {
        !           268:     pl022_state *s = (pl022_state *)opaque;
        !           269:     int i;
        !           270: 
        !           271:     if (version_id != 1)
        !           272:         return -EINVAL;
        !           273: 
        !           274:     s->cr0 = qemu_get_be32(f);
        !           275:     s->cr1 = qemu_get_be32(f);
        !           276:     s->bitmask = qemu_get_be32(f);
        !           277:     s->sr = qemu_get_be32(f);
        !           278:     s->cpsr = qemu_get_be32(f);
        !           279:     s->is = qemu_get_be32(f);
        !           280:     s->im = qemu_get_be32(f);
        !           281:     s->tx_fifo_head = qemu_get_be32(f);
        !           282:     s->rx_fifo_head = qemu_get_be32(f);
        !           283:     s->tx_fifo_len = qemu_get_be32(f);
        !           284:     s->rx_fifo_len = qemu_get_be32(f);
        !           285:     for (i = 0; i < 8; i++) {
        !           286:         s->tx_fifo[i] = qemu_get_be16(f);
        !           287:         s->rx_fifo[i] = qemu_get_be16(f);
        !           288:     }
        !           289: 
        !           290:     return 0;
        !           291: }
        !           292: 
1.1       root      293: void pl022_init(uint32_t base, qemu_irq irq, int (*xfer_cb)(void *, int),
                    294:                 void * opaque)
                    295: {
                    296:     int iomemtype;
                    297:     pl022_state *s;
                    298: 
                    299:     s = (pl022_state *)qemu_mallocz(sizeof(pl022_state));
                    300:     iomemtype = cpu_register_io_memory(0, pl022_readfn,
                    301:                                        pl022_writefn, s);
                    302:     cpu_register_physical_memory(base, 0x00001000, iomemtype);
                    303:     s->irq = irq;
                    304:     s->xfer_cb = xfer_cb;
                    305:     s->opaque = opaque;
                    306:     pl022_reset(s);
1.1.1.2 ! root      307:     register_savevm("pl022_ssp", -1, 1, pl022_save, pl022_load, s);
1.1       root      308: }

unix.superglobalmegacorp.com