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