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