qemu/hw/char/bcm2835_aux.c
<<
>>
Prefs
   1/*
   2 * BCM2835 (Raspberry Pi / Pi 2) Aux block (mini UART and SPI).
   3 * Copyright (c) 2015, Microsoft
   4 * Written by Andrew Baumann
   5 * Based on pl011.c, copyright terms below:
   6 *
   7 * Arm PrimeCell PL011 UART
   8 *
   9 * Copyright (c) 2006 CodeSourcery.
  10 * Written by Paul Brook
  11 *
  12 * This code is licensed under the GPL.
  13 *
  14 * At present only the core UART functions (data path for tx/rx) are
  15 * implemented. The following features/registers are unimplemented:
  16 *  - Line/modem control
  17 *  - Scratch register
  18 *  - Extra control
  19 *  - Baudrate
  20 *  - SPI interfaces
  21 */
  22
  23#include "qemu/osdep.h"
  24#include "hw/char/bcm2835_aux.h"
  25#include "hw/irq.h"
  26#include "hw/qdev-properties.h"
  27#include "hw/qdev-properties-system.h"
  28#include "migration/vmstate.h"
  29#include "qemu/log.h"
  30#include "qemu/module.h"
  31
  32#define AUX_IRQ         0x0
  33#define AUX_ENABLES     0x4
  34#define AUX_MU_IO_REG   0x40
  35#define AUX_MU_IER_REG  0x44
  36#define AUX_MU_IIR_REG  0x48
  37#define AUX_MU_LCR_REG  0x4c
  38#define AUX_MU_MCR_REG  0x50
  39#define AUX_MU_LSR_REG  0x54
  40#define AUX_MU_MSR_REG  0x58
  41#define AUX_MU_SCRATCH  0x5c
  42#define AUX_MU_CNTL_REG 0x60
  43#define AUX_MU_STAT_REG 0x64
  44#define AUX_MU_BAUD_REG 0x68
  45
  46/* bits in IER/IIR registers */
  47#define RX_INT  0x1
  48#define TX_INT  0x2
  49
  50static void bcm2835_aux_update(BCM2835AuxState *s)
  51{
  52    /* signal an interrupt if either:
  53     * 1. rx interrupt is enabled and we have a non-empty rx fifo, or
  54     * 2. the tx interrupt is enabled (since we instantly drain the tx fifo)
  55     */
  56    s->iir = 0;
  57    if ((s->ier & RX_INT) && s->read_count != 0) {
  58        s->iir |= RX_INT;
  59    }
  60    if (s->ier & TX_INT) {
  61        s->iir |= TX_INT;
  62    }
  63    qemu_set_irq(s->irq, s->iir != 0);
  64}
  65
  66static uint64_t bcm2835_aux_read(void *opaque, hwaddr offset, unsigned size)
  67{
  68    BCM2835AuxState *s = opaque;
  69    uint32_t c, res;
  70
  71    switch (offset) {
  72    case AUX_IRQ:
  73        return s->iir != 0;
  74
  75    case AUX_ENABLES:
  76        return 1; /* mini UART permanently enabled */
  77
  78    case AUX_MU_IO_REG:
  79        /* "DLAB bit set means access baudrate register" is NYI */
  80        c = s->read_fifo[s->read_pos];
  81        if (s->read_count > 0) {
  82            s->read_count--;
  83            if (++s->read_pos == BCM2835_AUX_RX_FIFO_LEN) {
  84                s->read_pos = 0;
  85            }
  86        }
  87        qemu_chr_fe_accept_input(&s->chr);
  88        bcm2835_aux_update(s);
  89        return c;
  90
  91    case AUX_MU_IER_REG:
  92        /* "DLAB bit set means access baudrate register" is NYI */
  93        return 0xc0 | s->ier; /* FIFO enables always read 1 */
  94
  95    case AUX_MU_IIR_REG:
  96        res = 0xc0; /* FIFO enables */
  97        /* The spec is unclear on what happens when both tx and rx
  98         * interrupts are active, besides that this cannot occur. At
  99         * present, we choose to prioritise the rx interrupt, since
 100         * the tx fifo is always empty. */
 101        if (s->read_count != 0) {
 102            res |= 0x4;
 103        } else {
 104            res |= 0x2;
 105        }
 106        if (s->iir == 0) {
 107            res |= 0x1;
 108        }
 109        return res;
 110
 111    case AUX_MU_LCR_REG:
 112        qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_LCR_REG unsupported\n", __func__);
 113        return 0;
 114
 115    case AUX_MU_MCR_REG:
 116        qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_MCR_REG unsupported\n", __func__);
 117        return 0;
 118
 119    case AUX_MU_LSR_REG:
 120        res = 0x60; /* tx idle, empty */
 121        if (s->read_count != 0) {
 122            res |= 0x1;
 123        }
 124        return res;
 125
 126    case AUX_MU_MSR_REG:
 127        qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_MSR_REG unsupported\n", __func__);
 128        return 0;
 129
 130    case AUX_MU_SCRATCH:
 131        qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_SCRATCH unsupported\n", __func__);
 132        return 0;
 133
 134    case AUX_MU_CNTL_REG:
 135        return 0x3; /* tx, rx enabled */
 136
 137    case AUX_MU_STAT_REG:
 138        res = 0x30e; /* space in the output buffer, empty tx fifo, idle tx/rx */
 139        if (s->read_count > 0) {
 140            res |= 0x1; /* data in input buffer */
 141            assert(s->read_count < BCM2835_AUX_RX_FIFO_LEN);
 142            res |= ((uint32_t)s->read_count) << 16; /* rx fifo fill level */
 143        }
 144        return res;
 145
 146    case AUX_MU_BAUD_REG:
 147        qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_BAUD_REG unsupported\n", __func__);
 148        return 0;
 149
 150    default:
 151        qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
 152                      __func__, offset);
 153        return 0;
 154    }
 155}
 156
 157static void bcm2835_aux_write(void *opaque, hwaddr offset, uint64_t value,
 158                              unsigned size)
 159{
 160    BCM2835AuxState *s = opaque;
 161    unsigned char ch;
 162
 163    switch (offset) {
 164    case AUX_ENABLES:
 165        if (value != 1) {
 166            qemu_log_mask(LOG_UNIMP, "%s: unsupported attempt to enable SPI"
 167                                     " or disable UART: 0x%"PRIx64"\n",
 168                          __func__, value);
 169        }
 170        break;
 171
 172    case AUX_MU_IO_REG:
 173        /* "DLAB bit set means access baudrate register" is NYI */
 174        ch = value;
 175        /* XXX this blocks entire thread. Rewrite to use
 176         * qemu_chr_fe_write and background I/O callbacks */
 177        qemu_chr_fe_write_all(&s->chr, &ch, 1);
 178        break;
 179
 180    case AUX_MU_IER_REG:
 181        /* "DLAB bit set means access baudrate register" is NYI */
 182        s->ier = value & (TX_INT | RX_INT);
 183        bcm2835_aux_update(s);
 184        break;
 185
 186    case AUX_MU_IIR_REG:
 187        if (value & 0x2) {
 188            s->read_count = 0;
 189        }
 190        break;
 191
 192    case AUX_MU_LCR_REG:
 193        qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_LCR_REG unsupported\n", __func__);
 194        break;
 195
 196    case AUX_MU_MCR_REG:
 197        qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_MCR_REG unsupported\n", __func__);
 198        break;
 199
 200    case AUX_MU_SCRATCH:
 201        qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_SCRATCH unsupported\n", __func__);
 202        break;
 203
 204    case AUX_MU_CNTL_REG:
 205        qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_CNTL_REG unsupported\n", __func__);
 206        break;
 207
 208    case AUX_MU_BAUD_REG:
 209        qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_BAUD_REG unsupported\n", __func__);
 210        break;
 211
 212    default:
 213        qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
 214                      __func__, offset);
 215    }
 216
 217    bcm2835_aux_update(s);
 218}
 219
 220static int bcm2835_aux_can_receive(void *opaque)
 221{
 222    BCM2835AuxState *s = opaque;
 223
 224    return s->read_count < BCM2835_AUX_RX_FIFO_LEN;
 225}
 226
 227static void bcm2835_aux_put_fifo(void *opaque, uint8_t value)
 228{
 229    BCM2835AuxState *s = opaque;
 230    int slot;
 231
 232    slot = s->read_pos + s->read_count;
 233    if (slot >= BCM2835_AUX_RX_FIFO_LEN) {
 234        slot -= BCM2835_AUX_RX_FIFO_LEN;
 235    }
 236    s->read_fifo[slot] = value;
 237    s->read_count++;
 238    if (s->read_count == BCM2835_AUX_RX_FIFO_LEN) {
 239        /* buffer full */
 240    }
 241    bcm2835_aux_update(s);
 242}
 243
 244static void bcm2835_aux_receive(void *opaque, const uint8_t *buf, int size)
 245{
 246    bcm2835_aux_put_fifo(opaque, *buf);
 247}
 248
 249static const MemoryRegionOps bcm2835_aux_ops = {
 250    .read = bcm2835_aux_read,
 251    .write = bcm2835_aux_write,
 252    .endianness = DEVICE_NATIVE_ENDIAN,
 253    .impl.min_access_size = 4,
 254    .impl.max_access_size = 4,
 255    .valid.min_access_size = 1,
 256    .valid.max_access_size = 4,
 257};
 258
 259static const VMStateDescription vmstate_bcm2835_aux = {
 260    .name = TYPE_BCM2835_AUX,
 261    .version_id = 1,
 262    .minimum_version_id = 1,
 263    .fields = (VMStateField[]) {
 264        VMSTATE_UINT8_ARRAY(read_fifo, BCM2835AuxState,
 265                            BCM2835_AUX_RX_FIFO_LEN),
 266        VMSTATE_UINT8(read_pos, BCM2835AuxState),
 267        VMSTATE_UINT8(read_count, BCM2835AuxState),
 268        VMSTATE_UINT8(ier, BCM2835AuxState),
 269        VMSTATE_UINT8(iir, BCM2835AuxState),
 270        VMSTATE_END_OF_LIST()
 271    }
 272};
 273
 274static void bcm2835_aux_init(Object *obj)
 275{
 276    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
 277    BCM2835AuxState *s = BCM2835_AUX(obj);
 278
 279    memory_region_init_io(&s->iomem, OBJECT(s), &bcm2835_aux_ops, s,
 280                          TYPE_BCM2835_AUX, 0x100);
 281    sysbus_init_mmio(sbd, &s->iomem);
 282    sysbus_init_irq(sbd, &s->irq);
 283}
 284
 285static void bcm2835_aux_realize(DeviceState *dev, Error **errp)
 286{
 287    BCM2835AuxState *s = BCM2835_AUX(dev);
 288
 289    qemu_chr_fe_set_handlers(&s->chr, bcm2835_aux_can_receive,
 290                             bcm2835_aux_receive, NULL, NULL, s, NULL, true);
 291}
 292
 293static Property bcm2835_aux_props[] = {
 294    DEFINE_PROP_CHR("chardev", BCM2835AuxState, chr),
 295    DEFINE_PROP_END_OF_LIST(),
 296};
 297
 298static void bcm2835_aux_class_init(ObjectClass *oc, void *data)
 299{
 300    DeviceClass *dc = DEVICE_CLASS(oc);
 301
 302    dc->realize = bcm2835_aux_realize;
 303    dc->vmsd = &vmstate_bcm2835_aux;
 304    set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
 305    device_class_set_props(dc, bcm2835_aux_props);
 306}
 307
 308static const TypeInfo bcm2835_aux_info = {
 309    .name          = TYPE_BCM2835_AUX,
 310    .parent        = TYPE_SYS_BUS_DEVICE,
 311    .instance_size = sizeof(BCM2835AuxState),
 312    .instance_init = bcm2835_aux_init,
 313    .class_init    = bcm2835_aux_class_init,
 314};
 315
 316static void bcm2835_aux_register_types(void)
 317{
 318    type_register_static(&bcm2835_aux_info);
 319}
 320
 321type_init(bcm2835_aux_register_types)
 322