[PATCH] aarch64/versal: Add UART interrupt support

chrisj at rtems.org chrisj at rtems.org
Tue Nov 22 02:03:17 UTC 2022


From: Chris Johns <chrisj at rtems.org>

---
 .../dev/serial/versal-uart-polled.c           |  37 --
 .../xilinx-versal/dev/serial/versal-uart.c    | 330 ++++++++++++++++++
 .../include/dev/serial/versal-uart-regs.h     |   1 -
 .../include/dev/serial/versal-uart.h          |   5 +-
 spec/build/bsps/aarch64/xilinx-versal/grp.yml |   2 +
 spec/build/bsps/aarch64/xilinx-versal/obj.yml |   1 +
 6 files changed, 337 insertions(+), 39 deletions(-)
 create mode 100644 bsps/aarch64/xilinx-versal/dev/serial/versal-uart.c

diff --git a/bsps/aarch64/xilinx-versal/dev/serial/versal-uart-polled.c b/bsps/aarch64/xilinx-versal/dev/serial/versal-uart-polled.c
index 83493db909..9453dc248b 100644
--- a/bsps/aarch64/xilinx-versal/dev/serial/versal-uart-polled.c
+++ b/bsps/aarch64/xilinx-versal/dev/serial/versal-uart-polled.c
@@ -152,23 +152,6 @@ int versal_uart_initialize(rtems_termios_device_context *base)
   return 0;
 }
 
-static bool versal_uart_first_open(
-  struct rtems_termios_tty *tty,
-  rtems_termios_device_context *base,
-  struct termios *term,
-  rtems_libio_open_close_args_t *args
-)
-{
-  int rc = versal_uart_initialize(base);
-  if ( rc < 0 ) {
-    return false;
-  }
-
-  rtems_termios_set_initial_baud(tty, VERSAL_UART_DEFAULT_BAUD);
-
-  return true;
-}
-
 int versal_uart_read_polled(rtems_termios_device_context *base)
 {
   volatile versal_uart *regs = versal_uart_get_regs(base);
@@ -209,23 +192,3 @@ void versal_uart_reset_tx_flush(rtems_termios_device_context *base)
     /* Wait for empty */
   }
 }
-
-static void versal_uart_write_support(
-  rtems_termios_device_context *base,
-  const char *s,
-  size_t n
-)
-{
-  size_t i;
-
-  for (i = 0; i < n; i++) {
-    versal_uart_write_polled(base, s[i]);
-  }
-}
-
-const rtems_termios_device_handler versal_uart_handler = {
-  .first_open = versal_uart_first_open,
-  .write = versal_uart_write_support,
-  .poll_read = versal_uart_read_polled,
-  .mode = TERMIOS_POLLED
-};
diff --git a/bsps/aarch64/xilinx-versal/dev/serial/versal-uart.c b/bsps/aarch64/xilinx-versal/dev/serial/versal-uart.c
new file mode 100644
index 0000000000..7076452588
--- /dev/null
+++ b/bsps/aarch64/xilinx-versal/dev/serial/versal-uart.c
@@ -0,0 +1,330 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (C) 2022 Chris Johns <chris at contemporary.software>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <dev/serial/versal-uart.h>
+#include <dev/serial/versal-uart-regs.h>
+#include <bsp/irq.h>
+
+#include <bspopts.h>
+
+#ifdef VERSAL_CONSOLE_USE_INTERRUPTS
+static uint32_t versal_uart_intr_all(void)
+{
+  return VERSAL_UARTI_OEI |
+    VERSAL_UARTI_BEI |
+    VERSAL_UARTI_PEI |
+    VERSAL_UARTI_FEI |
+    VERSAL_UARTI_RTI |
+    VERSAL_UARTI_TXI |
+    VERSAL_UARTI_RXI |
+    VERSAL_UARTI_DSRMI |
+    VERSAL_UARTI_DCDMI |
+    VERSAL_UARTI_CTSMI |
+    VERSAL_UARTI_RIMI;
+}
+
+static void versal_uart_intr_clear(volatile versal_uart *regs, uint32_t ints)
+{
+  regs->uarticr = ints;
+}
+
+static void versal_uart_intr_clearall(volatile versal_uart *regs)
+{
+  versal_uart_intr_clear(regs, versal_uart_intr_all());
+}
+
+static void versal_uart_intr_enable(volatile versal_uart *regs, uint32_t ints)
+{
+  regs->uartimsc |= ints;
+}
+
+static void versal_uart_intr_disable(volatile versal_uart *regs, uint32_t ints)
+{
+  regs->uartimsc &= ~ints;
+}
+
+static void versal_uart_intr_disableall(volatile versal_uart *regs)
+{
+  versal_uart_intr_disable(regs, versal_uart_intr_all());
+}
+
+static bool versal_uart_flags_clear(volatile versal_uart *regs, uint32_t flags)
+{
+  return (regs->uartfr & flags) == 0;
+}
+
+static void versal_uart_interrupt(void *arg)
+{
+  rtems_termios_tty *tty = arg;
+  versal_uart_context *ctx = rtems_termios_get_device_context(tty);
+  volatile versal_uart *regs = ctx->regs;
+  uint32_t uartmis = regs->uartmis;
+
+  versal_uart_intr_clear(regs, uartmis);
+
+  if ((uartmis & (VERSAL_UARTI_RTI | VERSAL_UARTI_RXI)) != 0) {
+    char buf[32];
+    int c = 0;
+    while (c < sizeof(buf) &&
+           versal_uart_flags_clear(regs, VERSAL_UARTFR_RXFE)) {
+      buf[c++] = (char) VERSAL_UARTDR_DATA_GET(regs->uartdr);
+    }
+    rtems_termios_enqueue_raw_characters(tty, buf, c);
+  }
+
+  if (ctx->transmitting) {
+    int sent = ctx->tx_queued;
+    ctx->transmitting = false;
+    ctx->tx_queued = 0;
+    versal_uart_intr_disable(regs, VERSAL_UARTI_TXI);
+    rtems_termios_dequeue_characters(tty, sent);
+  }
+}
+#endif
+
+static bool versal_uart_first_open(
+  rtems_termios_tty *tty,
+  rtems_termios_device_context *base,
+  struct termios *term,
+  rtems_libio_open_close_args_t *args
+)
+{
+#ifdef VERSAL_CONSOLE_USE_INTERRUPTS
+  versal_uart_context *ctx = (versal_uart_context *) base;
+  volatile versal_uart *regs = ctx->regs;
+  rtems_status_code sc;
+
+  rtems_interrupt_lock_initialize(ctx->interrupt_lock, "versal-uart");
+  ctx->transmitting = false;
+  ctx->tx_queued = 0;
+  ctx->first_send = true;
+#endif
+
+  rtems_termios_set_initial_baud(tty, VERSAL_UART_DEFAULT_BAUD);
+  versal_uart_initialize(base);
+
+#ifdef VERSAL_CONSOLE_USE_INTERRUPTS
+  regs->uartifls = VERSAL_UARTIFLS_RXIFLSEL(2) | VERSAL_UARTIFLS_TXIFLSEL(2);
+  regs->uartlcr_h |= VERSAL_UARTLCR_H_FEN;
+  versal_uart_intr_disableall(regs);
+  sc = rtems_interrupt_handler_install(
+    ctx->irq,
+    "UART",
+    RTEMS_INTERRUPT_SHARED,
+    versal_uart_interrupt,
+    tty
+  );
+  if (sc != RTEMS_SUCCESSFUL) {
+    return false;
+  }
+  versal_uart_intr_clearall(regs);
+  versal_uart_intr_enable(regs, VERSAL_UARTI_RTI | VERSAL_UARTI_RXI);
+#endif
+
+  return true;
+}
+
+#ifdef VERSAL_CONSOLE_USE_INTERRUPTS
+static void versal_uart_last_close(
+  rtems_termios_tty *tty,
+  rtems_termios_device_context *base,
+  rtems_libio_open_close_args_t *args
+)
+{
+  versal_uart_context *ctx = (versal_uart_context *) base;
+  rtems_interrupt_handler_remove(ctx->irq, versal_uart_interrupt, tty);
+}
+#endif
+
+static void versal_uart_write_support(
+  rtems_termios_device_context *base,
+  const char *buf,
+  size_t len
+)
+{
+#ifdef VERSAL_CONSOLE_USE_INTERRUPTS
+  versal_uart_context *ctx = (versal_uart_context *) base;
+  volatile versal_uart *regs = ctx->regs;
+
+  if (len > 0) {
+    rtems_interrupt_lock_context lock_context;
+    size_t len_remaining = len;
+    const char *p = &buf[0];
+    rtems_interrupt_lock_acquire(&ctx->interrupt_lock, &lock_context);
+    versal_uart_intr_enable(regs, VERSAL_UARTI_TXI);
+    /*
+     * The PL011 IP in the Versal needs preloading the TX FIFO with
+     * exactly 17 characters for the first TX interrupt to be
+     * generated.
+     */
+    if (ctx->first_send) {
+      ctx->first_send = false;
+      for (int i = 0; i < 17; ++i) {
+        regs->uartdr = VERSAL_UARTDR_DATA('\r');
+      }
+    }
+    while (versal_uart_flags_clear(regs, VERSAL_UARTFR_TXFF) &&
+           len_remaining > 0) {
+      regs->uartdr = VERSAL_UARTDR_DATA(*p++);
+      --len_remaining;
+    }
+    ctx->tx_queued = len - len_remaining;
+    ctx->transmitting = true;
+    rtems_interrupt_lock_release(&ctx->interrupt_lock, &lock_context);
+  }
+#else
+  ssize_t i;
+  for (i = 0; i < len; ++i) {
+    versal_uart_write_polled(base, buf[i]);
+  }
+#endif
+}
+
+static bool versal_uart_set_attributes(
+  rtems_termios_device_context *context,
+  const struct termios *term
+)
+{
+  versal_uart_context *ctx = (versal_uart_context *) context;
+  volatile versal_uart *regs = ctx->regs;
+  rtems_interrupt_lock_context lock_context;
+  int32_t baud;
+  uint32_t ibauddiv = 0;
+  uint32_t fbauddiv = 0;
+  uint32_t mode = 0;
+  int rc;
+
+  /*
+   * Determine the baud rate
+   */
+  baud = rtems_termios_baud_to_number(term->c_ospeed);
+
+  if (baud > 0) {
+    uint32_t maxerr = 3;
+
+    rc = versal_cal_baud_rate(
+        VERSAL_UART_DEFAULT_BAUD,
+        maxerr,
+        &ibauddiv,
+        &fbauddiv
+    );
+    if (rc != 0) {
+      return rc;
+    }
+  }
+
+  /*
+   * Configure the mode register
+   */
+  mode = regs->uartlcr_h & VERSAL_UARTLCR_H_FEN;
+
+  /*
+   * Parity
+   */
+  if ((term->c_cflag & PARENB) != 0) {
+    mode |= VERSAL_UARTLCR_H_PEN;
+    if ((term->c_cflag & PARODD) == 0) {
+      mode |= VERSAL_UARTLCR_H_EPS;
+    }
+  }
+
+  /*
+   * Character Size
+   */
+  switch (term->c_cflag & CSIZE)
+  {
+  case CS5:
+    mode = VERSAL_UARTLCR_H_WLEN_SET(mode, VERSAL_UARTLCR_H_WLEN_5);
+    break;
+  case CS6:
+    mode = VERSAL_UARTLCR_H_WLEN_SET(mode, VERSAL_UARTLCR_H_WLEN_6);
+    break;
+  case CS7:
+    mode = VERSAL_UARTLCR_H_WLEN_SET(mode, VERSAL_UARTLCR_H_WLEN_7);
+    break;
+  case CS8:
+  default:
+    mode = VERSAL_UARTLCR_H_WLEN_SET(mode, VERSAL_UARTLCR_H_WLEN_8);
+    break;
+  }
+
+  /*
+   * Stop Bits
+   */
+  if (term->c_cflag & CSTOPB) {
+    /* 2 stop bits */
+    mode |= VERSAL_UARTLCR_H_STP2;
+  }
+
+  rtems_interrupt_lock_acquire(&ctx->interrupt_lock, &lock_context);
+
+  versal_uart_intr_disableall(regs);
+
+  /*
+   * Wait for any data in the TXFIFO to be sent then wait while the
+   * transmiter is active.
+   */
+  while ((regs->uartfr & VERSAL_UARTFR_TXFE) == 0 ||
+         (regs->uartfr & VERSAL_UARTFR_BUSY) != 0) {
+    /* Wait */
+  }
+
+  regs->uartcr = VERSAL_UARTCR_UARTEN;
+  /* Ignore baud rate of B0. There are no modem control lines to de-assert */
+  if (baud > 0) {
+    regs->uartibrd = VERSAL_UARTIBRD_BAUD_DIVINT(ibauddiv);
+    regs->uartfbrd = VERSAL_UARTFBRD_BAUD_DIVFRAC(fbauddiv);
+  }
+  regs->uartlcr_h = mode;
+
+  /* Control: receive, transmit, uart enable, no CTS, no RTS, no loopback */
+  regs->uartcr = VERSAL_UARTCR_RXE
+    | VERSAL_UARTCR_TXE
+    | VERSAL_UARTCR_UARTEN;
+
+#ifdef VERSAL_CONSOLE_USE_INTERRUPTS
+  versal_uart_intr_clearall(regs);
+  versal_uart_intr_enable(regs, VERSAL_UARTI_RTI | VERSAL_UARTI_RXI);
+#endif
+
+  rtems_interrupt_lock_release(&ctx->interrupt_lock, &lock_context);
+
+  return true;
+}
+
+const rtems_termios_device_handler versal_uart_handler = {
+  .first_open = versal_uart_first_open,
+  .set_attributes = versal_uart_set_attributes,
+  .write = versal_uart_write_support,
+#ifdef VERSAL_CONSOLE_USE_INTERRUPTS
+  .last_close = versal_uart_last_close,
+  .mode = TERMIOS_IRQ_DRIVEN
+#else
+  .poll_read = versal_uart_read_polled,
+  .mode = TERMIOS_POLLED
+#endif
+};
diff --git a/bsps/aarch64/xilinx-versal/include/dev/serial/versal-uart-regs.h b/bsps/aarch64/xilinx-versal/include/dev/serial/versal-uart-regs.h
index 59db8f950a..30f918bc60 100644
--- a/bsps/aarch64/xilinx-versal/include/dev/serial/versal-uart-regs.h
+++ b/bsps/aarch64/xilinx-versal/include/dev/serial/versal-uart-regs.h
@@ -110,7 +110,6 @@ typedef struct versal_uart {
 #define VERSAL_UARTCR_RXE BSP_BIT32(9)
 #define VERSAL_UARTCR_TXE BSP_BIT32(8)
 #define VERSAL_UARTCR_LBE BSP_BIT32(7)
-//#define VERSAL_UARTCR_SIREN BSP_BIT32()?
 #define VERSAL_UARTCR_UARTEN BSP_BIT32(0)
   uint32_t uartifls;
 #define VERSAL_UARTIFLS_RXIFLSEL(val) BSP_FLD32(val, 3, 5)
diff --git a/bsps/aarch64/xilinx-versal/include/dev/serial/versal-uart.h b/bsps/aarch64/xilinx-versal/include/dev/serial/versal-uart.h
index 95b5172218..d02d9f1da6 100644
--- a/bsps/aarch64/xilinx-versal/include/dev/serial/versal-uart.h
+++ b/bsps/aarch64/xilinx-versal/include/dev/serial/versal-uart.h
@@ -54,7 +54,10 @@ extern "C" {
 typedef struct {
   rtems_termios_device_context base;
   volatile struct versal_uart *regs;
-  bool transmitting; /* Currently unused */
+  rtems_interrupt_lock interrupt_lock;
+  volatile size_t tx_queued;
+  volatile bool transmitting;
+  bool first_send;
   rtems_vector_number irq;
 } versal_uart_context;
 
diff --git a/spec/build/bsps/aarch64/xilinx-versal/grp.yml b/spec/build/bsps/aarch64/xilinx-versal/grp.yml
index 262278f214..badfa07fcc 100644
--- a/spec/build/bsps/aarch64/xilinx-versal/grp.yml
+++ b/spec/build/bsps/aarch64/xilinx-versal/grp.yml
@@ -24,6 +24,8 @@ links:
   uid: abi
 - role: build-dependency
   uid: obj
+- role: build-dependency
+  uid: optconirq
 - role: build-dependency
   uid: optloadoff
 - role: build-dependency
diff --git a/spec/build/bsps/aarch64/xilinx-versal/obj.yml b/spec/build/bsps/aarch64/xilinx-versal/obj.yml
index 2daebabc95..ac7e6519db 100644
--- a/spec/build/bsps/aarch64/xilinx-versal/obj.yml
+++ b/spec/build/bsps/aarch64/xilinx-versal/obj.yml
@@ -26,6 +26,7 @@ source:
 - bsps/aarch64/shared/mmu/vmsav8-64.c
 - bsps/aarch64/xilinx-versal/console/console.c
 - bsps/aarch64/xilinx-versal/dev/serial/versal-uart-polled.c
+- bsps/aarch64/xilinx-versal/dev/serial/versal-uart.c
 - bsps/aarch64/xilinx-versal/start/bspstart.c
 - bsps/aarch64/xilinx-versal/start/bspstarthooks.c
 - bsps/aarch64/xilinx-versal/start/bspstartmmu.c
-- 
2.19.1



More information about the devel mailing list