qemu/hw/ssi/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 "hw/sysbus.h"
  11#include "hw/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
  42#define TYPE_PL022 "pl022"
  43#define PL022(obj) OBJECT_CHECK(PL022State, (obj), TYPE_PL022)
  44
  45typedef struct PL022State {
  46    SysBusDevice parent_obj;
  47
  48    MemoryRegion iomem;
  49    uint32_t cr0;
  50    uint32_t cr1;
  51    uint32_t bitmask;
  52    uint32_t sr;
  53    uint32_t cpsr;
  54    uint32_t is;
  55    uint32_t im;
  56    /* The FIFO head points to the next empty entry.  */
  57    int tx_fifo_head;
  58    int rx_fifo_head;
  59    int tx_fifo_len;
  60    int rx_fifo_len;
  61    uint16_t tx_fifo[8];
  62    uint16_t rx_fifo[8];
  63    qemu_irq irq;
  64    SSIBus *ssi;
  65} PL022State;
  66
  67static const unsigned char pl022_id[8] =
  68  { 0x22, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
  69
  70static void pl022_update(PL022State *s)
  71{
  72    s->sr = 0;
  73    if (s->tx_fifo_len == 0)
  74        s->sr |= PL022_SR_TFE;
  75    if (s->tx_fifo_len != 8)
  76        s->sr |= PL022_SR_TNF;
  77    if (s->rx_fifo_len != 0)
  78        s->sr |= PL022_SR_RNE;
  79    if (s->rx_fifo_len == 8)
  80        s->sr |= PL022_SR_RFF;
  81    if (s->tx_fifo_len)
  82        s->sr |= PL022_SR_BSY;
  83    s->is = 0;
  84    if (s->rx_fifo_len >= 4)
  85        s->is |= PL022_INT_RX;
  86    if (s->tx_fifo_len <= 4)
  87        s->is |= PL022_INT_TX;
  88
  89    qemu_set_irq(s->irq, (s->is & s->im) != 0);
  90}
  91
  92static void pl022_xfer(PL022State *s)
  93{
  94    int i;
  95    int o;
  96    int val;
  97
  98    if ((s->cr1 & PL022_CR1_SSE) == 0) {
  99        pl022_update(s);
 100        DPRINTF("Disabled\n");
 101        return;
 102    }
 103
 104    DPRINTF("Maybe xfer %d/%d\n", s->tx_fifo_len, s->rx_fifo_len);
 105    i = (s->tx_fifo_head - s->tx_fifo_len) & 7;
 106    o = s->rx_fifo_head;
 107    /* ??? We do not emulate the line speed.
 108       This may break some applications.  The are two problematic cases:
 109        (a) A driver feeds data into the TX FIFO until it is full,
 110         and only then drains the RX FIFO.  On real hardware the CPU can
 111         feed data fast enough that the RX fifo never gets chance to overflow.
 112        (b) A driver transmits data, deliberately allowing the RX FIFO to
 113         overflow because it ignores the RX data anyway.
 114
 115       We choose to support (a) by stalling the transmit engine if it would
 116       cause the RX FIFO to overflow.  In practice much transmit-only code
 117       falls into (a) because it flushes the RX FIFO to determine when
 118       the transfer has completed.  */
 119    while (s->tx_fifo_len && s->rx_fifo_len < 8) {
 120        DPRINTF("xfer\n");
 121        val = s->tx_fifo[i];
 122        if (s->cr1 & PL022_CR1_LBM) {
 123            /* Loopback mode.  */
 124        } else {
 125            val = ssi_transfer(s->ssi, val);
 126        }
 127        s->rx_fifo[o] = val & s->bitmask;
 128        i = (i + 1) & 7;
 129        o = (o + 1) & 7;
 130        s->tx_fifo_len--;
 131        s->rx_fifo_len++;
 132    }
 133    s->rx_fifo_head = o;
 134    pl022_update(s);
 135}
 136
 137static uint64_t pl022_read(void *opaque, hwaddr offset,
 138                           unsigned size)
 139{
 140    PL022State *s = (PL022State *)opaque;
 141    int val;
 142
 143    if (offset >= 0xfe0 && offset < 0x1000) {
 144        return pl022_id[(offset - 0xfe0) >> 2];
 145    }
 146    switch (offset) {
 147    case 0x00: /* CR0 */
 148      return s->cr0;
 149    case 0x04: /* CR1 */
 150      return s->cr1;
 151    case 0x08: /* DR */
 152        if (s->rx_fifo_len) {
 153            val = s->rx_fifo[(s->rx_fifo_head - s->rx_fifo_len) & 7];
 154            DPRINTF("RX %02x\n", val);
 155            s->rx_fifo_len--;
 156            pl022_xfer(s);
 157        } else {
 158            val = 0;
 159        }
 160        return val;
 161    case 0x0c: /* SR */
 162        return s->sr;
 163    case 0x10: /* CPSR */
 164        return s->cpsr;
 165    case 0x14: /* IMSC */
 166        return s->im;
 167    case 0x18: /* RIS */
 168        return s->is;
 169    case 0x1c: /* MIS */
 170        return s->im & s->is;
 171    case 0x20: /* DMACR */
 172        /* Not implemented.  */
 173        return 0;
 174    default:
 175        qemu_log_mask(LOG_GUEST_ERROR,
 176                      "pl022_read: Bad offset %x\n", (int)offset);
 177        return 0;
 178    }
 179}
 180
 181static void pl022_write(void *opaque, hwaddr offset,
 182                        uint64_t value, unsigned size)
 183{
 184    PL022State *s = (PL022State *)opaque;
 185
 186    switch (offset) {
 187    case 0x00: /* CR0 */
 188        s->cr0 = value;
 189        /* Clock rate and format are ignored.  */
 190        s->bitmask = (1 << ((value & 15) + 1)) - 1;
 191        break;
 192    case 0x04: /* CR1 */
 193        s->cr1 = value;
 194        if ((s->cr1 & (PL022_CR1_MS | PL022_CR1_SSE))
 195                   == (PL022_CR1_MS | PL022_CR1_SSE)) {
 196            BADF("SPI slave mode not implemented\n");
 197        }
 198        pl022_xfer(s);
 199        break;
 200    case 0x08: /* DR */
 201        if (s->tx_fifo_len < 8) {
 202            DPRINTF("TX %02x\n", (unsigned)value);
 203            s->tx_fifo[s->tx_fifo_head] = value & s->bitmask;
 204            s->tx_fifo_head = (s->tx_fifo_head + 1) & 7;
 205            s->tx_fifo_len++;
 206            pl022_xfer(s);
 207        }
 208        break;
 209    case 0x10: /* CPSR */
 210        /* Prescaler.  Ignored.  */
 211        s->cpsr = value & 0xff;
 212        break;
 213    case 0x14: /* IMSC */
 214        s->im = value;
 215        pl022_update(s);
 216        break;
 217    case 0x20: /* DMACR */
 218        if (value) {
 219            qemu_log_mask(LOG_UNIMP, "pl022: DMA not implemented\n");
 220        }
 221        break;
 222    default:
 223        qemu_log_mask(LOG_GUEST_ERROR,
 224                      "pl022_write: Bad offset %x\n", (int)offset);
 225    }
 226}
 227
 228static void pl022_reset(PL022State *s)
 229{
 230    s->rx_fifo_len = 0;
 231    s->tx_fifo_len = 0;
 232    s->im = 0;
 233    s->is = PL022_INT_TX;
 234    s->sr = PL022_SR_TFE | PL022_SR_TNF;
 235}
 236
 237static const MemoryRegionOps pl022_ops = {
 238    .read = pl022_read,
 239    .write = pl022_write,
 240    .endianness = DEVICE_NATIVE_ENDIAN,
 241};
 242
 243static int pl022_post_load(void *opaque, int version_id)
 244{
 245    PL022State *s = opaque;
 246
 247    if (s->tx_fifo_head < 0 ||
 248        s->tx_fifo_head >= ARRAY_SIZE(s->tx_fifo) ||
 249        s->rx_fifo_head < 0 ||
 250        s->rx_fifo_head >= ARRAY_SIZE(s->rx_fifo)) {
 251        return -1;
 252    }
 253    return 0;
 254}
 255
 256static const VMStateDescription vmstate_pl022 = {
 257    .name = "pl022_ssp",
 258    .version_id = 1,
 259    .minimum_version_id = 1,
 260    .post_load = pl022_post_load,
 261    .fields = (VMStateField[]) {
 262        VMSTATE_UINT32(cr0, PL022State),
 263        VMSTATE_UINT32(cr1, PL022State),
 264        VMSTATE_UINT32(bitmask, PL022State),
 265        VMSTATE_UINT32(sr, PL022State),
 266        VMSTATE_UINT32(cpsr, PL022State),
 267        VMSTATE_UINT32(is, PL022State),
 268        VMSTATE_UINT32(im, PL022State),
 269        VMSTATE_INT32(tx_fifo_head, PL022State),
 270        VMSTATE_INT32(rx_fifo_head, PL022State),
 271        VMSTATE_INT32(tx_fifo_len, PL022State),
 272        VMSTATE_INT32(rx_fifo_len, PL022State),
 273        VMSTATE_UINT16(tx_fifo[0], PL022State),
 274        VMSTATE_UINT16(rx_fifo[0], PL022State),
 275        VMSTATE_UINT16(tx_fifo[1], PL022State),
 276        VMSTATE_UINT16(rx_fifo[1], PL022State),
 277        VMSTATE_UINT16(tx_fifo[2], PL022State),
 278        VMSTATE_UINT16(rx_fifo[2], PL022State),
 279        VMSTATE_UINT16(tx_fifo[3], PL022State),
 280        VMSTATE_UINT16(rx_fifo[3], PL022State),
 281        VMSTATE_UINT16(tx_fifo[4], PL022State),
 282        VMSTATE_UINT16(rx_fifo[4], PL022State),
 283        VMSTATE_UINT16(tx_fifo[5], PL022State),
 284        VMSTATE_UINT16(rx_fifo[5], PL022State),
 285        VMSTATE_UINT16(tx_fifo[6], PL022State),
 286        VMSTATE_UINT16(rx_fifo[6], PL022State),
 287        VMSTATE_UINT16(tx_fifo[7], PL022State),
 288        VMSTATE_UINT16(rx_fifo[7], PL022State),
 289        VMSTATE_END_OF_LIST()
 290    }
 291};
 292
 293static int pl022_init(SysBusDevice *sbd)
 294{
 295    DeviceState *dev = DEVICE(sbd);
 296    PL022State *s = PL022(dev);
 297
 298    memory_region_init_io(&s->iomem, OBJECT(s), &pl022_ops, s, "pl022", 0x1000);
 299    sysbus_init_mmio(sbd, &s->iomem);
 300    sysbus_init_irq(sbd, &s->irq);
 301    s->ssi = ssi_create_bus(dev, "ssi");
 302    pl022_reset(s);
 303    vmstate_register(dev, -1, &vmstate_pl022, s);
 304    return 0;
 305}
 306
 307static void pl022_class_init(ObjectClass *klass, void *data)
 308{
 309    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
 310
 311    sdc->init = pl022_init;
 312}
 313
 314static const TypeInfo pl022_info = {
 315    .name          = TYPE_PL022,
 316    .parent        = TYPE_SYS_BUS_DEVICE,
 317    .instance_size = sizeof(PL022State),
 318    .class_init    = pl022_class_init,
 319};
 320
 321static void pl022_register_types(void)
 322{
 323    type_register_static(&pl022_info);
 324}
 325
 326type_init(pl022_register_types)
 327