From 11f0d528b55d9cdf093be6dde3d7ccff22a3a331 Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Wed, 22 Feb 2017 09:01:40 +0100 Subject: bsp/xilinx-zynq: Add interrupt support to UART --- c/src/lib/libbsp/arm/xilinx-zynq/configure.ac | 3 + .../lib/libbsp/arm/xilinx-zynq/console/zynq-uart.c | 102 +++++++++++++++++++-- .../arm/xilinx-zynq/include/zynq-uart-regs.h | 2 + .../lib/libbsp/arm/xilinx-zynq/include/zynq-uart.h | 1 + 4 files changed, 100 insertions(+), 8 deletions(-) (limited to 'c/src/lib/libbsp/arm/xilinx-zynq') diff --git a/c/src/lib/libbsp/arm/xilinx-zynq/configure.ac b/c/src/lib/libbsp/arm/xilinx-zynq/configure.ac index 41dde99ee7..99140c3346 100644 --- a/c/src/lib/libbsp/arm/xilinx-zynq/configure.ac +++ b/c/src/lib/libbsp/arm/xilinx-zynq/configure.ac @@ -59,6 +59,9 @@ simulation times.]) RTEMS_BSPOPTS_SET([BSP_CONSOLE_MINOR],[*],[1]) RTEMS_BSPOPTS_HELP([BSP_CONSOLE_MINOR],[minor number of console device]) +RTEMS_BSPOPTS_SET([ZYNQ_CONSOLE_USE_INTERRUPTS],[*],[1]) +RTEMS_BSPOPTS_HELP([ZYNQ_CONSOLE_USE_INTERRUPTS],[use interrupt driven mode for console devices (used by default)]) + ZYNQ_CPUS="1" RTEMS_CHECK_SMP AM_CONDITIONAL(HAS_SMP,[test "$rtems_cv_HAS_SMP" = "yes"]) diff --git a/c/src/lib/libbsp/arm/xilinx-zynq/console/zynq-uart.c b/c/src/lib/libbsp/arm/xilinx-zynq/console/zynq-uart.c index 05c8e5424e..fa91f3f46e 100644 --- a/c/src/lib/libbsp/arm/xilinx-zynq/console/zynq-uart.c +++ b/c/src/lib/libbsp/arm/xilinx-zynq/console/zynq-uart.c @@ -14,6 +14,7 @@ #include #include +#include #include @@ -128,6 +129,34 @@ void zynq_uart_initialize(rtems_termios_device_context *base) | ZYNQ_UART_CONTROL_RSTTO; } +#ifdef ZYNQ_CONSOLE_USE_INTERRUPTS +static void zynq_uart_interrupt(void *arg) +{ + rtems_termios_tty *tty = arg; + zynq_uart_context *ctx = rtems_termios_get_device_context(tty); + volatile zynq_uart *regs = ctx->regs; + uint32_t channel_sts; + + if ((regs->irq_sts & (ZYNQ_UART_TIMEOUT | ZYNQ_UART_RTRIG)) != 0) { + regs->irq_sts = ZYNQ_UART_TIMEOUT | ZYNQ_UART_RTRIG; + + do { + char c = (char) ZYNQ_UART_TX_RX_FIFO_FIFO_GET(regs->tx_rx_fifo); + + rtems_termios_enqueue_raw_characters(tty, &c, 1); + + channel_sts = regs->channel_sts; + } while ((channel_sts & ZYNQ_UART_CHANNEL_STS_REMPTY) == 0); + } else { + channel_sts = regs->channel_sts; + } + + if (ctx->transmitting && (channel_sts & ZYNQ_UART_CHANNEL_STS_TEMPTY) != 0) { + rtems_termios_dequeue_characters(tty, 1); + } +} +#endif + static bool zynq_uart_first_open( rtems_termios_tty *tty, rtems_termios_device_context *base, @@ -135,12 +164,49 @@ static bool zynq_uart_first_open( rtems_libio_open_close_args_t *args ) { +#ifdef ZYNQ_CONSOLE_USE_INTERRUPTS + zynq_uart_context *ctx = (zynq_uart_context *) base; + volatile zynq_uart *regs = ctx->regs; + rtems_status_code sc; +#endif + rtems_termios_set_initial_baud(tty, ZYNQ_UART_DEFAULT_BAUD); zynq_uart_initialize(base); +#ifdef ZYNQ_CONSOLE_USE_INTERRUPTS + regs->rx_timeout = 32; + regs->rx_fifo_trg_lvl = ZYNQ_UART_FIFO_DEPTH / 2; + regs->irq_dis = 0xffffffff; + regs->irq_sts = 0xffffffff; + regs->irq_en = ZYNQ_UART_RTRIG | ZYNQ_UART_TIMEOUT; + sc = rtems_interrupt_handler_install( + ctx->irq, + "UART", + RTEMS_INTERRUPT_SHARED, + zynq_uart_interrupt, + tty + ); + if (sc != RTEMS_SUCCESSFUL) { + return false; + } +#endif + return true; } +#ifdef ZYNQ_CONSOLE_USE_INTERRUPTS +static void zynq_uart_last_close( + rtems_termios_tty *tty, + rtems_termios_device_context *base, + rtems_libio_open_close_args_t *args +) +{ + zynq_uart_context *ctx = (zynq_uart_context *) base; + + rtems_interrupt_handler_remove(ctx->irq, zynq_uart_interrupt, tty); +} +#endif + int zynq_uart_read_polled(rtems_termios_device_context *base) { zynq_uart_context *ctx = (zynq_uart_context *) base; @@ -168,17 +234,32 @@ void zynq_uart_write_polled( regs->tx_rx_fifo = ZYNQ_UART_TX_RX_FIFO_FIFO(c); } -static void zynq_uart_write_support_polled( +static void zynq_uart_write_support( rtems_termios_device_context *base, - const char *s, - size_t n + const char *buf, + size_t len ) { - ssize_t i = 0; +#ifdef ZYNQ_CONSOLE_USE_INTERRUPTS + zynq_uart_context *ctx = (zynq_uart_context *) base; + volatile zynq_uart *regs = ctx->regs; + + if (len > 0) { + ctx->transmitting = true; + regs->irq_sts = ZYNQ_UART_TEMPTY; + regs->irq_en = ZYNQ_UART_TEMPTY; + regs->tx_rx_fifo = ZYNQ_UART_TX_RX_FIFO_FIFO(buf[0]); + } else { + ctx->transmitting = false; + regs->irq_dis = ZYNQ_UART_TEMPTY; + } +#else + ssize_t i; - for (i = 0; i < n; ++i) { - zynq_uart_write_polled(base, s[i]); + for (i = 0; i < len; ++i) { + zynq_uart_write_polled(base, buf[i]); } +#endif } static bool zynq_uart_set_attributes( @@ -209,10 +290,15 @@ static bool zynq_uart_set_attributes( const rtems_termios_device_handler zynq_uart_handler = { .first_open = zynq_uart_first_open, - .write = zynq_uart_write_support_polled, - .poll_read = zynq_uart_read_polled, .set_attributes = zynq_uart_set_attributes, + .write = zynq_uart_write_support, +#ifdef ZYNQ_CONSOLE_USE_INTERRUPTS + .last_close = zynq_uart_last_close, + .mode = TERMIOS_IRQ_DRIVEN +#else + .poll_read = zynq_uart_read_polled, .mode = TERMIOS_POLLED +#endif }; void zynq_uart_reset_tx_flush(zynq_uart_context *ctx) diff --git a/c/src/lib/libbsp/arm/xilinx-zynq/include/zynq-uart-regs.h b/c/src/lib/libbsp/arm/xilinx-zynq/include/zynq-uart-regs.h index e72b93a9dc..127f272fd9 100644 --- a/c/src/lib/libbsp/arm/xilinx-zynq/include/zynq-uart-regs.h +++ b/c/src/lib/libbsp/arm/xilinx-zynq/include/zynq-uart-regs.h @@ -30,6 +30,8 @@ #include +#define ZYNQ_UART_FIFO_DEPTH 64 + typedef struct zynq_uart { uint32_t control; #define ZYNQ_UART_CONTROL_STPBRK BSP_BIT32(8) diff --git a/c/src/lib/libbsp/arm/xilinx-zynq/include/zynq-uart.h b/c/src/lib/libbsp/arm/xilinx-zynq/include/zynq-uart.h index 57412d7ec9..4d3edfabef 100644 --- a/c/src/lib/libbsp/arm/xilinx-zynq/include/zynq-uart.h +++ b/c/src/lib/libbsp/arm/xilinx-zynq/include/zynq-uart.h @@ -36,6 +36,7 @@ extern "C" { typedef struct { rtems_termios_device_context base; volatile struct zynq_uart *regs; + bool transmitting; rtems_vector_number irq; } zynq_uart_context; -- cgit v1.2.3