qemu/hw/milkymist-uart.c
<<
>>
Prefs
   1/*
   2 *  QEMU model of the Milkymist UART block.
   3 *
   4 *  Copyright (c) 2010 Michael Walle <michael@walle.cc>
   5 *
   6 * This library is free software; you can redistribute it and/or
   7 * modify it under the terms of the GNU Lesser General Public
   8 * License as published by the Free Software Foundation; either
   9 * version 2 of the License, or (at your option) any later version.
  10 *
  11 * This library is distributed in the hope that it will be useful,
  12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14 * Lesser General Public License for more details.
  15 *
  16 * You should have received a copy of the GNU Lesser General Public
  17 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
  18 *
  19 *
  20 * Specification available at:
  21 *   http://www.milkymist.org/socdoc/uart.pdf
  22 */
  23
  24#include "hw.h"
  25#include "sysbus.h"
  26#include "trace.h"
  27#include "qemu-char.h"
  28#include "qemu-error.h"
  29
  30enum {
  31    R_RXTX = 0,
  32    R_DIV,
  33    R_MAX
  34};
  35
  36struct MilkymistUartState {
  37    SysBusDevice busdev;
  38    CharDriverState *chr;
  39    qemu_irq rx_irq;
  40    qemu_irq tx_irq;
  41
  42    uint32_t regs[R_MAX];
  43};
  44typedef struct MilkymistUartState MilkymistUartState;
  45
  46static uint32_t uart_read(void *opaque, target_phys_addr_t addr)
  47{
  48    MilkymistUartState *s = opaque;
  49    uint32_t r = 0;
  50
  51    addr >>= 2;
  52    switch (addr) {
  53    case R_RXTX:
  54    case R_DIV:
  55        r = s->regs[addr];
  56        break;
  57
  58    default:
  59        error_report("milkymist_uart: read access to unknown register 0x"
  60                TARGET_FMT_plx, addr << 2);
  61        break;
  62    }
  63
  64    trace_milkymist_uart_memory_read(addr << 2, r);
  65
  66    return r;
  67}
  68
  69static void uart_write(void *opaque, target_phys_addr_t addr, uint32_t value)
  70{
  71    MilkymistUartState *s = opaque;
  72    unsigned char ch = value;
  73
  74    trace_milkymist_uart_memory_write(addr, value);
  75
  76    addr >>= 2;
  77    switch (addr) {
  78    case R_RXTX:
  79        if (s->chr) {
  80            qemu_chr_write(s->chr, &ch, 1);
  81        }
  82        trace_milkymist_uart_pulse_irq_tx();
  83        qemu_irq_pulse(s->tx_irq);
  84        break;
  85    case R_DIV:
  86        s->regs[addr] = value;
  87        break;
  88
  89    default:
  90        error_report("milkymist_uart: write access to unknown register 0x"
  91                TARGET_FMT_plx, addr << 2);
  92        break;
  93    }
  94}
  95
  96static CPUReadMemoryFunc * const uart_read_fn[] = {
  97    NULL,
  98    NULL,
  99    &uart_read,
 100};
 101
 102static CPUWriteMemoryFunc * const uart_write_fn[] = {
 103    NULL,
 104    NULL,
 105    &uart_write,
 106};
 107
 108static void uart_rx(void *opaque, const uint8_t *buf, int size)
 109{
 110    MilkymistUartState *s = opaque;
 111
 112    s->regs[R_RXTX] = *buf;
 113    trace_milkymist_uart_pulse_irq_rx();
 114    qemu_irq_pulse(s->rx_irq);
 115}
 116
 117static int uart_can_rx(void *opaque)
 118{
 119    return 1;
 120}
 121
 122static void uart_event(void *opaque, int event)
 123{
 124}
 125
 126static void milkymist_uart_reset(DeviceState *d)
 127{
 128    MilkymistUartState *s = container_of(d, MilkymistUartState, busdev.qdev);
 129    int i;
 130
 131    for (i = 0; i < R_MAX; i++) {
 132        s->regs[i] = 0;
 133    }
 134}
 135
 136static int milkymist_uart_init(SysBusDevice *dev)
 137{
 138    MilkymistUartState *s = FROM_SYSBUS(typeof(*s), dev);
 139    int uart_regs;
 140
 141    sysbus_init_irq(dev, &s->rx_irq);
 142    sysbus_init_irq(dev, &s->tx_irq);
 143
 144    uart_regs = cpu_register_io_memory(uart_read_fn, uart_write_fn, s,
 145            DEVICE_NATIVE_ENDIAN);
 146    sysbus_init_mmio(dev, R_MAX * 4, uart_regs);
 147
 148    s->chr = qdev_init_chardev(&dev->qdev);
 149    if (s->chr) {
 150        qemu_chr_add_handlers(s->chr, uart_can_rx, uart_rx, uart_event, s);
 151    }
 152
 153    return 0;
 154}
 155
 156static const VMStateDescription vmstate_milkymist_uart = {
 157    .name = "milkymist-uart",
 158    .version_id = 1,
 159    .minimum_version_id = 1,
 160    .minimum_version_id_old = 1,
 161    .fields      = (VMStateField[]) {
 162        VMSTATE_UINT32_ARRAY(regs, MilkymistUartState, R_MAX),
 163        VMSTATE_END_OF_LIST()
 164    }
 165};
 166
 167static SysBusDeviceInfo milkymist_uart_info = {
 168    .init = milkymist_uart_init,
 169    .qdev.name  = "milkymist-uart",
 170    .qdev.size  = sizeof(MilkymistUartState),
 171    .qdev.vmsd  = &vmstate_milkymist_uart,
 172    .qdev.reset = milkymist_uart_reset,
 173};
 174
 175static void milkymist_uart_register(void)
 176{
 177    sysbus_register_withprop(&milkymist_uart_info);
 178}
 179
 180device_init(milkymist_uart_register)
 181