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