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