qemu/hw/char/mcf_uart.c
<<
>>
Prefs
   1/*
   2 * ColdFire UART emulation.
   3 *
   4 * Copyright (c) 2007 CodeSourcery.
   5 *
   6 * This code is licensed under the GPL
   7 */
   8#include "qemu/osdep.h"
   9#include "hw/hw.h"
  10#include "hw/sysbus.h"
  11#include "hw/m68k/mcf.h"
  12#include "chardev/char-fe.h"
  13#include "exec/address-spaces.h"
  14
  15typedef struct {
  16    SysBusDevice parent_obj;
  17
  18    MemoryRegion iomem;
  19    uint8_t mr[2];
  20    uint8_t sr;
  21    uint8_t isr;
  22    uint8_t imr;
  23    uint8_t bg1;
  24    uint8_t bg2;
  25    uint8_t fifo[4];
  26    uint8_t tb;
  27    int current_mr;
  28    int fifo_len;
  29    int tx_enabled;
  30    int rx_enabled;
  31    qemu_irq irq;
  32    CharBackend chr;
  33} mcf_uart_state;
  34
  35#define TYPE_MCF_UART "mcf-uart"
  36#define MCF_UART(obj) OBJECT_CHECK(mcf_uart_state, (obj), TYPE_MCF_UART)
  37
  38/* UART Status Register bits.  */
  39#define MCF_UART_RxRDY  0x01
  40#define MCF_UART_FFULL  0x02
  41#define MCF_UART_TxRDY  0x04
  42#define MCF_UART_TxEMP  0x08
  43#define MCF_UART_OE     0x10
  44#define MCF_UART_PE     0x20
  45#define MCF_UART_FE     0x40
  46#define MCF_UART_RB     0x80
  47
  48/* Interrupt flags.  */
  49#define MCF_UART_TxINT  0x01
  50#define MCF_UART_RxINT  0x02
  51#define MCF_UART_DBINT  0x04
  52#define MCF_UART_COSINT 0x80
  53
  54/* UMR1 flags.  */
  55#define MCF_UART_BC0    0x01
  56#define MCF_UART_BC1    0x02
  57#define MCF_UART_PT     0x04
  58#define MCF_UART_PM0    0x08
  59#define MCF_UART_PM1    0x10
  60#define MCF_UART_ERR    0x20
  61#define MCF_UART_RxIRQ  0x40
  62#define MCF_UART_RxRTS  0x80
  63
  64static void mcf_uart_update(mcf_uart_state *s)
  65{
  66    s->isr &= ~(MCF_UART_TxINT | MCF_UART_RxINT);
  67    if (s->sr & MCF_UART_TxRDY)
  68        s->isr |= MCF_UART_TxINT;
  69    if ((s->sr & ((s->mr[0] & MCF_UART_RxIRQ)
  70                  ? MCF_UART_FFULL : MCF_UART_RxRDY)) != 0)
  71        s->isr |= MCF_UART_RxINT;
  72
  73    qemu_set_irq(s->irq, (s->isr & s->imr) != 0);
  74}
  75
  76uint64_t mcf_uart_read(void *opaque, hwaddr addr,
  77                       unsigned size)
  78{
  79    mcf_uart_state *s = (mcf_uart_state *)opaque;
  80    switch (addr & 0x3f) {
  81    case 0x00:
  82        return s->mr[s->current_mr];
  83    case 0x04:
  84        return s->sr;
  85    case 0x0c:
  86        {
  87            uint8_t val;
  88            int i;
  89
  90            if (s->fifo_len == 0)
  91                return 0;
  92
  93            val = s->fifo[0];
  94            s->fifo_len--;
  95            for (i = 0; i < s->fifo_len; i++)
  96                s->fifo[i] = s->fifo[i + 1];
  97            s->sr &= ~MCF_UART_FFULL;
  98            if (s->fifo_len == 0)
  99                s->sr &= ~MCF_UART_RxRDY;
 100            mcf_uart_update(s);
 101            qemu_chr_fe_accept_input(&s->chr);
 102            return val;
 103        }
 104    case 0x10:
 105        /* TODO: Implement IPCR.  */
 106        return 0;
 107    case 0x14:
 108        return s->isr;
 109    case 0x18:
 110        return s->bg1;
 111    case 0x1c:
 112        return s->bg2;
 113    default:
 114        return 0;
 115    }
 116}
 117
 118/* Update TxRDY flag and set data if present and enabled.  */
 119static void mcf_uart_do_tx(mcf_uart_state *s)
 120{
 121    if (s->tx_enabled && (s->sr & MCF_UART_TxEMP) == 0) {
 122        /* XXX this blocks entire thread. Rewrite to use
 123         * qemu_chr_fe_write and background I/O callbacks */
 124        qemu_chr_fe_write_all(&s->chr, (unsigned char *)&s->tb, 1);
 125        s->sr |= MCF_UART_TxEMP;
 126    }
 127    if (s->tx_enabled) {
 128        s->sr |= MCF_UART_TxRDY;
 129    } else {
 130        s->sr &= ~MCF_UART_TxRDY;
 131    }
 132}
 133
 134static void mcf_do_command(mcf_uart_state *s, uint8_t cmd)
 135{
 136    /* Misc command.  */
 137    switch ((cmd >> 4) & 7) {
 138    case 0: /* No-op.  */
 139        break;
 140    case 1: /* Reset mode register pointer.  */
 141        s->current_mr = 0;
 142        break;
 143    case 2: /* Reset receiver.  */
 144        s->rx_enabled = 0;
 145        s->fifo_len = 0;
 146        s->sr &= ~(MCF_UART_RxRDY | MCF_UART_FFULL);
 147        break;
 148    case 3: /* Reset transmitter.  */
 149        s->tx_enabled = 0;
 150        s->sr |= MCF_UART_TxEMP;
 151        s->sr &= ~MCF_UART_TxRDY;
 152        break;
 153    case 4: /* Reset error status.  */
 154        break;
 155    case 5: /* Reset break-change interrupt.  */
 156        s->isr &= ~MCF_UART_DBINT;
 157        break;
 158    case 6: /* Start break.  */
 159    case 7: /* Stop break.  */
 160        break;
 161    }
 162
 163    /* Transmitter command.  */
 164    switch ((cmd >> 2) & 3) {
 165    case 0: /* No-op.  */
 166        break;
 167    case 1: /* Enable.  */
 168        s->tx_enabled = 1;
 169        mcf_uart_do_tx(s);
 170        break;
 171    case 2: /* Disable.  */
 172        s->tx_enabled = 0;
 173        mcf_uart_do_tx(s);
 174        break;
 175    case 3: /* Reserved.  */
 176        fprintf(stderr, "mcf_uart: Bad TX command\n");
 177        break;
 178    }
 179
 180    /* Receiver command.  */
 181    switch (cmd & 3) {
 182    case 0: /* No-op.  */
 183        break;
 184    case 1: /* Enable.  */
 185        s->rx_enabled = 1;
 186        break;
 187    case 2:
 188        s->rx_enabled = 0;
 189        break;
 190    case 3: /* Reserved.  */
 191        fprintf(stderr, "mcf_uart: Bad RX command\n");
 192        break;
 193    }
 194}
 195
 196void mcf_uart_write(void *opaque, hwaddr addr,
 197                    uint64_t val, unsigned size)
 198{
 199    mcf_uart_state *s = (mcf_uart_state *)opaque;
 200    switch (addr & 0x3f) {
 201    case 0x00:
 202        s->mr[s->current_mr] = val;
 203        s->current_mr = 1;
 204        break;
 205    case 0x04:
 206        /* CSR is ignored.  */
 207        break;
 208    case 0x08: /* Command Register.  */
 209        mcf_do_command(s, val);
 210        break;
 211    case 0x0c: /* Transmit Buffer.  */
 212        s->sr &= ~MCF_UART_TxEMP;
 213        s->tb = val;
 214        mcf_uart_do_tx(s);
 215        break;
 216    case 0x10:
 217        /* ACR is ignored.  */
 218        break;
 219    case 0x14:
 220        s->imr = val;
 221        break;
 222    default:
 223        break;
 224    }
 225    mcf_uart_update(s);
 226}
 227
 228static void mcf_uart_reset(DeviceState *dev)
 229{
 230    mcf_uart_state *s = MCF_UART(dev);
 231
 232    s->fifo_len = 0;
 233    s->mr[0] = 0;
 234    s->mr[1] = 0;
 235    s->sr = MCF_UART_TxEMP;
 236    s->tx_enabled = 0;
 237    s->rx_enabled = 0;
 238    s->isr = 0;
 239    s->imr = 0;
 240}
 241
 242static void mcf_uart_push_byte(mcf_uart_state *s, uint8_t data)
 243{
 244    /* Break events overwrite the last byte if the fifo is full.  */
 245    if (s->fifo_len == 4)
 246        s->fifo_len--;
 247
 248    s->fifo[s->fifo_len] = data;
 249    s->fifo_len++;
 250    s->sr |= MCF_UART_RxRDY;
 251    if (s->fifo_len == 4)
 252        s->sr |= MCF_UART_FFULL;
 253
 254    mcf_uart_update(s);
 255}
 256
 257static void mcf_uart_event(void *opaque, int event)
 258{
 259    mcf_uart_state *s = (mcf_uart_state *)opaque;
 260
 261    switch (event) {
 262    case CHR_EVENT_BREAK:
 263        s->isr |= MCF_UART_DBINT;
 264        mcf_uart_push_byte(s, 0);
 265        break;
 266    default:
 267        break;
 268    }
 269}
 270
 271static int mcf_uart_can_receive(void *opaque)
 272{
 273    mcf_uart_state *s = (mcf_uart_state *)opaque;
 274
 275    return s->rx_enabled && (s->sr & MCF_UART_FFULL) == 0;
 276}
 277
 278static void mcf_uart_receive(void *opaque, const uint8_t *buf, int size)
 279{
 280    mcf_uart_state *s = (mcf_uart_state *)opaque;
 281
 282    mcf_uart_push_byte(s, buf[0]);
 283}
 284
 285static const MemoryRegionOps mcf_uart_ops = {
 286    .read = mcf_uart_read,
 287    .write = mcf_uart_write,
 288    .endianness = DEVICE_NATIVE_ENDIAN,
 289};
 290
 291static void mcf_uart_instance_init(Object *obj)
 292{
 293    SysBusDevice *dev = SYS_BUS_DEVICE(obj);
 294    mcf_uart_state *s = MCF_UART(dev);
 295
 296    memory_region_init_io(&s->iomem, obj, &mcf_uart_ops, s, "uart", 0x40);
 297    sysbus_init_mmio(dev, &s->iomem);
 298
 299    sysbus_init_irq(dev, &s->irq);
 300}
 301
 302static void mcf_uart_realize(DeviceState *dev, Error **errp)
 303{
 304    mcf_uart_state *s = MCF_UART(dev);
 305
 306    qemu_chr_fe_set_handlers(&s->chr, mcf_uart_can_receive, mcf_uart_receive,
 307                             mcf_uart_event, NULL, s, NULL, true);
 308}
 309
 310static Property mcf_uart_properties[] = {
 311    DEFINE_PROP_CHR("chardev", mcf_uart_state, chr),
 312    DEFINE_PROP_END_OF_LIST(),
 313};
 314
 315static void mcf_uart_class_init(ObjectClass *oc, void *data)
 316{
 317    DeviceClass *dc = DEVICE_CLASS(oc);
 318
 319    dc->realize = mcf_uart_realize;
 320    dc->reset = mcf_uart_reset;
 321    dc->props = mcf_uart_properties;
 322    set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
 323}
 324
 325static const TypeInfo mcf_uart_info = {
 326    .name          = TYPE_MCF_UART,
 327    .parent        = TYPE_SYS_BUS_DEVICE,
 328    .instance_size = sizeof(mcf_uart_state),
 329    .instance_init = mcf_uart_instance_init,
 330    .class_init    = mcf_uart_class_init,
 331};
 332
 333static void mcf_uart_register(void)
 334{
 335    type_register_static(&mcf_uart_info);
 336}
 337
 338type_init(mcf_uart_register)
 339
 340void *mcf_uart_init(qemu_irq irq, Chardev *chrdrv)
 341{
 342    DeviceState  *dev;
 343
 344    dev = qdev_create(NULL, TYPE_MCF_UART);
 345    if (chrdrv) {
 346        qdev_prop_set_chr(dev, "chardev", chrdrv);
 347    }
 348    qdev_init_nofail(dev);
 349
 350    sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq);
 351
 352    return dev;
 353}
 354
 355void mcf_uart_mm_init(hwaddr base, qemu_irq irq, Chardev *chrdrv)
 356{
 357    DeviceState  *dev;
 358
 359    dev = mcf_uart_init(irq, chrdrv);
 360    sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base);
 361}
 362