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