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