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 _______________________________________________ devel mailing list devel@rtems.org http://lists.rtems.org/mailman/listinfo/devel