@@ -334,6 +334,7 @@ endif
obj-arm-y = integratorcp.o versatilepb.o arm_pic.o arm_timer.o
obj-arm-y += arm_boot.o pl011.o pl031.o pl050.o pl080.o pl110.o pl181.o pl190.o
obj-arm-y += versatile_pci.o
+obj-arm-y += cadence_uart.o
obj-arm-y += realview_gic.o realview.o arm_sysctl.o arm11mpcore.o a9mpcore.o
obj-arm-y += arm_l2x0.o
obj-arm-y += arm_mptimer.o
new file mode 100644
@@ -0,0 +1,619 @@
+/*
+ * Device model for Cadence UART
+ *
+ * Copyright (c) 2010 Xilinx Inc.
+ * Written by Haibing Ma
+ * M.Habib
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA
+ * 02139, USA.
+ */
+
+#include "sysbus.h"
+#include "qemu-char.h"
+#include "qemu-timer.h"
+
+#define UART_INTR_RTRIG 0x00000001
+#define UART_INTR_REMPTY 0x00000002
+#define UART_INTR_RFUL 0x00000004
+#define UART_INTR_TEMPTY 0x00000008
+#define UART_INTR_TFUL 0x00000010
+#define UART_INTR_ROVR 0x00000020
+#define UART_INTR_FRAME 0x00000040
+#define UART_INTR_PARE 0x00000080
+#define UART_INTR_TIMEOUT 0x00000100
+#define UART_INTR_DMSI 0x00000200
+#define UART_INTR_TTRIG 0x00000400
+#define UART_INTR_TNFUL 0x00000800
+#define UART_INTR_TOVR 0x00001000
+
+#define UART_CSR_RTRIG 0x00000001
+#define UART_CSR_REMPTY 0x00000002
+#define UART_CSR_RFUL 0x00000004
+#define UART_CSR_TEMPTY 0x00000008
+#define UART_CSR_TFUL 0x00000010
+#define UART_CSR_ROVR 0x00000020
+#define UART_CSR_FRAME 0x00000040
+#define UART_CSR_PARE 0x00000080
+#define UART_CSR_TIMEOUT 0x00000100
+#define UART_CSR_DMSI 0x00000200
+#define UART_CSR_RACTIVE 0x00000400
+#define UART_CSR_TACTIVE 0x00000800
+#define UART_CSR_FDELT 0x00001000
+#define UART_CSR_TTRIG 0x00002000
+#define UART_CSR_TNFUL 0x00004000
+
+#define UART_CR_STOPBRK 0x00000100
+#define UART_CR_STARTBRK 0x00000080
+#define UART_CR_TX_DIS 0x00000020
+#define UART_CR_TX_EN 0x00000010
+#define UART_CR_RX_DIS 0x00000008
+#define UART_CR_RX_EN 0x00000004
+#define UART_CR_TXRST 0x00000002
+#define UART_CR_RXRST 0x00000001
+#define UART_CR_RST_TO 0x00000040
+
+#define UART_MR_CLKSEL 0x00000001
+#define UART_MR_CHMODE_L_LOOP 0x00000200
+#define UART_MR_CHMODE_NORM 0x00000000
+#define UART_MR_STOPMODE_2_BIT 0x00000080
+#define UART_MR_STOPMODE_1_BIT 0x00000000
+#define UART_MR_PARITY_NONE 0x00000020
+#define UART_MR_PARITY_MARK 0x00000018
+#define UART_MR_PARITY_SPACE 0x00000010
+#define UART_MR_PARITY_ODD 0x00000008
+#define UART_MR_PARITY_EVEN 0x00000000
+#define UART_MR_CHARLEN_6_BIT 0x00000006
+#define UART_MR_CHARLEN_7_BIT 0x00000004
+#define UART_MR_CHARLEN_8_BIT 0x00000000
+
+#define UART_MR_CLKS 0x00000001
+#define UART_CHRL_SHFT 1
+#define UART_MR_CHRL 0x00000006
+#define UART_PAR_SHFT 3
+#define UART_MR_PAR 0x00000038
+#define UART_NBSTOP_SHFT 6
+#define UART_MR_NBSTOP 0x000000C0
+#define UART_CHMODE_SHFT 8
+#define UART_MR_CHMODE 0x00000300
+#define UART_UCLKEN_SHFT 10
+#define UART_MR_UCLKEN 0x00000400
+#define UART_IRMODE_SHFT 11
+#define UART_MR_IRMODE 0x00000800
+
+#define UART_PARITY_ODD 0x001
+#define UART_PARITY_EVEN 0x000
+#define UART_DATA_BITS_6 0x003
+#define UART_DATA_BITS_7 0x002
+#define UART_STOP_BITS_1 0x003
+#define UART_STOP_BITS_2 0x002
+#define RX_FIFO_SIZE 16
+#define TX_FIFO_SIZE 16
+#define RESET_TX_RX 0xFFFFFFFC
+#define UARK_INPUT_CLK 50000000
+
+#define NORMAL_MODE 0
+#define ECHO_MODE 1
+#define LOCAL_LOOPBACK 2
+#define REMOTE_LOOPBACK 3
+
+typedef struct {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+ uint32_t cr;
+ uint32_t mr;
+ uint32_t ier;
+ uint32_t idr;
+ uint32_t imr;
+ uint32_t cisr;
+ uint32_t brgr;
+ uint32_t rtor;
+ uint32_t rtrig;
+ uint32_t mcr;
+ uint32_t msr;
+ uint32_t csr;
+ uint8_t r_fifo[RX_FIFO_SIZE];
+ uint8_t t_fifo[TX_FIFO_SIZE];
+ uint32_t bdiv;
+ uint32_t fdel;
+ uint32_t pmin;
+ uint32_t pwid;
+ uint32_t ttrig;
+ int rx_rpos;
+ int rx_wpos;
+ int rx_count;
+ int tx_trigger;
+ int rx_trigger;
+ int tx_enabled;
+ int rx_enabled;
+ int parity;
+ int data_bits;
+ int stop_bits;
+ int sel_clk;
+ int ch_mode;
+ int ur_mode;
+ int ir_mode;
+ uint64_t char_tx_time;
+ CharDriverState *chr;
+ qemu_irq irq;
+ struct QEMUTimer *fifo_trigger_handle;
+ struct QEMUTimer *tx_time_handle;
+} uart_state;
+
+static void uart_update_status(uart_state *s)
+{
+ uint32_t flags;
+
+ flags = s->imr & s->cisr;
+
+ qemu_set_irq(s->irq, flags != 0);
+}
+
+static void fifo_trigger_update (void *opaque)
+{
+ uart_state *s = (uart_state *)opaque;
+
+ s->csr |= UART_CSR_TIMEOUT;
+ s->cisr |= UART_INTR_TIMEOUT;
+
+ uart_update_status(s);
+}
+
+static void uart_tx_redo (uart_state *s)
+{
+ uint64_t new_tx_time = qemu_get_clock_ns(vm_clock);
+
+ qemu_mod_timer(s->tx_time_handle, new_tx_time + s->char_tx_time);
+
+ s->csr |= UART_CSR_TEMPTY;
+ s->cisr |= UART_INTR_TEMPTY;
+
+ uart_update_status(s);
+}
+
+static void uart_tx_write (void *opaque)
+{
+ uart_state *s = (uart_state *)opaque;
+
+ uart_tx_redo (s);
+}
+
+static void uart_rx_reset(uart_state *s)
+{
+ s->rx_count = 0;
+ s->rx_rpos = 0;
+ s->rx_wpos = 0;
+
+ s->csr |= UART_CSR_REMPTY;
+ s->csr &= ~UART_CSR_RFUL;
+ s->csr &= ~UART_CSR_ROVR;
+ s->csr &= ~UART_CSR_TIMEOUT;
+
+ s->cisr &= ~UART_INTR_REMPTY;
+ s->cisr &= ~UART_INTR_RFUL;
+ s->cisr &= ~UART_INTR_ROVR;
+ s->cisr &= ~UART_INTR_TIMEOUT;
+}
+
+static void uart_tx_reset(uart_state *s)
+{
+ s->csr |= UART_CSR_TEMPTY;
+ s->csr &= ~UART_CSR_TFUL;
+
+ s->cisr &= ~UART_INTR_TEMPTY;
+ s->cisr &= ~UART_INTR_TFUL;
+}
+
+static void uart_send_breaks(uart_state *s)
+{
+ int break_enabled = 1;
+ qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_BREAK,
+ &break_enabled);
+
+}
+
+static void uart_parameters_setup(uart_state *s)
+{
+ QEMUSerialSetParams ssp;
+ unsigned int baud_rate , packet_size;
+
+ if (s->sel_clk) {
+ baud_rate = UARK_INPUT_CLK /8;
+ } else
+ baud_rate = UARK_INPUT_CLK ;
+
+ ssp.speed = baud_rate/(s->brgr * (s->bdiv + 1));
+ packet_size = 1;
+
+ switch (s->parity) {
+ case UART_PARITY_EVEN:
+ ssp.parity = 'E';
+ packet_size++;
+ break;
+ case UART_PARITY_ODD:
+ ssp.parity = 'O';
+ packet_size++;
+ break;
+ default:
+ ssp.parity = 'N';
+ break;
+ }
+
+ switch (s->data_bits) {
+ case UART_DATA_BITS_6:
+ ssp.data_bits = 6;
+ break;
+ case UART_DATA_BITS_7:
+ ssp.data_bits = 7;
+ break;
+ default:
+ ssp.data_bits = 8;
+ break;
+ }
+
+ if (s->stop_bits == UART_STOP_BITS_1) {
+ ssp.stop_bits = 1;
+ } else {
+ ssp.stop_bits = 2;
+ }
+
+ packet_size += ssp.data_bits + ssp.stop_bits;
+ s->char_tx_time = (get_ticks_per_sec() / ssp.speed) * packet_size;
+ qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp);
+}
+
+static void uart_mode_update(uart_state *s, uint32_t value)
+{
+ s->mr = value;
+
+ s->sel_clk = value & UART_MR_CLKS;
+
+ s->data_bits = (value >> UART_CHRL_SHFT) & UART_MR_CHRL;
+ s->parity = (value >> UART_PAR_SHFT) & UART_MR_PAR;
+ s->stop_bits = (value >> UART_NBSTOP_SHFT) & UART_MR_NBSTOP;
+
+ s->ch_mode = (value >> UART_CHMODE_SHFT) & UART_MR_CHMODE;
+ s->ur_mode = (value >> UART_UCLKEN_SHFT) & UART_MR_UCLKEN;
+ s->ir_mode = (value >> UART_IRMODE_SHFT) & UART_MR_IRMODE;
+
+ uart_parameters_setup(s);
+}
+
+static void uart_stop_breaks(uart_state *s)
+{
+ int break_enabled = 0;
+ qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_BREAK,
+ &break_enabled);
+
+}
+
+static int uart_can_receive(void *opaque)
+{
+ uart_state *s = (uart_state *)opaque;
+
+ return (RX_FIFO_SIZE - s->rx_count);
+}
+
+static void uart_ctrl_update(uart_state *s, uint32_t value)
+{
+ s->cr = value;
+
+ if (value & UART_CR_TXRST) {
+ uart_tx_reset(s);
+ }
+
+ if (value & UART_CR_RXRST) {
+ uart_rx_reset(s);
+ }
+
+ s->cr &= RESET_TX_RX;
+
+ if (value & UART_CR_TX_EN) {
+ if (!(s->cr & UART_CR_TX_DIS)) {
+ s->tx_enabled = 1;
+ uart_tx_redo (s);
+ }
+ }
+ if (value & UART_CR_TX_DIS) {
+ s->tx_enabled = 0;
+ }
+
+ if (value & UART_CR_RX_EN) {
+ if (!(s->cr & UART_CR_RX_DIS)) {
+ s->rx_enabled = 1;
+ }
+ }
+ if (value & UART_CR_RX_DIS) {
+ s->rx_enabled = 0;
+ }
+
+ if (value & UART_CR_STARTBRK) {
+ if (!(s->cr & UART_CR_STOPBRK)) {
+ uart_send_breaks(s);
+ }
+ }
+ if (value & UART_CR_STARTBRK) {
+ uart_stop_breaks(s);
+ }
+}
+
+static void uart_write_rx_fifo(void *opaque, const uint8_t *buf, int size)
+{
+ uart_state *s = (uart_state *)opaque;
+ uint64_t new_rx_time = qemu_get_clock_ns(vm_clock);
+ int i;
+
+ if (!s->rx_enabled)
+ return;
+
+ s->csr &= ~UART_CSR_REMPTY;
+ s->cisr &= ~UART_INTR_REMPTY;
+
+ if (s->rx_count == RX_FIFO_SIZE) {
+ s->cisr |= UART_INTR_ROVR;
+ s->csr |= UART_CSR_ROVR;
+ } else {
+ if (s->rx_wpos == RX_FIFO_SIZE)
+ s->rx_wpos = 0;
+
+ for (i = 0; i < size; i++) {
+ s->r_fifo[s->rx_wpos++] = buf[i];
+ s->rx_count++;
+
+ if (s->rx_count == RX_FIFO_SIZE) {
+ s->csr |= UART_CSR_RFUL;
+ s->cisr |= UART_INTR_RFUL;
+ break;
+ }
+
+ if (s->rx_count >= s->rtrig) {
+ s->cisr |= UART_INTR_RTRIG;
+ s->csr |= UART_CSR_RTRIG;
+ }
+ }
+ qemu_mod_timer(s->fifo_trigger_handle, new_rx_time +
+ (s->char_tx_time * 4));
+ }
+ uart_update_status(s);
+}
+
+static void uart_write_tx_fifo(uart_state *s, unsigned char *c)
+{
+ unsigned char ch = *c;
+
+ if (!s->tx_enabled)
+ return;
+
+ while (!(qemu_chr_fe_write(s->chr, &ch, 1)));
+}
+
+static void uart_receive(void *opaque, const uint8_t *buf, int size)
+{
+ uart_state *s = (uart_state *)opaque;
+ if (s->ch_mode == NORMAL_MODE || s->ch_mode == ECHO_MODE) {
+ uart_write_rx_fifo(opaque, buf, size);
+ }
+ if (s->ch_mode == REMOTE_LOOPBACK || s->ch_mode == ECHO_MODE) {
+ uart_write_tx_fifo(s, (unsigned char *)buf);
+ }
+}
+
+static void uart_event(void *opaque, int event)
+{
+ uart_state *s = (uart_state *)opaque;
+ uint8_t buf= '\0';
+
+ if (event == CHR_EVENT_BREAK) {
+ uart_write_rx_fifo(opaque, &buf, 1);
+ }
+
+ uart_update_status(s);
+}
+
+static void uart_read_rx_fifo(uart_state *s, uint32_t *c)
+{
+
+ if (!s->rx_enabled)
+ return;
+
+ s->csr &= ~UART_CSR_RFUL;
+ s->csr &= ~UART_CSR_ROVR;
+ s->cisr &= ~UART_INTR_ROVR;
+ s->cisr &= ~UART_INTR_RFUL;
+
+ if (s->rx_count > 0) {
+ s->rx_count--;
+
+ *c = s->r_fifo[s->rx_rpos];
+ ++s->rx_rpos;
+
+ if (s->rx_rpos == RX_FIFO_SIZE)
+ s->rx_rpos = 0;
+
+ if (s->rx_count == 0) {
+ s->cisr |= UART_INTR_REMPTY;
+ s->csr |= UART_CSR_REMPTY;
+ }
+
+ } else {
+ *c = 0;
+ s->cisr |= UART_INTR_REMPTY;
+ s->csr |= UART_CSR_REMPTY;
+ }
+
+ if (s->rx_count < s->rtrig) {
+ s->csr &= ~UART_CSR_RTRIG;
+ s->cisr &= ~UART_INTR_RTRIG;
+
+ }
+ uart_update_status(s);
+}
+
+static void uart_write(void *opaque, target_phys_addr_t offset,
+ uint64_t value, unsigned size)
+{
+ uart_state *s = (uart_state *)opaque;
+ switch (offset) {
+ case 0x00:
+ uart_ctrl_update(s, value);
+ break;
+ case 0x04:
+ uart_mode_update(s, value);
+ break;
+ case 0x08: /* ier */
+ s->imr |= value;
+ break;
+ case 0x0c: /* idr */
+ s->imr &= ~value;
+ break;
+ case 0x14: /* cisr */
+ s->cisr &= ~value;
+ break;
+ case 0x18: /* brgr */
+ s->brgr = value;
+ break;
+ case 0x1c: /* rtor */
+ s->rtor = value;
+ break;
+ case 0x20: /* rtrig */
+ s->rtrig = value;
+ break;
+ case 0x24: /* mcr */
+ s->mcr = value;
+ break;
+ case 0x30: /* UARTDR */
+ if (s->ch_mode == NORMAL_MODE) {
+ uart_write_tx_fifo(s, (unsigned char *) &value);
+ }
+ if (s->ch_mode == LOCAL_LOOPBACK) {
+ uart_write_rx_fifo(opaque, (unsigned char *) &value, 1);
+ }
+ break;
+ case 0x34: /* bdiv */
+ s->bdiv = value;
+ break;
+ case 0x38: /* fdel */
+ s->fdel = value;
+ break;
+ case 0x3c: /* pmin */
+ s->pmin = value;
+ break;
+ case 0x40: /* pwid */
+ s->pwid = value;
+ break;
+ case 0x44: /* ttrig */
+ s->ttrig = value;
+ break;
+ default:
+ return;
+ }
+}
+
+static uint64_t uart_read(void *opaque, target_phys_addr_t offset,
+ unsigned size)
+{
+ uart_state *s = (uart_state *)opaque;
+ uint32_t c = 0;
+ uint32_t value;
+
+ switch (offset) {
+ case 0x00:
+ return s->cr;
+ case 0x04:
+ return s->mr;
+ case 0x10:
+ return s->imr;
+ case 0x14:
+ value = s->cisr;
+ s->cisr = 0;
+ uart_update_status(s);
+ return value;
+ case 0x18:
+ return s->brgr;
+ case 0x1c:
+ return s->rtor;
+ case 0x20:
+ return s->rtrig;
+ case 0x24:
+ return s->mcr;
+ case 0x28:
+ return s->msr;
+ case 0x2c:
+ return s->csr;
+ case 0x30: /* Receive FIFO */
+ uart_read_rx_fifo (s, &c);
+ return c;
+ case 0x34:
+ return s->bdiv;
+ case 0x38:
+ return s->fdel;
+ case 0x3c:
+ return s->pmin;
+ case 0x40:
+ return s->pwid;
+ case 0x44:
+ return s->ttrig;
+ default:
+ return 0;
+ }
+}
+
+static const MemoryRegionOps uart_ops = {
+ .read = uart_read,
+ .write = uart_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int uart_init(SysBusDevice *dev)
+{
+ uart_state *s = FROM_SYSBUS(uart_state, dev);
+
+ memory_region_init_io(&s->iomem, &uart_ops, s, "uart", 0x1000);
+ sysbus_init_mmio(dev, &s->iomem);
+ sysbus_init_irq(dev, &s->irq);
+
+ s->fifo_trigger_handle = qemu_new_timer_ns(vm_clock,
+ (QEMUTimerCB *)fifo_trigger_update, s);
+
+ s->tx_time_handle = qemu_new_timer_ns(vm_clock,
+ (QEMUTimerCB *)uart_tx_write, s);
+
+ s->char_tx_time = (get_ticks_per_sec() / 9600) * 10;
+
+ s->chr = qdev_init_chardev(&dev->qdev);
+
+ s->cr = 0x00000128;
+ s->imr = 0;
+ s->cisr = 0;
+ s->rtrig = 0x00000020;
+ s->brgr = 0x0000000F;
+ s->ttrig = 0x00000020;
+
+ s->rx_rpos = 0;
+ s->rx_wpos = 0;
+ s->rx_count = 0;
+ s->tx_enabled = 1;
+ s->rx_enabled = 1;
+
+ if (s->chr) {
+ qemu_chr_add_handlers(s->chr, uart_can_receive, uart_receive,
+ uart_event, s);
+ }
+
+ return 0;
+}
+
+static void uart_register_devices(void)
+{
+ sysbus_register_dev("cadence_uart", sizeof(uart_state),
+ uart_init);
+}
+
+device_init(uart_register_devices)
Device model for Cadence UART Signed-off-by: Peter A. G. Crosthwaite <peter.crosthwaite@petalogix.com> --- Makefile.target | 1 + hw/cadence_uart.c | 619 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 620 insertions(+), 0 deletions(-) create mode 100644 hw/cadence_uart.c