[PATCH 1/4] bsps/shared: Add new PL011 driver with IRQ support
Utkarsh Verma
utkarsh at bitbanged.com
Tue Aug 29 04:44:48 UTC 2023
This commit adds a new driver for the PL011. It uses the termios API
with support for setting attributes and interrupts. Interrupts can be
enabled using the BSP_CONSOLE_USE_INTERRUPTS macro.
---
bsps/include/dev/serial/arm-pl011-regs.h | 143 ------
.../dev/serial/{arm-pl011.h => pl011.h} | 62 +--
bsps/shared/dev/serial/arm-pl011.c | 104 ----
bsps/shared/dev/serial/pl011.c | 470 ++++++++++++++++++
4 files changed, 503 insertions(+), 276 deletions(-)
delete mode 100644 bsps/include/dev/serial/arm-pl011-regs.h
rename bsps/include/dev/serial/{arm-pl011.h => pl011.h} (51%)
delete mode 100644 bsps/shared/dev/serial/arm-pl011.c
create mode 100644 bsps/shared/dev/serial/pl011.c
diff --git a/bsps/include/dev/serial/arm-pl011-regs.h b/bsps/include/dev/serial/arm-pl011-regs.h
deleted file mode 100644
index d6ea9ae11a..0000000000
--- a/bsps/include/dev/serial/arm-pl011-regs.h
+++ /dev/null
@@ -1,143 +0,0 @@
-/* SPDX-License-Identifier: BSD-2-Clause */
-
-/**
- * @file
- *
- * @ingroup RTEMSBSPsARMShared
- *
- * @brief ARM PL011 Register definitions
- */
-
-/*
- * Copyright (c) 2013 embedded brains GmbH & Co. KG
- *
- * 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.
- */
-
-#ifndef LIBBSP_ARM_SHARED_ARM_PL011_REGS_H
-#define LIBBSP_ARM_SHARED_ARM_PL011_REGS_H
-
-#include <bsp/utility.h>
-
-typedef struct {
- uint32_t uartdr;
-#define PL011_UARTDR_OE BSP_BIT32(11)
-#define PL011_UARTDR_BE BSP_BIT32(10)
-#define PL011_UARTDR_PE BSP_BIT32(9)
-#define PL011_UARTDR_FE BSP_BIT32(8)
-#define PL011_UARTDR_DATA(val) BSP_FLD32(val, 0, 7)
-#define PL011_UARTDR_DATA_GET(reg) BSP_FLD32GET(reg, 0, 7)
-#define PL011_UARTDR_DATA_SET(reg, val) BSP_FLD32SET(reg, val, 0, 7)
- uint32_t uartrsr_uartecr;
-#define PL011_UARTRSR_UARTECR_OE BSP_BIT32(3)
-#define PL011_UARTRSR_UARTECR_BE BSP_BIT32(2)
-#define PL011_UARTRSR_UARTECR_PE BSP_BIT32(1)
-#define PL011_UARTRSR_UARTECR_FE BSP_BIT32(0)
- uint32_t reserved_08[4];
- uint32_t uartfr;
-#define PL011_UARTFR_RI BSP_BIT32(8)
-#define PL011_UARTFR_TXFE BSP_BIT32(7)
-#define PL011_UARTFR_RXFF BSP_BIT32(6)
-#define PL011_UARTFR_TXFF BSP_BIT32(5)
-#define PL011_UARTFR_RXFE BSP_BIT32(4)
-#define PL011_UARTFR_BUSY BSP_BIT32(3)
-#define PL011_UARTFR_DCD BSP_BIT32(2)
-#define PL011_UARTFR_DSR BSP_BIT32(1)
-#define PL011_UARTFR_CTS BSP_BIT32(0)
- uint32_t reserved_1c;
- uint32_t uartilpr;
-#define PL011_UARTILPR_ILPDVSR(val) BSP_FLD32(val, 0, 7)
-#define PL011_UARTILPR_ILPDVSR_GET(reg) BSP_FLD32GET(reg, 0, 7)
-#define PL011_UARTILPR_ILPDVSR_SET(reg, val) BSP_FLD32SET(reg, val, 0, 7)
- uint32_t uartibrd;
-#define PL011_UARTIBRD_BAUD_DIVINT(val) BSP_FLD32(val, 0, 15)
-#define PL011_UARTIBRD_BAUD_DIVINT_GET(reg) BSP_FLD32GET(reg, 0, 15)
-#define PL011_UARTIBRD_BAUD_DIVINT_SET(reg, val) BSP_FLD32SET(reg, val, 0, 15)
- uint32_t uartfbrd;
-#define PL011_UARTFBRD_BAUD_DIVFRAC(val) BSP_FLD32(val, 0, 5)
-#define PL011_UARTFBRD_BAUD_DIVFRAC_GET(reg) BSP_FLD32GET(reg, 0, 5)
-#define PL011_UARTFBRD_BAUD_DIVFRAC_SET(reg, val) BSP_FLD32SET(reg, val, 0, 5)
- uint32_t uartlcr_h;
-#define PL011_UARTLCR_H_SPS BSP_BIT32(7)
-#define PL011_UARTLCR_H_WLEN(val) BSP_FLD32(val, 5, 6)
-#define PL011_UARTLCR_H_WLEN_GET(reg) BSP_FLD32GET(reg, 5, 6)
-#define PL011_UARTLCR_H_WLEN_SET(reg, val) BSP_FLD32SET(reg, val, 5, 6)
-#define PL011_UARTLCR_H_WLEN_5 0x00U
-#define PL011_UARTLCR_H_WLEN_6 0x01U
-#define PL011_UARTLCR_H_WLEN_7 0x02U
-#define PL011_UARTLCR_H_WLEN_8 0x03U
-#define PL011_UARTLCR_H_FEN BSP_BIT32(4)
-#define PL011_UARTLCR_H_STP2 BSP_BIT32(3)
-#define PL011_UARTLCR_H_EPS BSP_BIT32(2)
-#define PL011_UARTLCR_H_PEN BSP_BIT32(1)
-#define PL011_UARTLCR_H_BRK BSP_BIT32(0)
- uint32_t uartcr;
-#define PL011_UARTCR_CTSEN BSP_BIT32(15)
-#define PL011_UARTCR_RTSEN BSP_BIT32(14)
-#define PL011_UARTCR_OUT2 BSP_BIT32(13)
-#define PL011_UARTCR_OUT1 BSP_BIT32(12)
-#define PL011_UARTCR_RTS BSP_BIT32(11)
-#define PL011_UARTCR_DTR BSP_BIT32(10)
-#define PL011_UARTCR_RXE BSP_BIT32(9)
-#define PL011_UARTCR_TXE BSP_BIT32(8)
-#define PL011_UARTCR_LBE BSP_BIT32(7)
-#define PL011_UARTCR_SIRLP BSP_BIT32(3)
-#define PL011_UARTCR_SIREN BSP_BIT32(2)
-#define PL011_UARTCR_UARTEN BSP_BIT32(1)
- uint32_t uartifls;
-#define PL011_UARTIFLS_RXIFLSEL(val) BSP_FLD32(val, 3, 5)
-#define PL011_UARTIFLS_RXIFLSEL_GET(reg) BSP_FLD32GET(reg, 3, 5)
-#define PL011_UARTIFLS_RXIFLSEL_SET(reg, val) BSP_FLD32SET(reg, val, 3, 5)
-#define PL011_UARTIFLS_TXIFLSEL(val) BSP_FLD32(val, 0, 2)
-#define PL011_UARTIFLS_TXIFLSEL_GET(reg) BSP_FLD32GET(reg, 0, 2)
-#define PL011_UARTIFLS_TXIFLSEL_SET(reg, val) BSP_FLD32SET(reg, val, 0, 2)
- uint32_t uartimsc;
- uint32_t uartris;
- uint32_t uartmis;
- uint32_t uarticr;
-#define PL011_UARTI_OEI BSP_BIT32(10)
-#define PL011_UARTI_BEI BSP_BIT32(9)
-#define PL011_UARTI_PEI BSP_BIT32(8)
-#define PL011_UARTI_FEI BSP_BIT32(7)
-#define PL011_UARTI_RTI BSP_BIT32(6)
-#define PL011_UARTI_TXI BSP_BIT32(5)
-#define PL011_UARTI_RXI BSP_BIT32(4)
-#define PL011_UARTI_DSRMI BSP_BIT32(3)
-#define PL011_UARTI_DCDMI BSP_BIT32(2)
-#define PL011_UARTI_CTSMI BSP_BIT32(1)
-#define PL011_UARTI_RIMI BSP_BIT32(0)
- uint32_t uartdmacr;
-#define PL011_UARTDMACR_DMAONERR BSP_BIT32(2)
-#define PL011_UARTDMACR_TXDMAE BSP_BIT32(1)
-#define PL011_UARTDMACR_RXDMAE BSP_BIT32(0)
- uint32_t reserved_4c[997];
- uint32_t uartperiphid0;
- uint32_t uartperiphid1;
- uint32_t uartperiphid2;
- uint32_t uartperiphid3;
- uint32_t uartpcellid0;
- uint32_t uartpcellid1;
- uint32_t uartpcellid2;
- uint32_t uartpcellid3;
-} pl011;
-
-#endif /* LIBBSP_ARM_SHARED_ARM_PL011_REGS_H */
diff --git a/bsps/include/dev/serial/arm-pl011.h b/bsps/include/dev/serial/pl011.h
similarity index 51%
rename from bsps/include/dev/serial/arm-pl011.h
rename to bsps/include/dev/serial/pl011.h
index a22fa1ac06..128bdce5f5 100644
--- a/bsps/include/dev/serial/arm-pl011.h
+++ b/bsps/include/dev/serial/pl011.h
@@ -1,15 +1,8 @@
/* SPDX-License-Identifier: BSD-2-Clause */
-/**
- * @file
- *
- * @ingroup RTEMSBSPsARMShared
- *
- * @brief ARM PL011 Support Package
- */
-
/*
- * Copyright (C) 2013, 2014 embedded brains GmbH & Co. KG
+ * Copyright (C) 2023 Utkarsh Verma
+ *
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -33,32 +26,43 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef LIBBSP_ARM_SHARED_ARM_PL011_H
-#define LIBBSP_ARM_SHARED_ARM_PL011_H
+#ifndef LIBBSP_SHARED_DEV_SERIAL_PL011_H
+#define LIBBSP_SHARED_DEV_SERIAL_PL011_H
+#include <bspopts.h>
+#include <rtems/rtems/intr.h>
+#include <rtems/termiosdevice.h>
#include <rtems/termiostypes.h>
-
-#include <dev/serial/arm-pl011-regs.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
+#include <stddef.h>
+#include <stdint.h>
typedef struct {
- rtems_termios_device_context base;
- volatile pl011 *regs;
- rtems_vector_number irq;
- uint32_t initial_baud;
-} arm_pl011_context;
+ rtems_termios_device_context context;
+ const uintptr_t regs_base;
+ const uint32_t clock;
+ const uint32_t initial_baud;
+ const rtems_vector_number irq;
-bool arm_pl011_probe(rtems_termios_device_context *base);
+#ifdef BSP_CONSOLE_USE_INTERRUPTS
+ /*
+ * Due to HW limitation, the first TX interrupt should be triggered by the
+ * software. This is because TX interrupts are based on transition through
+ * a level, rather than on the level itself. When the UART interrupt and
+ * UART is enabled before any data is written to the TXFIFO, the interrupt
+ * is not set. The interrupt is only set once the TXFIFO becomes empty
+ * after being filled to the trigger level. Until then, this flag variable
+ * ensures that the interrupt handler is software triggered.
+ */
+ volatile bool needs_sw_triggered_tx_irq;
-void arm_pl011_write_polled(rtems_termios_device_context *base, char c);
+ volatile int tx_queued_chars;
+ rtems_termios_tty* tty;
+#endif
+} pl011_context;
-extern const rtems_termios_device_handler arm_pl011_fns;
+extern const rtems_termios_device_handler pl011_handler;
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
+void pl011_write_char_polled(const rtems_termios_device_context* base,
+ const char ch);
-#endif /* LIBBSP_ARM_SHARED_ARM_PL011_H */
+#endif /* LIBBSP_SHARED_DEV_SERIAL_PL011_H */
diff --git a/bsps/shared/dev/serial/arm-pl011.c b/bsps/shared/dev/serial/arm-pl011.c
deleted file mode 100644
index e9a8e3f5a4..0000000000
--- a/bsps/shared/dev/serial/arm-pl011.c
+++ /dev/null
@@ -1,104 +0,0 @@
-/* SPDX-License-Identifier: BSD-2-Clause */
-
-/*
- * Copyright (C) 2013, 2014 embedded brains GmbH & Co. KG
- *
- * 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/arm-pl011.h>
-
-static volatile pl011 *pl011_get_regs(rtems_termios_device_context *base)
-{
- arm_pl011_context *ctx = (arm_pl011_context *) base;
-
- return ctx->regs;
-}
-
-
-bool arm_pl011_probe(rtems_termios_device_context *base)
-{
- volatile pl011 *regs = pl011_get_regs(base);
-
- regs->uartlcr_h = PL011_UARTLCR_H_WLEN(PL011_UARTLCR_H_WLEN_8);
- regs->uartcr = PL011_UARTCR_RXE
- | PL011_UARTCR_TXE
- | PL011_UARTCR_UARTEN;
-
- return true;
-}
-
-static bool pl011_first_open(
- struct rtems_termios_tty *tty,
- rtems_termios_device_context *base,
- struct termios *term,
- rtems_libio_open_close_args_t *args
-)
-{
- arm_pl011_context *ctx = (arm_pl011_context *) base;
-
- rtems_termios_set_initial_baud(tty, ctx->initial_baud);
-
- return true;
-}
-
-static int pl011_read_polled(rtems_termios_device_context *base)
-{
- volatile pl011 *regs = pl011_get_regs(base);
-
- if ((regs->uartfr & PL011_UARTFR_RXFE) != 0) {
- return -1;
- } else {
- return PL011_UARTDR_DATA_GET(regs->uartdr);
- }
-}
-
-void arm_pl011_write_polled(rtems_termios_device_context *base, char c)
-{
- volatile pl011 *regs = pl011_get_regs(base);
-
- while ((regs->uartfr & PL011_UARTFR_TXFF) != 0) {
- /* Wait */
- }
-
- regs->uartdr = PL011_UARTDR_DATA(c);
-}
-
-static void pl011_write_support_polled(
- rtems_termios_device_context *base,
- const char *s,
- size_t n
-)
-{
- size_t i;
-
- for (i = 0; i < n; ++i) {
- arm_pl011_write_polled(base, s[i]);
- }
-}
-
-const rtems_termios_device_handler arm_pl011_fns = {
- .first_open = pl011_first_open,
- .poll_read = pl011_read_polled,
- .write = pl011_write_support_polled,
- .mode = TERMIOS_POLLED
-};
diff --git a/bsps/shared/dev/serial/pl011.c b/bsps/shared/dev/serial/pl011.c
new file mode 100644
index 0000000000..5bee3aac9c
--- /dev/null
+++ b/bsps/shared/dev/serial/pl011.c
@@ -0,0 +1,470 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+
+/*
+ * Copyright (C) 2023 Utkarsh Verma
+ *
+ *
+ * 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/pl011.h"
+
+#include <bsp/utility.h>
+#include <bspopts.h>
+#include <rtems/libio.h>
+#include <rtems/termiosdevice.h>
+#include <rtems/termiostypes.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/_termios.h>
+
+#define REG(addr) *(volatile uint32_t *)(addr)
+
+#define DR(base) REG(base + 0x00)
+#define DR_DATA_MASK BSP_MSK32(0, 7)
+#define FR(base) REG(base + 0x18)
+#define FR_BUSY BSP_BIT32(3)
+#define FR_RXFE BSP_BIT32(4)
+#define FR_TXFF BSP_BIT32(5)
+#define FR_TXFE BSP_BIT32(7)
+#define IBRD(base) REG(base + 0x24)
+#define IBRD_BAUD_DIVINT_WIDTH 16
+#define IBRD_BAUD_DIVINT_MASK BSP_MSK32(0, IBRD_BAUD_DIVINT_WIDTH - 1)
+#define FBRD(base) REG(base + 0x28)
+#define FBRD_BAUD_DIVFRAC_WIDTH 6
+#define FBRD_BAUD_DIVFRAC_MASK BSP_MSK32(0, FBRD_BAUD_DIVFRAC_WIDTH - 1)
+#define LCRH(base) REG(base + 0x2c)
+#define LCRH_PEN BSP_BIT32(1)
+#define LCRH_EPS BSP_BIT32(2)
+#define LCRH_STP2 BSP_BIT32(3)
+#define LCRH_FEN BSP_BIT32(4)
+#define LCRH_WLEN_MASK BSP_MSK32(5, 6)
+#define LCRH_WLEN_5BITS BSP_FLD32(0, 5, 6)
+#define LCRH_WLEN_6BITS BSP_FLD32(1, 5, 6)
+#define LCRH_WLEN_7BITS BSP_FLD32(2, 5, 6)
+#define LCRH_WLEN_8BITS BSP_FLD32(3, 5, 6)
+#define CR(base) REG(base + 0x30)
+#define CR_UARTEN BSP_BIT32(0)
+#define CR_TXE BSP_BIT32(8)
+#define CR_RXE BSP_BIT32(9)
+#define CR_RTSEN BSP_BIT32(14)
+#define CR_CTSEN BSP_BIT32(15)
+#define IFLS(base) REG(base + 0x34)
+#define IFLS_TXIFLSEL_MASK BSP_MSK32(0, 2)
+#define IFLS_TXIFLSEL(level) BSP_FLD32(level, 0, 2)
+#define IFLS_RXIFLSEL_MASK BSP_MSK32(3, 5)
+#define IFLS_RXIFLSEL(level) BSP_FLD32(level, 3, 5)
+#define IMSC(base) REG(base + 0x38)
+#define MIS(base) REG(base + 0x40)
+#define ICR(base) REG(base + 0x44)
+
+/* Applies to IMSC, ICR, and MIS */
+#define IRQ_RX_BIT BSP_BIT32(4)
+#define IRQ_RT_BIT BSP_BIT32(6)
+#define IRQ_TX_BIT BSP_BIT32(5)
+#define IRQ_FE_BIT BSP_BIT32(7)
+#define IRQ_PE_BIT BSP_BIT32(8)
+#define IRQ_BE_BIT BSP_BIT32(9)
+#define IRQ_OE_BIT BSP_BIT32(10)
+#define IRQ_MASK BSP_MSK32(0, 10)
+
+#ifdef BSP_CONSOLE_USE_INTERRUPTS
+#define FIFO_SIZE 32
+#define TXFIFO_IRQ_TRIGGER_LEVEL FIFO_LEVEL_ONE_EIGHTH
+#define RXFIFO_IRQ_TRIGGER_LEVEL FIFO_LEVEL_ONE_HALF
+
+enum fifo_trigger_level {
+ FIFO_LEVEL_ONE_EIGHTH,
+ FIFO_LEVEL_ONE_FOURTH,
+ FIFO_LEVEL_ONE_HALF,
+ FIFO_LEVEL_THREE_FOURTH,
+ FIFO_LELEL_SEVEN_HALF,
+};
+#endif
+
+static bool first_open(struct rtems_termios_tty *tty,
+ rtems_termios_device_context *base,
+ struct termios *term,
+ rtems_libio_open_close_args_t *args);
+
+#ifdef BSP_CONSOLE_USE_INTERRUPTS
+static void last_close(rtems_termios_tty *tty,
+ rtems_termios_device_context *base,
+ rtems_libio_open_close_args_t *args);
+#else
+static int read_char_polled(rtems_termios_device_context *base);
+#endif
+
+static void write_buffer(rtems_termios_device_context *base,
+ const char *buffer, size_t n);
+
+static bool set_attributes(rtems_termios_device_context *base,
+ const struct termios *term);
+
+const rtems_termios_device_handler pl011_handler = {
+ .first_open = first_open,
+ .write = write_buffer,
+ .set_attributes = set_attributes,
+ .ioctl = NULL,
+#ifdef BSP_CONSOLE_USE_INTERRUPTS
+ .last_close = last_close,
+ .poll_read = NULL,
+ .mode = TERMIOS_IRQ_DRIVEN,
+#else
+ .last_close = NULL,
+ .poll_read = read_char_polled,
+ .mode = TERMIOS_POLLED,
+#endif
+};
+
+static inline char read_char(const uintptr_t regs_base) {
+ return DR(regs_base) & DR_DATA_MASK;
+}
+
+static inline void write_char(const uintptr_t regs_base, const char ch) {
+ DR(regs_base) = ch;
+}
+
+static inline bool is_rxfifo_empty(const uintptr_t regs_base) {
+ return (FR(regs_base) & FR_RXFE) != 0;
+}
+
+static inline bool is_txfifo_full(const uintptr_t regs_base) {
+ return (FR(regs_base) & FR_TXFF) != 0;
+}
+
+static void flush_fifos(const pl011_context *context) {
+ const uintptr_t regs_base = context->regs_base;
+
+ /* Wait for pending transactions */
+ while ((FR(regs_base) & FR_BUSY) != 0)
+ ;
+
+ LCRH(regs_base) &= ~LCRH_FEN;
+ LCRH(regs_base) |= LCRH_FEN;
+}
+
+#ifdef BSP_CONSOLE_USE_INTERRUPTS
+static inline void clear_irq(const uintptr_t regs_base,
+ const uint32_t irq_mask) {
+ /* ICR is a write-only register */
+ ICR(regs_base) = irq_mask;
+}
+
+static inline void enable_irq(const uintptr_t regs_base,
+ const uint32_t irq_mask) {
+ IMSC(regs_base) |= irq_mask;
+}
+#endif
+
+static inline void disable_irq(uintptr_t regs_base, uint32_t irq_mask) {
+ IMSC(regs_base) &= ~irq_mask;
+}
+
+#ifdef BSP_CONSOLE_USE_INTERRUPTS
+static void irq_handler(void *arg) {
+ pl011_context *context = (void *)arg;
+ const uintptr_t regs_base = context->regs_base;
+ const uint32_t irqs = MIS(regs_base);
+
+ /* RXFIFO got data */
+ const uint32_t rx_irq_mask = IRQ_RT_BIT | IRQ_RX_BIT;
+ if ((irqs & rx_irq_mask) != 0) {
+ char buffer[FIFO_SIZE];
+
+ unsigned int i = 0;
+ while (i < sizeof(buffer) && !is_rxfifo_empty(regs_base)) {
+ buffer[i] = read_char(regs_base);
+ i++;
+ }
+
+ (void)rtems_termios_enqueue_raw_characters(context->tty, buffer, i);
+
+ /* Clear all interrupts */
+ clear_irq(regs_base, rx_irq_mask);
+ }
+
+ /*
+ * Some characters got queued in the TXFIFO, so dequeue them from Termios'
+ * structures.
+ */
+ if (context->tx_queued_chars != -1) {
+ /*
+ * First interrupt was raised, so no need to trigger the handler
+ * through software anymore.
+ */
+ if (context->needs_sw_triggered_tx_irq && (irqs & IRQ_TX_BIT) != 0)
+ context->needs_sw_triggered_tx_irq = false;
+
+ (void)rtems_termios_dequeue_characters(context->tty,
+ context->tx_queued_chars);
+
+ /* No need to clear the interrupt. It will automatically get cleared
+ * when TXFIFO is filled above the trigger level. */
+ }
+}
+#endif
+
+static bool first_open(struct rtems_termios_tty *tty,
+ rtems_termios_device_context *base,
+ struct termios *term,
+ rtems_libio_open_close_args_t *args) {
+#ifndef BSP_CONSOLE_USE_INTERRUPTS
+ const
+#endif
+ pl011_context *context = (void *)base;
+
+#ifdef BSP_CONSOLE_USE_INTERRUPTS
+ context->tty = tty;
+#endif
+
+ if (rtems_termios_set_initial_baud(tty, context->initial_baud) != 0)
+ return false;
+
+ if (!set_attributes(base, term))
+ return false;
+
+#ifdef BSP_CONSOLE_USE_INTERRUPTS
+ const uintptr_t regs_base = context->regs_base;
+
+ /* Set FIFO trigger levels for interrupts */
+ uint32_t ifls = IFLS(regs_base);
+ ifls &= ~(IFLS_RXIFLSEL_MASK | IFLS_TXIFLSEL_MASK);
+ ifls |= IFLS_TXIFLSEL(TXFIFO_IRQ_TRIGGER_LEVEL) |
+ IFLS_RXIFLSEL(RXFIFO_IRQ_TRIGGER_LEVEL);
+ IFLS(regs_base) = ifls;
+
+ const rtems_status_code sc = rtems_interrupt_handler_install(
+ context->irq, "UART", RTEMS_INTERRUPT_SHARED, irq_handler, context);
+ if (sc != RTEMS_SUCCESSFUL)
+ return false;
+#endif
+
+ return true;
+}
+
+#ifdef BSP_CONSOLE_USE_INTERRUPTS
+static void last_close(rtems_termios_tty *tty,
+ rtems_termios_device_context *base,
+ rtems_libio_open_close_args_t *args) {
+ const pl011_context *context = (void *)base;
+ (void)rtems_interrupt_handler_remove(context->irq, irq_handler, tty);
+}
+#else
+static int read_char_polled(rtems_termios_device_context *base) {
+ const pl011_context *context = (void *)base;
+ const uintptr_t regs_base = context->regs_base;
+
+ if (is_rxfifo_empty(regs_base))
+ return -1;
+
+ return read_char(regs_base);
+}
+#endif
+
+static void write_buffer(rtems_termios_device_context *base,
+ const char *buffer, size_t n) {
+#ifdef BSP_CONSOLE_USE_INTERRUPTS
+ pl011_context *context = (void *)base;
+ const uintptr_t regs_base = context->regs_base;
+
+ if (n > 0) {
+ size_t i = 0;
+ while (!is_txfifo_full(regs_base) && i < n) {
+ write_char(regs_base, buffer[i]);
+ i++;
+ }
+
+ context->tx_queued_chars = i;
+ enable_irq(regs_base, IRQ_TX_BIT);
+
+ if (context->needs_sw_triggered_tx_irq)
+ irq_handler(context);
+
+ return;
+ }
+
+ /*
+ * Termios will set n to zero to indicate that the transmitter is now
+ * inactive. The output buffer is empty in this case. The driver may
+ * disable the transmit interrupts now.
+ */
+ disable_irq(regs_base, IRQ_TX_BIT);
+#else
+ for (size_t i = 0; i < n; i++)
+ pl011_write_char_polled(base, buffer[i]);
+#endif
+}
+
+static int compute_baudrate_params(uint32_t *ibrd, uint32_t *fbrd,
+ const uint32_t baudrate,
+ const uint32_t clock,
+ const unsigned short max_error) {
+ /*
+ * The integer baudrate divisor, i.e. (clock / (baudrate * 16)), value
+ * should lie on [1, 2^16 - 1]. To ensure this, clock and baudrate have to
+ * be validated.
+ */
+ *ibrd = clock / 16 / baudrate;
+ if (*ibrd < 1 || *ibrd > IBRD_BAUD_DIVINT_MASK)
+ return 2;
+
+ /* Find the fractional part */
+ const uint16_t scalar = 1 << (FBRD_BAUD_DIVFRAC_WIDTH + 1);
+ const uint64_t scaled_bauddiv = ((uint64_t)clock * scalar) / 16 / baudrate;
+ const unsigned short round_off = scaled_bauddiv & 0x1;
+ *fbrd = ((scaled_bauddiv >> 1) & FBRD_BAUD_DIVFRAC_MASK) + round_off;
+
+ /* Calculate the baudrate and check if the error is too large */
+ const uint32_t computed_bauddiv =
+ (*ibrd << FBRD_BAUD_DIVFRAC_WIDTH) | *fbrd;
+ const uint32_t computed_baudrate =
+ ((uint64_t)clock << FBRD_BAUD_DIVFRAC_WIDTH) / 16 / computed_bauddiv;
+
+ uint32_t baud_error = computed_baudrate - baudrate;
+ if (baudrate > computed_baudrate)
+ baud_error = baudrate - computed_baudrate;
+
+ const unsigned short percent_error = (baud_error * 100) / baudrate;
+ if (percent_error >= max_error)
+ return 1;
+
+ return 0;
+}
+
+static bool set_attributes(rtems_termios_device_context *base,
+ const struct termios *term) {
+ pl011_context *context = (void *)base;
+ const uintptr_t regs_base = context->regs_base;
+
+ /* Determine baudrate parameters */
+ const uint32_t baud = rtems_termios_number_to_baud(term->c_ospeed);
+ if (baud == B0)
+ return false;
+
+ uint32_t ibrd, fbrd;
+ if (compute_baudrate_params(&ibrd, &fbrd, baud, context->clock, 3))
+ return false;
+
+ /* Start mode configuration from a clean slate */
+ uint32_t lcrh = LCRH(regs_base) & LCRH_FEN;
+
+ /* Mode: parity */
+ if ((term->c_cflag & PARENB) != 0) {
+ lcrh |= LCRH_PEN;
+
+ if ((term->c_cflag & PARODD) == 0)
+ lcrh |= LCRH_EPS;
+ }
+
+ /* Mode: character size */
+ switch (term->c_cflag & CSIZE) {
+ case CS5:
+ lcrh |= LCRH_WLEN_5BITS;
+ break;
+ case CS6:
+ lcrh |= LCRH_WLEN_6BITS;
+ break;
+ case CS7:
+ lcrh |= LCRH_WLEN_7BITS;
+ break;
+ case CS8:
+ default:
+ lcrh |= LCRH_WLEN_8BITS;
+ }
+
+ /* Mode: stop bits */
+ if ((term->c_cflag & CSTOPB) != 0)
+ lcrh |= LCRH_STP2;
+
+ /* Control: Disable UART */
+ CR(regs_base) &= ~(CR_UARTEN | CR_RXE | CR_TXE);
+ uint32_t cr = CR(regs_base);
+
+ /*
+ * Control: Configure flow control
+ * NOTE: Flow control is untested
+ */
+ cr &= ~(CR_CTSEN | CR_RTSEN);
+ if ((term->c_cflag & CCTS_OFLOW) != 0)
+ cr |= CR_CTSEN;
+ if ((term->c_cflag & CRTS_IFLOW) != 0)
+ cr |= CR_RTSEN;
+
+ /* Control: Configure receiver */
+ const bool rx_enabled = (term->c_cflag & CREAD) != 0;
+ if (rx_enabled)
+ cr |= CR_RXE;
+
+ /* Control: Re-enable UART */
+ cr |= CR_UARTEN | CR_TXE;
+
+ /* Disable all interrupts */
+ disable_irq(regs_base, IRQ_MASK);
+
+ flush_fifos(context);
+
+ /* Set the baudrate */
+ IBRD(regs_base) = ibrd;
+ FBRD(regs_base) = fbrd;
+
+ /*
+ * Commit mode configurations
+ * NOTE:
+ * This has to happen after IBRD and FBRD as writing to LCRH is
+ * required to trigger the baudrate update.
+ */
+ LCRH(regs_base) = lcrh;
+
+ /* Commit changes to control register */
+ CR(regs_base) = cr;
+
+#ifdef BSP_CONSOLE_USE_INTERRUPTS
+ /* Clear all interrupts */
+ clear_irq(regs_base, IRQ_MASK);
+
+ /* Re-enable RX interrupts */
+ if (rx_enabled)
+ enable_irq(regs_base, IRQ_RT_BIT | IRQ_RX_BIT);
+
+ /*
+ * UART is freshly enabled, TXFIFO is empty, and interrupt will be enabled,
+ * so the next transmission will required software-trigger interrupt.
+ */
+ context->needs_sw_triggered_tx_irq = true;
+#endif
+
+ return true;
+}
+
+void pl011_write_char_polled(const rtems_termios_device_context *base,
+ const char ch) {
+ const pl011_context *context = (void *)base;
+ const uintptr_t regs_base = context->regs_base;
+
+ /* Wait until TXFIFO has space */
+ while (is_txfifo_full(regs_base))
+ ;
+
+ write_char(regs_base, ch);
+}
--
2.41.0
More information about the devel
mailing list