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