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