From 691d0edd34f25a883c5dd0a56051d087b88e4fa4 Mon Sep 17 00:00:00 2001 From: Chris Johns Date: Tue, 17 Aug 2021 17:57:41 +1000 Subject: arm/xilinx: Fix zynq-uart interrupt receive - Trigger on a single character entering the RX FIFO - Disable the RX timeout - Send up to a FIFO full of data --- bsps/include/dev/serial/zynq-uart.h | 1 + bsps/shared/dev/serial/zynq-uart-polled.c | 45 +++++++++--------- bsps/shared/dev/serial/zynq-uart.c | 78 +++++++++++++++++-------------- 3 files changed, 67 insertions(+), 57 deletions(-) diff --git a/bsps/include/dev/serial/zynq-uart.h b/bsps/include/dev/serial/zynq-uart.h index 220d9b7717..b21e16f6de 100644 --- a/bsps/include/dev/serial/zynq-uart.h +++ b/bsps/include/dev/serial/zynq-uart.h @@ -52,6 +52,7 @@ extern "C" { typedef struct { rtems_termios_device_context base; volatile struct zynq_uart *regs; + int tx_queued; bool transmitting; rtems_vector_number irq; } zynq_uart_context; diff --git a/bsps/shared/dev/serial/zynq-uart-polled.c b/bsps/shared/dev/serial/zynq-uart-polled.c index 95c51dea11..6865fa8d6f 100644 --- a/bsps/shared/dev/serial/zynq-uart-polled.c +++ b/bsps/shared/dev/serial/zynq-uart-polled.c @@ -121,35 +121,35 @@ void zynq_uart_initialize(rtems_termios_device_context *base) volatile zynq_uart *regs = ctx->regs; uint32_t brgr = 0x3e; uint32_t bauddiv = 0x6; + uint32_t mode_clks = regs->mode & ZYNQ_UART_MODE_CLKS; - zynq_uart_reset_tx_flush(ctx); + while ((regs->channel_sts & ZYNQ_UART_CHANNEL_STS_TEMPTY) == 0 || + (regs->channel_sts & ZYNQ_UART_CHANNEL_STS_TACTIVE) != 0) { + /* Wait */ + } - zynq_cal_baud_rate(ZYNQ_UART_DEFAULT_BAUD, &brgr, &bauddiv, regs->mode); + zynq_cal_baud_rate(ZYNQ_UART_DEFAULT_BAUD, &brgr, &bauddiv, mode_clks); - regs->control &= ~(ZYNQ_UART_CONTROL_RXEN | ZYNQ_UART_CONTROL_TXEN); - regs->control = ZYNQ_UART_CONTROL_RXDIS - | ZYNQ_UART_CONTROL_TXDIS; - regs->mode = ZYNQ_UART_MODE_CHMODE(ZYNQ_UART_MODE_CHMODE_NORMAL) - | ZYNQ_UART_MODE_PAR(ZYNQ_UART_MODE_PAR_NONE) - | ZYNQ_UART_MODE_CHRL(ZYNQ_UART_MODE_CHRL_8); + regs->control = 0; + regs->control = ZYNQ_UART_CONTROL_RXDIS | ZYNQ_UART_CONTROL_TXDIS; regs->baud_rate_gen = ZYNQ_UART_BAUD_RATE_GEN_CD(brgr); regs->baud_rate_div = ZYNQ_UART_BAUD_RATE_DIV_BDIV(bauddiv); /* A Tx/Rx logic reset must be issued after baud rate manipulation */ - regs->control = ZYNQ_UART_CONTROL_RXDIS - | ZYNQ_UART_CONTROL_TXDIS - | ZYNQ_UART_CONTROL_RXRES - | ZYNQ_UART_CONTROL_TXRES; + regs->control = ZYNQ_UART_CONTROL_RXDIS | ZYNQ_UART_CONTROL_TXDIS; + regs->control = ZYNQ_UART_CONTROL_RXRES | ZYNQ_UART_CONTROL_TXRES; regs->rx_fifo_trg_lvl = ZYNQ_UART_RX_FIFO_TRG_LVL_RTRIG(0); regs->rx_timeout = ZYNQ_UART_RX_TIMEOUT_RTO(0); - regs->control = ZYNQ_UART_CONTROL_RXEN - | ZYNQ_UART_CONTROL_TXEN - | ZYNQ_UART_CONTROL_RSTTO; + regs->control = ZYNQ_UART_CONTROL_RXEN | ZYNQ_UART_CONTROL_TXEN; + regs->mode = ZYNQ_UART_MODE_CHMODE(ZYNQ_UART_MODE_CHMODE_NORMAL) + | ZYNQ_UART_MODE_PAR(ZYNQ_UART_MODE_PAR_NONE) + | ZYNQ_UART_MODE_CHRL(ZYNQ_UART_MODE_CHRL_8) + | mode_clks; - /* - * Some ZynqMP UARTs have a hardware bug that causes TX/RX logic restarts to - * require a kick after baud rate registers are initialized. - */ - zynq_uart_write_polled(base, 0); + while (zynq_uart_read_polled(base) >= 0) { + /* Drop */ + } + + zynq_uart_reset_tx_flush(ctx); } int zynq_uart_read_polled(rtems_termios_device_context *base) @@ -172,7 +172,7 @@ void zynq_uart_write_polled( zynq_uart_context *ctx = (zynq_uart_context *) base; volatile zynq_uart *regs = ctx->regs; - while ((regs->channel_sts & ZYNQ_UART_CHANNEL_STS_TFUL) != 0) { + while ((regs->channel_sts & ZYNQ_UART_CHANNEL_STS_TNFUL) != 0) { /* Wait */ } @@ -187,7 +187,8 @@ void zynq_uart_reset_tx_flush(zynq_uart_context *ctx) while (c-- > 0) zynq_uart_write_polled(&ctx->base, '\r'); - while ((regs->channel_sts & ZYNQ_UART_CHANNEL_STS_TEMPTY) == 0) { + while ((regs->channel_sts & ZYNQ_UART_CHANNEL_STS_TEMPTY) == 0 || + (regs->channel_sts & ZYNQ_UART_CHANNEL_STS_TACTIVE) != 0) { /* Wait */ } } diff --git a/bsps/shared/dev/serial/zynq-uart.c b/bsps/shared/dev/serial/zynq-uart.c index 8503e31d49..8ff1d25da0 100644 --- a/bsps/shared/dev/serial/zynq-uart.c +++ b/bsps/shared/dev/serial/zynq-uart.c @@ -37,24 +37,24 @@ 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 ((regs->irq_sts & ZYNQ_UART_RTRIG) != 0) { + char buf[32]; + int c = 0; + regs->irq_sts = ZYNQ_UART_RTRIG; + while (c < sizeof(buf) && + (regs->channel_sts & ZYNQ_UART_CHANNEL_STS_REMPTY) == 0) { + buf[c++] = (char) ZYNQ_UART_TX_RX_FIFO_FIFO_GET(regs->tx_rx_fifo); + } + rtems_termios_enqueue_raw_characters(tty, buf, c); } - if (ctx->transmitting && (channel_sts & ZYNQ_UART_CHANNEL_STS_TEMPTY) != 0) { - rtems_termios_dequeue_characters(tty, 1); + if (ctx->transmitting) { + int sent = ctx->tx_queued; + regs->irq_dis = ZYNQ_UART_TEMPTY; + ctx->transmitting = false; + ctx->tx_queued = 0; + rtems_termios_dequeue_characters(tty, sent); } } #endif @@ -76,11 +76,10 @@ static bool zynq_uart_first_open( zynq_uart_initialize(base); #ifdef ZYNQ_CONSOLE_USE_INTERRUPTS - regs->rx_timeout = 32; - regs->rx_fifo_trg_lvl = ZYNQ_UART_FIFO_DEPTH / 2; + regs->rx_fifo_trg_lvl = 1; regs->irq_dis = 0xffffffff; regs->irq_sts = 0xffffffff; - regs->irq_en = ZYNQ_UART_RTRIG | ZYNQ_UART_TIMEOUT; + regs->irq_en = ZYNQ_UART_RTRIG; sc = rtems_interrupt_handler_install( ctx->irq, "UART", @@ -119,18 +118,23 @@ static void zynq_uart_write_support( zynq_uart_context *ctx = (zynq_uart_context *) base; volatile zynq_uart *regs = ctx->regs; + regs->irq_dis = ZYNQ_UART_TEMPTY; + if (len > 0) { - ctx->transmitting = true; + const char *p = &buf[0]; regs->irq_sts = ZYNQ_UART_TEMPTY; + while (((regs->channel_sts & ZYNQ_UART_CHANNEL_STS_TNFUL) == 0) && + len > 0) { + regs->tx_rx_fifo = ZYNQ_UART_TX_RX_FIFO_FIFO(*p); + ++p; + ++ctx->tx_queued; + --len; + } + ctx->transmitting = true; 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 < len; ++i) { zynq_uart_write_polled(base, buf[i]); } @@ -164,6 +168,7 @@ static bool zynq_uart_set_attributes( /* * Configure the mode register */ + mode = regs->mode & ZYNQ_UART_MODE_CLKS; mode |= ZYNQ_UART_MODE_CHMODE(ZYNQ_UART_MODE_CHMODE_NORMAL); /* @@ -208,22 +213,25 @@ static bool zynq_uart_set_attributes( mode |= ZYNQ_UART_MODE_NBSTOP(ZYNQ_UART_MODE_NBSTOP_STOP_1); } - regs->control &= ~(ZYNQ_UART_CONTROL_RXEN | ZYNQ_UART_CONTROL_TXEN); - regs->mode = mode; + /* + * Wait for any data in the TXFIFO to be sent then wait while the + * transmiter is active. + */ + while ((regs->channel_sts & ZYNQ_UART_CHANNEL_STS_TEMPTY) == 0 || + (regs->channel_sts & ZYNQ_UART_CHANNEL_STS_TACTIVE) != 0) { + /* Wait */ + } + + regs->control = ZYNQ_UART_CONTROL_RXDIS | ZYNQ_UART_CONTROL_TXDIS; /* Ignore baud rate of B0. There are no modem control lines to de-assert */ if (baud > 0) { regs->baud_rate_gen = ZYNQ_UART_BAUD_RATE_GEN_CD(brgr); regs->baud_rate_div = ZYNQ_UART_BAUD_RATE_DIV_BDIV(bauddiv); - regs->control |= ZYNQ_UART_CONTROL_RXRES - | ZYNQ_UART_CONTROL_TXRES; } - regs->control |= ZYNQ_UART_CONTROL_RXEN | ZYNQ_UART_CONTROL_TXEN; - - /* - * Some ZynqMP UARTs have a hardware bug that causes TX/RX logic restarts to - * require a kick after baud rate registers are initialized. - */ - zynq_uart_write_polled(context, 0); + regs->control = ZYNQ_UART_CONTROL_RXRES | ZYNQ_UART_CONTROL_TXRES; + regs->mode = mode; + regs->irq_sts = 0xffffffff; + regs->control = ZYNQ_UART_CONTROL_RXEN | ZYNQ_UART_CONTROL_TXEN; return true; } -- cgit v1.2.3