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