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