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