Patchwork [RFC,1/7] cadence_uart: first revision

login
register
mail settings
Submitter Peter A. G. Crosthwaite
Date Jan. 23, 2012, 7:20 a.m.
Message ID <e8da0ee9fa24990d9e290aabe1b0e8124a61fc9c.1327302677.git.peter.crosthwaite@petalogix.com>
Download mbox | patch
Permalink /patch/137286/
State New
Headers show

Comments

Peter A. G. Crosthwaite - Jan. 23, 2012, 7:20 a.m.
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
John Linn - Jan. 24, 2012, 2:02 a.m.
> -----Original Message-----
> From: Peter A. G. Crosthwaite [mailto:peter.crosthwaite@petalogix.com]
> Sent: Sunday, January 22, 2012 11:20 PM
> To: qemu-devel@nongnu.org; monstr@monstr.eu;
> john.williams@petalogix.com; peter.crosthwaite@petalogix.com;
> edgar.iglesias@petalogix.com; Duy Le; John Linn
> Subject: [RFC PATCH 1/7] cadence_uart: first revision
> 
> Device model for Cadence UART
> 
> Signed-off-by: Peter A. G. Crosthwaite
> <peter.crosthwaite@petalogix.com>

Signed-off-by: John Linn <john.linn@xilinx.com>

*** My apologies, please ignore the legal footer below which I'm working
on getting removed ***

This email and any attachments are intended for the sole use of the named recipient(s) and contain(s) confidential information that may be proprietary, privileged or copyrighted under applicable law. If you are not the intended recipient, do not read, copy, or forward this email message or any attachments. Delete this email message and any attachments immediately.
Andreas Färber - Jan. 24, 2012, 8:07 a.m.
Am 23.01.2012 08:20, schrieb Peter A. G. Crosthwaite:
> 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

> 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

> +typedef struct {
[...]
> +} uart_state;

Coding Style mandates CamelCase for structs.

> +static void uart_register_devices(void)
> +{
> +    sysbus_register_dev("cadence_uart", sizeof(uart_state),
> +                        uart_init);

Please use sysbus_register_withprop() instead, for the upcoming QOM
conversion.

Could you also consider adding VMState for the devices, to allow
load/save? If not, they should at least be marked as unmigratable.

> +}

Andreas

Patch

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)