qemu/hw/char/pl011.c
<<
>>
Prefs
   1/*
   2 * Arm PrimeCell PL011 UART
   3 *
   4 * Copyright (c) 2006 CodeSourcery.
   5 * Written by Paul Brook
   6 *
   7 * This code is licensed under the GPL.
   8 */
   9
  10/*
  11 * QEMU interface:
  12 *  + sysbus MMIO region 0: device registers
  13 *  + sysbus IRQ 0: UARTINTR (combined interrupt line)
  14 *  + sysbus IRQ 1: UARTRXINTR (receive FIFO interrupt line)
  15 *  + sysbus IRQ 2: UARTTXINTR (transmit FIFO interrupt line)
  16 *  + sysbus IRQ 3: UARTRTINTR (receive timeout interrupt line)
  17 *  + sysbus IRQ 4: UARTMSINTR (momem status interrupt line)
  18 *  + sysbus IRQ 5: UARTEINTR (error interrupt line)
  19 */
  20
  21#include "qemu/osdep.h"
  22#include "hw/char/pl011.h"
  23#include "hw/irq.h"
  24#include "hw/sysbus.h"
  25#include "migration/vmstate.h"
  26#include "chardev/char-fe.h"
  27#include "qemu/log.h"
  28#include "qemu/module.h"
  29#include "trace.h"
  30
  31#define PL011_INT_TX 0x20
  32#define PL011_INT_RX 0x10
  33
  34#define PL011_FLAG_TXFE 0x80
  35#define PL011_FLAG_RXFF 0x40
  36#define PL011_FLAG_TXFF 0x20
  37#define PL011_FLAG_RXFE 0x10
  38
  39/* Interrupt status bits in UARTRIS, UARTMIS, UARTIMSC */
  40#define INT_OE (1 << 10)
  41#define INT_BE (1 << 9)
  42#define INT_PE (1 << 8)
  43#define INT_FE (1 << 7)
  44#define INT_RT (1 << 6)
  45#define INT_TX (1 << 5)
  46#define INT_RX (1 << 4)
  47#define INT_DSR (1 << 3)
  48#define INT_DCD (1 << 2)
  49#define INT_CTS (1 << 1)
  50#define INT_RI (1 << 0)
  51#define INT_E (INT_OE | INT_BE | INT_PE | INT_FE)
  52#define INT_MS (INT_RI | INT_DSR | INT_DCD | INT_CTS)
  53
  54static const unsigned char pl011_id_arm[8] =
  55  { 0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
  56static const unsigned char pl011_id_luminary[8] =
  57  { 0x11, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1 };
  58
  59/* Which bits in the interrupt status matter for each outbound IRQ line ? */
  60static const uint32_t irqmask[] = {
  61    INT_E | INT_MS | INT_RT | INT_TX | INT_RX, /* combined IRQ */
  62    INT_RX,
  63    INT_TX,
  64    INT_RT,
  65    INT_MS,
  66    INT_E,
  67};
  68
  69static void pl011_update(PL011State *s)
  70{
  71    uint32_t flags;
  72    int i;
  73
  74    flags = s->int_level & s->int_enabled;
  75    trace_pl011_irq_state(flags != 0);
  76    for (i = 0; i < ARRAY_SIZE(s->irq); i++) {
  77        qemu_set_irq(s->irq[i], (flags & irqmask[i]) != 0);
  78    }
  79}
  80
  81static uint64_t pl011_read(void *opaque, hwaddr offset,
  82                           unsigned size)
  83{
  84    PL011State *s = (PL011State *)opaque;
  85    uint32_t c;
  86    uint64_t r;
  87
  88    switch (offset >> 2) {
  89    case 0: /* UARTDR */
  90        s->flags &= ~PL011_FLAG_RXFF;
  91        c = s->read_fifo[s->read_pos];
  92        if (s->read_count > 0) {
  93            s->read_count--;
  94            if (++s->read_pos == 16)
  95                s->read_pos = 0;
  96        }
  97        if (s->read_count == 0) {
  98            s->flags |= PL011_FLAG_RXFE;
  99        }
 100        if (s->read_count == s->read_trigger - 1)
 101            s->int_level &= ~ PL011_INT_RX;
 102        trace_pl011_read_fifo(s->read_count);
 103        s->rsr = c >> 8;
 104        pl011_update(s);
 105        qemu_chr_fe_accept_input(&s->chr);
 106        r = c;
 107        break;
 108    case 1: /* UARTRSR */
 109        r = s->rsr;
 110        break;
 111    case 6: /* UARTFR */
 112        r = s->flags;
 113        break;
 114    case 8: /* UARTILPR */
 115        r = s->ilpr;
 116        break;
 117    case 9: /* UARTIBRD */
 118        r = s->ibrd;
 119        break;
 120    case 10: /* UARTFBRD */
 121        r = s->fbrd;
 122        break;
 123    case 11: /* UARTLCR_H */
 124        r = s->lcr;
 125        break;
 126    case 12: /* UARTCR */
 127        r = s->cr;
 128        break;
 129    case 13: /* UARTIFLS */
 130        r = s->ifl;
 131        break;
 132    case 14: /* UARTIMSC */
 133        r = s->int_enabled;
 134        break;
 135    case 15: /* UARTRIS */
 136        r = s->int_level;
 137        break;
 138    case 16: /* UARTMIS */
 139        r = s->int_level & s->int_enabled;
 140        break;
 141    case 18: /* UARTDMACR */
 142        r = s->dmacr;
 143        break;
 144    case 0x3f8 ... 0x400:
 145        r = s->id[(offset - 0xfe0) >> 2];
 146        break;
 147    default:
 148        qemu_log_mask(LOG_GUEST_ERROR,
 149                      "pl011_read: Bad offset 0x%x\n", (int)offset);
 150        r = 0;
 151        break;
 152    }
 153
 154    trace_pl011_read(offset, r);
 155    return r;
 156}
 157
 158static void pl011_set_read_trigger(PL011State *s)
 159{
 160#if 0
 161    /* The docs say the RX interrupt is triggered when the FIFO exceeds
 162       the threshold.  However linux only reads the FIFO in response to an
 163       interrupt.  Triggering the interrupt when the FIFO is non-empty seems
 164       to make things work.  */
 165    if (s->lcr & 0x10)
 166        s->read_trigger = (s->ifl >> 1) & 0x1c;
 167    else
 168#endif
 169        s->read_trigger = 1;
 170}
 171
 172static void pl011_write(void *opaque, hwaddr offset,
 173                        uint64_t value, unsigned size)
 174{
 175    PL011State *s = (PL011State *)opaque;
 176    unsigned char ch;
 177
 178    trace_pl011_write(offset, value);
 179
 180    switch (offset >> 2) {
 181    case 0: /* UARTDR */
 182        /* ??? Check if transmitter is enabled.  */
 183        ch = value;
 184        /* XXX this blocks entire thread. Rewrite to use
 185         * qemu_chr_fe_write and background I/O callbacks */
 186        qemu_chr_fe_write_all(&s->chr, &ch, 1);
 187        s->int_level |= PL011_INT_TX;
 188        pl011_update(s);
 189        break;
 190    case 1: /* UARTRSR/UARTECR */
 191        s->rsr = 0;
 192        break;
 193    case 6: /* UARTFR */
 194        /* Writes to Flag register are ignored.  */
 195        break;
 196    case 8: /* UARTUARTILPR */
 197        s->ilpr = value;
 198        break;
 199    case 9: /* UARTIBRD */
 200        s->ibrd = value;
 201        break;
 202    case 10: /* UARTFBRD */
 203        s->fbrd = value;
 204        break;
 205    case 11: /* UARTLCR_H */
 206        /* Reset the FIFO state on FIFO enable or disable */
 207        if ((s->lcr ^ value) & 0x10) {
 208            s->read_count = 0;
 209            s->read_pos = 0;
 210        }
 211        s->lcr = value;
 212        pl011_set_read_trigger(s);
 213        break;
 214    case 12: /* UARTCR */
 215        /* ??? Need to implement the enable and loopback bits.  */
 216        s->cr = value;
 217        break;
 218    case 13: /* UARTIFS */
 219        s->ifl = value;
 220        pl011_set_read_trigger(s);
 221        break;
 222    case 14: /* UARTIMSC */
 223        s->int_enabled = value;
 224        pl011_update(s);
 225        break;
 226    case 17: /* UARTICR */
 227        s->int_level &= ~value;
 228        pl011_update(s);
 229        break;
 230    case 18: /* UARTDMACR */
 231        s->dmacr = value;
 232        if (value & 3) {
 233            qemu_log_mask(LOG_UNIMP, "pl011: DMA not implemented\n");
 234        }
 235        break;
 236    default:
 237        qemu_log_mask(LOG_GUEST_ERROR,
 238                      "pl011_write: Bad offset 0x%x\n", (int)offset);
 239    }
 240}
 241
 242static int pl011_can_receive(void *opaque)
 243{
 244    PL011State *s = (PL011State *)opaque;
 245    int r;
 246
 247    if (s->lcr & 0x10) {
 248        r = s->read_count < 16;
 249    } else {
 250        r = s->read_count < 1;
 251    }
 252    trace_pl011_can_receive(s->lcr, s->read_count, r);
 253    return r;
 254}
 255
 256static void pl011_put_fifo(void *opaque, uint32_t value)
 257{
 258    PL011State *s = (PL011State *)opaque;
 259    int slot;
 260
 261    slot = s->read_pos + s->read_count;
 262    if (slot >= 16)
 263        slot -= 16;
 264    s->read_fifo[slot] = value;
 265    s->read_count++;
 266    s->flags &= ~PL011_FLAG_RXFE;
 267    trace_pl011_put_fifo(value, s->read_count);
 268    if (!(s->lcr & 0x10) || s->read_count == 16) {
 269        trace_pl011_put_fifo_full();
 270        s->flags |= PL011_FLAG_RXFF;
 271    }
 272    if (s->read_count == s->read_trigger) {
 273        s->int_level |= PL011_INT_RX;
 274        pl011_update(s);
 275    }
 276}
 277
 278static void pl011_receive(void *opaque, const uint8_t *buf, int size)
 279{
 280    pl011_put_fifo(opaque, *buf);
 281}
 282
 283static void pl011_event(void *opaque, int event)
 284{
 285    if (event == CHR_EVENT_BREAK)
 286        pl011_put_fifo(opaque, 0x400);
 287}
 288
 289static const MemoryRegionOps pl011_ops = {
 290    .read = pl011_read,
 291    .write = pl011_write,
 292    .endianness = DEVICE_NATIVE_ENDIAN,
 293};
 294
 295static const VMStateDescription vmstate_pl011 = {
 296    .name = "pl011",
 297    .version_id = 2,
 298    .minimum_version_id = 2,
 299    .fields = (VMStateField[]) {
 300        VMSTATE_UINT32(readbuff, PL011State),
 301        VMSTATE_UINT32(flags, PL011State),
 302        VMSTATE_UINT32(lcr, PL011State),
 303        VMSTATE_UINT32(rsr, PL011State),
 304        VMSTATE_UINT32(cr, PL011State),
 305        VMSTATE_UINT32(dmacr, PL011State),
 306        VMSTATE_UINT32(int_enabled, PL011State),
 307        VMSTATE_UINT32(int_level, PL011State),
 308        VMSTATE_UINT32_ARRAY(read_fifo, PL011State, 16),
 309        VMSTATE_UINT32(ilpr, PL011State),
 310        VMSTATE_UINT32(ibrd, PL011State),
 311        VMSTATE_UINT32(fbrd, PL011State),
 312        VMSTATE_UINT32(ifl, PL011State),
 313        VMSTATE_INT32(read_pos, PL011State),
 314        VMSTATE_INT32(read_count, PL011State),
 315        VMSTATE_INT32(read_trigger, PL011State),
 316        VMSTATE_END_OF_LIST()
 317    }
 318};
 319
 320static Property pl011_properties[] = {
 321    DEFINE_PROP_CHR("chardev", PL011State, chr),
 322    DEFINE_PROP_END_OF_LIST(),
 323};
 324
 325static void pl011_init(Object *obj)
 326{
 327    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
 328    PL011State *s = PL011(obj);
 329    int i;
 330
 331    memory_region_init_io(&s->iomem, OBJECT(s), &pl011_ops, s, "pl011", 0x1000);
 332    sysbus_init_mmio(sbd, &s->iomem);
 333    for (i = 0; i < ARRAY_SIZE(s->irq); i++) {
 334        sysbus_init_irq(sbd, &s->irq[i]);
 335    }
 336
 337    s->read_trigger = 1;
 338    s->ifl = 0x12;
 339    s->cr = 0x300;
 340    s->flags = 0x90;
 341
 342    s->id = pl011_id_arm;
 343}
 344
 345static void pl011_realize(DeviceState *dev, Error **errp)
 346{
 347    PL011State *s = PL011(dev);
 348
 349    qemu_chr_fe_set_handlers(&s->chr, pl011_can_receive, pl011_receive,
 350                             pl011_event, NULL, s, NULL, true);
 351}
 352
 353static void pl011_class_init(ObjectClass *oc, void *data)
 354{
 355    DeviceClass *dc = DEVICE_CLASS(oc);
 356
 357    dc->realize = pl011_realize;
 358    dc->vmsd = &vmstate_pl011;
 359    dc->props = pl011_properties;
 360}
 361
 362static const TypeInfo pl011_arm_info = {
 363    .name          = TYPE_PL011,
 364    .parent        = TYPE_SYS_BUS_DEVICE,
 365    .instance_size = sizeof(PL011State),
 366    .instance_init = pl011_init,
 367    .class_init    = pl011_class_init,
 368};
 369
 370static void pl011_luminary_init(Object *obj)
 371{
 372    PL011State *s = PL011(obj);
 373
 374    s->id = pl011_id_luminary;
 375}
 376
 377static const TypeInfo pl011_luminary_info = {
 378    .name          = TYPE_PL011_LUMINARY,
 379    .parent        = TYPE_PL011,
 380    .instance_init = pl011_luminary_init,
 381};
 382
 383static void pl011_register_types(void)
 384{
 385    type_register_static(&pl011_arm_info);
 386    type_register_static(&pl011_luminary_info);
 387}
 388
 389type_init(pl011_register_types)
 390