qemu/hw/pl022.c
<<
>>
Prefs
   1/*
   2 * Arm PrimeCell PL022 Synchronous Serial Port
   3 *
   4 * Copyright (c) 2007 CodeSourcery.
   5 * Written by Paul Brook
   6 *
   7 * This code is licensed under the GPL.
   8 */
   9
  10#include "sysbus.h"
  11#include "ssi.h"
  12
  13//#define DEBUG_PL022 1
  14
  15#ifdef DEBUG_PL022
  16#define DPRINTF(fmt, ...) \
  17do { printf("pl022: " fmt , ## __VA_ARGS__); } while (0)
  18#define BADF(fmt, ...) \
  19do { fprintf(stderr, "pl022: error: " fmt , ## __VA_ARGS__); exit(1);} while (0)
  20#else
  21#define DPRINTF(fmt, ...) do {} while(0)
  22#define BADF(fmt, ...) \
  23do { fprintf(stderr, "pl022: error: " fmt , ## __VA_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
  42typedef struct {
  43    SysBusDevice busdev;
  44    MemoryRegion iomem;
  45    uint32_t cr0;
  46    uint32_t cr1;
  47    uint32_t bitmask;
  48    uint32_t sr;
  49    uint32_t cpsr;
  50    uint32_t is;
  51    uint32_t im;
  52    /* The FIFO head points to the next empty entry.  */
  53    int tx_fifo_head;
  54    int rx_fifo_head;
  55    int tx_fifo_len;
  56    int rx_fifo_len;
  57    uint16_t tx_fifo[8];
  58    uint16_t rx_fifo[8];
  59    qemu_irq irq;
  60    SSIBus *ssi;
  61} pl022_state;
  62
  63static const unsigned char pl022_id[8] =
  64  { 0x22, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
  65
  66static void pl022_update(pl022_state *s)
  67{
  68    s->sr = 0;
  69    if (s->tx_fifo_len == 0)
  70        s->sr |= PL022_SR_TFE;
  71    if (s->tx_fifo_len != 8)
  72        s->sr |= PL022_SR_TNF;
  73    if (s->rx_fifo_len != 0)
  74        s->sr |= PL022_SR_RNE;
  75    if (s->rx_fifo_len == 8)
  76        s->sr |= PL022_SR_RFF;
  77    if (s->tx_fifo_len)
  78        s->sr |= PL022_SR_BSY;
  79    s->is = 0;
  80    if (s->rx_fifo_len >= 4)
  81        s->is |= PL022_INT_RX;
  82    if (s->tx_fifo_len <= 4)
  83        s->is |= PL022_INT_TX;
  84
  85    qemu_set_irq(s->irq, (s->is & s->im) != 0);
  86}
  87
  88static void pl022_xfer(pl022_state *s)
  89{
  90    int i;
  91    int o;
  92    int val;
  93
  94    if ((s->cr1 & PL022_CR1_SSE) == 0) {
  95        pl022_update(s);
  96        DPRINTF("Disabled\n");
  97        return;
  98    }
  99
 100    DPRINTF("Maybe xfer %d/%d\n", s->tx_fifo_len, s->rx_fifo_len);
 101    i = (s->tx_fifo_head - s->tx_fifo_len) & 7;
 102    o = s->rx_fifo_head;
 103    /* ??? We do not emulate the line speed.
 104       This may break some applications.  The are two problematic cases:
 105        (a) A driver feeds data into the TX FIFO until it is full,
 106         and only then drains the RX FIFO.  On real hardware the CPU can
 107         feed data fast enough that the RX fifo never gets chance to overflow.
 108        (b) A driver transmits data, deliberately allowing the RX FIFO to
 109         overflow because it ignores the RX data anyway.
 110
 111       We choose to support (a) by stalling the transmit engine if it would
 112       cause the RX FIFO to overflow.  In practice much transmit-only code
 113       falls into (a) because it flushes the RX FIFO to determine when
 114       the transfer has completed.  */
 115    while (s->tx_fifo_len && s->rx_fifo_len < 8) {
 116        DPRINTF("xfer\n");
 117        val = s->tx_fifo[i];
 118        if (s->cr1 & PL022_CR1_LBM) {
 119            /* Loopback mode.  */
 120        } else {
 121            val = ssi_transfer(s->ssi, val);
 122        }
 123        s->rx_fifo[o] = val & s->bitmask;
 124        i = (i + 1) & 7;
 125        o = (o + 1) & 7;
 126        s->tx_fifo_len--;
 127        s->rx_fifo_len++;
 128    }
 129    s->rx_fifo_head = o;
 130    pl022_update(s);
 131}
 132
 133static uint64_t pl022_read(void *opaque, target_phys_addr_t offset,
 134                           unsigned size)
 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        hw_error("pl022_read: Bad offset %x\n", (int)offset);
 172        return 0;
 173    }
 174}
 175
 176static void pl022_write(void *opaque, target_phys_addr_t offset,
 177                        uint64_t value, unsigned size)
 178{
 179    pl022_state *s = (pl022_state *)opaque;
 180
 181    switch (offset) {
 182    case 0x00: /* CR0 */
 183        s->cr0 = value;
 184        /* Clock rate and format are ignored.  */
 185        s->bitmask = (1 << ((value & 15) + 1)) - 1;
 186        break;
 187    case 0x04: /* CR1 */
 188        s->cr1 = value;
 189        if ((s->cr1 & (PL022_CR1_MS | PL022_CR1_SSE))
 190                   == (PL022_CR1_MS | PL022_CR1_SSE)) {
 191            BADF("SPI slave mode not implemented\n");
 192        }
 193        pl022_xfer(s);
 194        break;
 195    case 0x08: /* DR */
 196        if (s->tx_fifo_len < 8) {
 197            DPRINTF("TX %02x\n", (unsigned)value);
 198            s->tx_fifo[s->tx_fifo_head] = value & s->bitmask;
 199            s->tx_fifo_head = (s->tx_fifo_head + 1) & 7;
 200            s->tx_fifo_len++;
 201            pl022_xfer(s);
 202        }
 203        break;
 204    case 0x10: /* CPSR */
 205        /* Prescaler.  Ignored.  */
 206        s->cpsr = value & 0xff;
 207        break;
 208    case 0x14: /* IMSC */
 209        s->im = value;
 210        pl022_update(s);
 211        break;
 212    case 0x20: /* DMACR */
 213        if (value) {
 214            hw_error("pl022: DMA not implemented\n");
 215        }
 216        break;
 217    default:
 218        hw_error("pl022_write: Bad offset %x\n", (int)offset);
 219    }
 220}
 221
 222static void pl022_reset(pl022_state *s)
 223{
 224    s->rx_fifo_len = 0;
 225    s->tx_fifo_len = 0;
 226    s->im = 0;
 227    s->is = PL022_INT_TX;
 228    s->sr = PL022_SR_TFE | PL022_SR_TNF;
 229}
 230
 231static const MemoryRegionOps pl022_ops = {
 232    .read = pl022_read,
 233    .write = pl022_write,
 234    .endianness = DEVICE_NATIVE_ENDIAN,
 235};
 236
 237static const VMStateDescription vmstate_pl022 = {
 238    .name = "pl022_ssp",
 239    .version_id = 1,
 240    .minimum_version_id = 1,
 241    .minimum_version_id_old = 1,
 242    .fields      = (VMStateField[]) {
 243        VMSTATE_UINT32(cr0, pl022_state),
 244        VMSTATE_UINT32(cr1, pl022_state),
 245        VMSTATE_UINT32(bitmask, pl022_state),
 246        VMSTATE_UINT32(sr, pl022_state),
 247        VMSTATE_UINT32(cpsr, pl022_state),
 248        VMSTATE_UINT32(is, pl022_state),
 249        VMSTATE_UINT32(im, pl022_state),
 250        VMSTATE_INT32(tx_fifo_head, pl022_state),
 251        VMSTATE_INT32(rx_fifo_head, pl022_state),
 252        VMSTATE_INT32(tx_fifo_len, pl022_state),
 253        VMSTATE_INT32(rx_fifo_len, pl022_state),
 254        VMSTATE_UINT16(tx_fifo[0], pl022_state),
 255        VMSTATE_UINT16(rx_fifo[0], pl022_state),
 256        VMSTATE_UINT16(tx_fifo[1], pl022_state),
 257        VMSTATE_UINT16(rx_fifo[1], pl022_state),
 258        VMSTATE_UINT16(tx_fifo[2], pl022_state),
 259        VMSTATE_UINT16(rx_fifo[2], pl022_state),
 260        VMSTATE_UINT16(tx_fifo[3], pl022_state),
 261        VMSTATE_UINT16(rx_fifo[3], pl022_state),
 262        VMSTATE_UINT16(tx_fifo[4], pl022_state),
 263        VMSTATE_UINT16(rx_fifo[4], pl022_state),
 264        VMSTATE_UINT16(tx_fifo[5], pl022_state),
 265        VMSTATE_UINT16(rx_fifo[5], pl022_state),
 266        VMSTATE_UINT16(tx_fifo[6], pl022_state),
 267        VMSTATE_UINT16(rx_fifo[6], pl022_state),
 268        VMSTATE_UINT16(tx_fifo[7], pl022_state),
 269        VMSTATE_UINT16(rx_fifo[7], pl022_state),
 270        VMSTATE_END_OF_LIST()
 271    }
 272};
 273
 274static int pl022_init(SysBusDevice *dev)
 275{
 276    pl022_state *s = FROM_SYSBUS(pl022_state, dev);
 277
 278    memory_region_init_io(&s->iomem, &pl022_ops, s, "pl022", 0x1000);
 279    sysbus_init_mmio(dev, &s->iomem);
 280    sysbus_init_irq(dev, &s->irq);
 281    s->ssi = ssi_create_bus(&dev->qdev, "ssi");
 282    pl022_reset(s);
 283    vmstate_register(&dev->qdev, -1, &vmstate_pl022, s);
 284    return 0;
 285}
 286
 287static void pl022_class_init(ObjectClass *klass, void *data)
 288{
 289    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
 290
 291    sdc->init = pl022_init;
 292}
 293
 294static TypeInfo pl022_info = {
 295    .name          = "pl022",
 296    .parent        = TYPE_SYS_BUS_DEVICE,
 297    .instance_size = sizeof(pl022_state),
 298    .class_init    = pl022_class_init,
 299};
 300
 301static void pl022_register_types(void)
 302{
 303    type_register_static(&pl022_info);
 304}
 305
 306type_init(pl022_register_types)
 307