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