qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [RFC PATCH 1/7] cadence_uart: first revision


From: Peter A. G. Crosthwaite
Subject: [Qemu-devel] [RFC PATCH 1/7] cadence_uart: first revision
Date: Mon, 23 Jan 2012 17:20:28 +1000

Device model for Cadence UART

Signed-off-by: Peter A. G. Crosthwaite <address@hidden>
---
 Makefile.target   |    1 +
 hw/cadence_uart.c |  619 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 620 insertions(+), 0 deletions(-)
 create mode 100644 hw/cadence_uart.c

diff --git a/Makefile.target b/Makefile.target
index bb18d72..824f6eb 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -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
diff --git a/hw/cadence_uart.c b/hw/cadence_uart.c
new file mode 100644
index 0000000..931ff2c
--- /dev/null
+++ b/hw/cadence_uart.c
@@ -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)
-- 
1.7.3.2




reply via email to

[Prev in Thread] Current Thread [Next in Thread]