From 58f0ee63645d936e44d0c9958f9717b15c86aa48 Mon Sep 17 00:00:00 2001 From: Jukka Laitinen Date: Tue, 16 Apr 2024 15:44:30 +0300 Subject: [PATCH] arch/arm64/src/imx9: Add a more capable uart driver Add an uart driver supporting LPUART1-8, dma, flow control, tc etc. Signed-off-by: Jukka Laitinen --- arch/arm64/src/imx9/Kconfig | 85 +- arch/arm64/src/imx9/Make.defs | 5 +- arch/arm64/src/imx9/hardware/imx9_lpuart.h | 403 ++- arch/arm64/src/imx9/imx9_boot.c | 7 + arch/arm64/src/imx9/imx9_lowputc.c | 532 +++ .../imx9/{imx93_lowputc.S => imx9_lowputc.h} | 97 +- arch/arm64/src/imx9/imx9_lpuart.c | 3121 +++++++++++++---- arch/arm64/src/imx9/imx9_serial.h | 228 +- .../imx9/imx93-evk/configs/nsh/defconfig | 4 +- boards/arm64/imx9/imx93-evk/include/board.h | 7 + 10 files changed, 3641 insertions(+), 848 deletions(-) create mode 100644 arch/arm64/src/imx9/imx9_lowputc.c rename arch/arm64/src/imx9/{imx93_lowputc.S => imx9_lowputc.h} (50%) diff --git a/arch/arm64/src/imx9/Kconfig b/arch/arm64/src/imx9/Kconfig index 9368c961c160f..7673dc6356259 100644 --- a/arch/arm64/src/imx9/Kconfig +++ b/arch/arm64/src/imx9/Kconfig @@ -28,10 +28,89 @@ config IMX9_FLEXIO_PWM default n menu "i.MX9 Peripheral Selection" -config IMX9_UART1 - bool "UART1" + +menu "LPUART" + +config IMX9_LPUART + bool + default n + select ARCH_HAVE_SERIAL_TERMIOS + +config IMX9_LPUART1 + bool "LPUART1" + default n + select IMX9_LPUART + select LPUART1_SERIALDRIVER + +config IMX9_LPUART2 + bool "LPUART2" + default n + select IMX9_LPUART + select LPUART2_SERIALDRIVER + +config IMX9_LPUART3 + bool "LPUART3" + default n + select IMX9_LPUART + select LPUART3_SERIALDRIVER + +config IMX9_LPUART4 + bool "LPUART4" + default n + select IMX9_LPUART + select LPUART4_SERIALDRIVER + +config IMX9_LPUART5 + bool "LPUART5" + default n + select IMX9_LPUART + select LPUART5_SERIALDRIVER + +config IMX9_LPUART6 + bool "LPUART6" default n - select UART1_SERIALDRIVER + select IMX9_LPUART + select LPUART6_SERIALDRIVER + +config IMX9_LPUART7 + bool "LPUART7" + default n + select IMX9_LPUART + select LPUART7_SERIALDRIVER + +config IMX9_LPUART8 + bool "LPUART8" + default n + select IMX9_LPUART + select LPUART8_SERIALDRIVER + +menu "LPUART Configuration" + depends on IMX9_LPUART + +config IMX9_LPUART_INVERT + bool "Signal Invert Support" + default n + +config IMX9_LPUART_SINGLEWIRE + bool "Signal Wire Support" + default n + +config IMX9_SERIAL_RXDMA_BUFFER_SIZE + int "RX DMA buffer size" + default 64 + depends on LPUART1_RXDMA || LPUART2_RXDMA || LPUART3_RXDMA || LPUART4_RXDMA || \ + LPUART5_RXDMA || LPUART6_RXDMA || LPUART7_RXDMA || LPUART8_RXDMA + ---help--- + The DMA buffer size when using RX DMA to emulate a FIFO. + + When streaming data, the generic serial layer will be called + every time the FIFO receives half this number of bytes. + + Value given here will be rounded up to next multiple of 64 bytes. + +endmenu # LPUART Configuration + +endmenu # LPUART config IMX9_FLEXIO1_PWM depends on PWM diff --git a/arch/arm64/src/imx9/Make.defs b/arch/arm64/src/imx9/Make.defs index 27a19cb357b97..5e11a38b59c76 100644 --- a/arch/arm64/src/imx9/Make.defs +++ b/arch/arm64/src/imx9/Make.defs @@ -25,10 +25,7 @@ include common/Make.defs CHIP_CSRCS = imx9_boot.c imx9_ccm.c imx9_clockconfig.c imx9_gpio.c imx9_iomuxc.c ifeq ($(CONFIG_ARCH_CHIP_IMX93),y) - CHIP_CSRCS += imx9_lpuart.c - ifeq ($(CONFIG_ARCH_EARLY_PRINT),y) - CHIP_ASRCS = imx93_lowputc.S - endif + CHIP_CSRCS += imx9_lpuart.c imx9_lowputc.c endif ifeq ($(CONFIG_IMX9_GPIO_IRQ),y) diff --git a/arch/arm64/src/imx9/hardware/imx9_lpuart.h b/arch/arm64/src/imx9/hardware/imx9_lpuart.h index 3925c51bc606f..c688d5966a14e 100644 --- a/arch/arm64/src/imx9/hardware/imx9_lpuart.h +++ b/arch/arm64/src/imx9/hardware/imx9_lpuart.h @@ -25,134 +25,289 @@ * Included Files ****************************************************************************/ -#include +#include /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ -/* 32-bit register definition */ - -#define UARTVERID 0x0000 /* Version ID Register */ -#define UARTPARAM 0x0004 /* Parameter Register */ -#define UARTGLOBAL 0x0008 /* LPUART Global Register */ -#define UARTPINCFG 0x000c /* LPUART Pin Configuration Register */ -#define UARTBAUD 0x0010 /* LPUART Baud Rate Register */ -#define UARTSTAT 0x0014 /* LPUART Status Register */ -#define UARTCTRL 0x0018 /* LPUART Control Register */ -#define UARTDATA 0x001c /* LPUART Data Register */ -#define UARTMATCH 0x0020 /* LPUART Match Address Register */ -#define UARTMODIR 0x0024 /* LPUART Modem IrDA Register */ -#define UARTFIFO 0x0028 /* LPUART FIFO Register */ -#define UARTWATER 0x002c /* LPUART Watermark Register */ - -#define UARTBAUD_MAEN1 0x80000000 -#define UARTBAUD_MAEN2 0x40000000 -#define UARTBAUD_M10 0x20000000 -#define UARTBAUD_TDMAE 0x00800000 -#define UARTBAUD_RDMAE 0x00200000 -#define UARTBAUD_RIDMAE 0x00100000 -#define UARTBAUD_MATCFG 0x00400000 -#define UARTBAUD_BOTHEDGE 0x00020000 -#define UARTBAUD_RESYNCDIS 0x00010000 -#define UARTBAUD_LBKDIE 0x00008000 -#define UARTBAUD_RXEDGIE 0x00004000 -#define UARTBAUD_SBNS 0x00002000 -#define UARTBAUD_SBR 0x00000000 -#define UARTBAUD_SBR_MASK 0x1fff -#define UARTBAUD_OSR_MASK 0x1f -#define UARTBAUD_OSR_SHIFT 24 - -#define UARTSTAT_LBKDIF 0x80000000 -#define UARTSTAT_RXEDGIF 0x40000000 -#define UARTSTAT_MSBF 0x20000000 -#define UARTSTAT_RXINV 0x10000000 -#define UARTSTAT_RWUID 0x08000000 -#define UARTSTAT_BRK13 0x04000000 -#define UARTSTAT_LBKDE 0x02000000 -#define UARTSTAT_RAF 0x01000000 -#define UARTSTAT_TDRE 0x00800000 -#define UARTSTAT_TC 0x00400000 -#define UARTSTAT_RDRF 0x00200000 -#define UARTSTAT_IDLE 0x00100000 -#define UARTSTAT_OR 0x00080000 -#define UARTSTAT_NF 0x00040000 -#define UARTSTAT_FE 0x00020000 -#define UARTSTAT_PE 0x00010000 -#define UARTSTAT_MA1F 0x00008000 -#define UARTSTAT_M21F 0x00004000 - -#define UARTCTRL_R8T9 0x80000000 -#define UARTCTRL_R9T8 0x40000000 -#define UARTCTRL_TXDIR 0x20000000 -#define UARTCTRL_TXINV 0x10000000 -#define UARTCTRL_ORIE 0x08000000 -#define UARTCTRL_NEIE 0x04000000 -#define UARTCTRL_FEIE 0x02000000 -#define UARTCTRL_PEIE 0x01000000 -#define UARTCTRL_TIE 0x00800000 -#define UARTCTRL_TCIE 0x00400000 -#define UARTCTRL_RIE 0x00200000 -#define UARTCTRL_ILIE 0x00100000 -#define UARTCTRL_TE 0x00080000 -#define UARTCTRL_RE 0x00040000 -#define UARTCTRL_RWU 0x00020000 -#define UARTCTRL_SBK 0x00010000 -#define UARTCTRL_MA1IE 0x00008000 -#define UARTCTRL_MA2IE 0x00004000 -#define UARTCTRL_IDLECFG_OFF 0x8 -#define UARTCTRL_LOOPS 0x00000080 -#define UARTCTRL_DOZEEN 0x00000040 -#define UARTCTRL_RSRC 0x00000020 -#define UARTCTRL_M 0x00000010 -#define UARTCTRL_WAKE 0x00000008 -#define UARTCTRL_ILT 0x00000004 -#define UARTCTRL_PE 0x00000002 -#define UARTCTRL_PT 0x00000001 - -#define UARTDATA_NOISY 0x00008000 -#define UARTDATA_PARITYE 0x00004000 -#define UARTDATA_FRETSC 0x00002000 -#define UARTDATA_RXEMPT 0x00001000 -#define UARTDATA_IDLINE 0x00000800 -#define UARTDATA_MASK 0x3ff - -#define UARTMODIR_IREN 0x00020000 -#define UARTMODIR_RTSWATER_S 0x8 -#define UARTMODIR_TXCTSSRC 0x00000020 -#define UARTMODIR_TXCTSC 0x00000010 -#define UARTMODIR_RXRTSE 0x00000008 -#define UARTMODIR_TXRTSPOL 0x00000004 -#define UARTMODIR_TXRTSE 0x00000002 -#define UARTMODIR_TXCTSE 0x00000001 - -#define UARTFIFO_TXEMPT 0x00800000 -#define UARTFIFO_RXEMPT 0x00400000 -#define UARTFIFO_TXOF 0x00020000 -#define UARTFIFO_RXUF 0x00010000 -#define UARTFIFO_TXFLUSH 0x00008000 -#define UARTFIFO_RXFLUSH 0x00004000 -#define UARTFIFO_RXIDEN_MASK 0x7 -#define UARTFIFO_RXIDEN_OFF 10 -#define UARTFIFO_TXOFE 0x00000200 -#define UARTFIFO_RXUFE 0x00000100 -#define UARTFIFO_TXFE 0x00000080 -#define UARTFIFO_FIFOSIZE_MASK 0x7 -#define UARTFIFO_TXSIZE_OFF 4 -#define UARTFIFO_RXFE 0x00000008 -#define UARTFIFO_RXSIZE_OFF 0 -#define UARTFIFO_DEPTH(x) (0x1 << ((x) ? ((x) + 1) : 0)) - -#define UARTWATER_COUNT_MASK 0xff -#define UARTWATER_TXCNT_OFF 8 -#define UARTWATER_RXCNT_OFF 24 -#define UARTWATER_WATER_MASK 0xff -#define UARTWATER_TXWATER_OFF 0 -#define UARTWATER_RXWATER_OFF 16 - -#define UARTGLOBAL_RST 0x2 - -#define UARTFIFO_RXIDEN_RDRF 0x3 -#define UARTCTRL_IDLECFG 0x7 +/* LPUART Register Offsets **************************************************/ + +#define IMX9_LPUART_VERID_OFFSET (0x00) /* Version ID Register (VERID) */ +#define IMX9_LPUART_PARAM_OFFSET (0x04) /* Parameter Register (PARAM) */ +#define IMX9_LPUART_GLOBAL_OFFSET (0x08) /* LPUART Global Register (GLOBAL) */ +#define IMX9_LPUART_PINCFG_OFFSET (0x0c) /* LPUART Pin Configuration Register (PINCFG) */ +#define IMX9_LPUART_BAUD_OFFSET (0x10) /* LPUART Baud Rate Register (BAUD) */ +#define IMX9_LPUART_STAT_OFFSET (0x14) /* LPUART Status Register (STAT) */ +#define IMX9_LPUART_CTRL_OFFSET (0x18) /* LPUART Control Register (CTRL) */ +#define IMX9_LPUART_DATA_OFFSET (0x1c) /* LPUART Data Register (DATA) */ +#define IMX9_LPUART_MATCH_OFFSET (0x20) /* LPUART Match Address Register (MATCH) */ +#define IMX9_LPUART_MODIR_OFFSET (0x24) /* LPUART Modem IrDA Register (MODIR) */ +#define IMX9_LPUART_FIFO_OFFSET (0x28) /* LPUART FIFO Register (FIFO) */ +#define IMX9_LPUART_WATER_OFFSET (0x2c) /* LPUART Watermark Register (WATER) */ +#define IMX9_LPUART_DATARO_OFFSET (0x30) /* Data read-only Register (DATARO) */ + +/* Register bit definitions *************************************************/ + +/* Version ID Register (VERID) */ + +#define LPUART_VERID_FEATURE_SHIFT (0) /* Bits 0-15: Feature Identification Number (FEATURE) */ +#define LPUART_VERID_FEATURE_MASK (0xffff << LPUART_VERID_FEATURE_SHIFT) +# define LPUART_VERID_FEATURE_STD (1 << LPUART_VERID_FEATURE_SHIFT) /* Standard feature set */ +# define LPUART_VERID_FEATURE_MODEM (3 << LPUART_VERID_FEATURE_SHIFT) /* MODEM/IrDA support */ + +#define LPUART_VERID_MINOR_SHIFT (16) /* Bits 16-23: Minor Version Number (MINOR) */ +#define LPUART_VERID_MINOR_MASK (0xff << LPUART_VERID_MINOR_SHIFT) +#define LPUART_VERID_MAJOR_SHIFT (24) /* Bits 24-31: Major Version Number (MAJOR) */ +#define LPUART_VERID_MAJOR_MASK (0xff << LPUART_VERID_MAJOR_SHIFT) + +/* Parameter Register (PARAM) */ + +#define LPUART_PARAM_TXFIFO_SHIFT (0) /* Bits 0-7: Transmit FIFO Size (TXFIFO) */ +#define LPUART_PARAM_TXFIFO_MASK (0xff << LPUART_PARAM_TXFIFO_SHIFT) +#define LPUART_PARAM_RXFIFO_SHIFT (8) /* Bits 8-15: Receive FIFO Size (RXFIFO) */ +#define LPUART_PARAM_RXFIFO_MASK (0xff << LPUART_PARAM_RXFIFO_SHIFT) + /* Bits 16-31: Reserved */ + +/* LPUART Global Register (GLOBAL) */ + + /* Bit 0: Reserved */ +#define LPUART_GLOBAL_RST (1 << 1) /* Bit 1: Software Reset (RST) */ + /* Bits 2-31: Reserved */ + +/* LPUART Pin Configuration Register (PINCFG) */ + +#define LPUART_PINCFG_TRGSEL_SHIFT (0) /* Bits 0-1: Trigger Select (TRGSEL) */ +#define LPUART_PINCFG_TRGSEL_MASK (0x03 << LPUART_PINCFG_TRGSEL_SHIFT) +# define LPUART_PINCFG_TRGSEL_DISABLE (0 << LPUART_PINCFG_TRGSEL_SHIFT) /* Trigger disabled */ +# define LPUART_PINCFG_TRGSEL_RXD (1 << LPUART_PINCFG_TRGSEL_SHIFT) /* Trigger used instead of RXD pin */ +# define LPUART_PINCFG_TRGSEL_CTSB (2 << LPUART_PINCFG_TRGSEL_SHIFT) /* Trigger used instead of CTS_B pin */ +# define LPUART_PINCFG_TRGSEL_TXDMOD (3 << LPUART_PINCFG_TRGSEL_SHIFT) /* Trigger used to modulate the TXD output */ + + /* Bits 2-31: Reserved */ + +/* LPUART Baud Rate Register (BAUD) */ + +#define LPUART_BAUD_SBR_SHIFT (0) /* Bits 0-12: Baud Rate Modulo Divisor (SBR) */ +#define LPUART_BAUD_SBR_MASK (0x1fff << LPUART_BAUD_SBR_SHIFT) +# define LPUART_BAUD_SBR(n) ((n) << LPUART_BAUD_SBR_SHIFT) +#define LPUART_BAUD_SBNS (1 << 13) /* Bit 13: Stop Bit Number Select (SBNS) */ +#define LPUART_BAUD_RXEDGIE (1 << 14) /* Bit 14: RX Input Active Edge Interrupt Enable (RXEDGIE) */ +#define LPUART_BAUD_LBKDIE (1 << 15) /* Bit 15: LIN Break Detect Interrupt Enable (LBKDIE) */ +#define LPUART_BAUD_RESYNCDIS (1 << 16) /* Bit 16: Resynchronization Disable (RESYNCDIS) */ +#define LPUART_BAUD_BOTHEDGE (1 << 17) /* Bit 17: Both Edge Sampling (BOTHEDGE) */ +#define LPUART_BAUD_MATCFG_SHIFT (18) /* Bits 18-19: Match Configuration (MATCFG) */ +#define LPUART_BAUD_MATCFG_MASK (0x03 << LPUART_BAUD_MATCFG_SHIFT) +# define LPUART_BAUD_MATCFG_ADDR (0 << LPUART_BAUD_MATCFG_SHIFT) /* Address Match Wakeup */ +# define LPUART_BAUD_MATCFG_IDLE (1 << LPUART_BAUD_MATCFG_SHIFT) /* Idle Match Wakeup */ +# define LPUART_BAUD_MATCFG_ONOFF (2 << LPUART_BAUD_MATCFG_SHIFT) /* Match On and Match Off */ +# define LPUART_BAUD_MATCFG_RWUENAB (3 << LPUART_BAUD_MATCFG_SHIFT) /* Enables RWU on Data Match and Match On/Off for transmitter CTS input */ + + /* Bit 20: Reserved */ +#define LPUART_BAUD_RDMAE (1 << 21) /* Bit 21: Receiver Full DMA Enable (RDMAE) */ + /* Bit 22: Reserved */ +#define LPUART_BAUD_TDMAE (1 << 23) /* Bit 23: Transmitter DMA Enable (TDMAE) */ +#define LPUART_BAUD_OSR_SHIFT (24) /* Bits 24-29: Oversampling Ratio (OSR) */ +#define LPUART_BAUD_OSR_MASK (0x1f << LPUART_BAUD_OSR_SHIFT) +# define LPUART_BAUD_OSR(n) (((n) - 1) << LPUART_BAUD_OSR_SHIFT) /* n=4..32 */ + +#define LPUART_BAUD_M10 (1 << 29) /* Bit 29: 10-bit Mode Select (M10) */ +#define LPUART_BAUD_MAEN2 (1 << 30) /* Bit 30: Match Address Mode Enable 2 (MAEN2) */ +#define LPUART_BAUD_MAEN1 (1 << 31) /* Bit 31: Match Address Mode Enable 1 (MAEN1) */ + +/* LPUART Status Register (STAT) */ + +#define LPUART_STAT_LBKFE (1 << 0) /* Bit 0: LIN Break Flag Enable (LBKFE) */ +#define LPUART_STAT_AME (1 << 1) /* Bit 1: Address Mark Enable (AME) */ + /* Bits 2-13: Reserved */ +#define LPUART_STAT_MA2F (1 << 14) /* Bit 14: Match 2 Flag (MA2F) */ +#define LPUART_STAT_MA1F (1 << 15) /* Bit 15: Match 1 Flag (MA1F) */ +#define LPUART_STAT_PF (1 << 16) /* Bit 16: Parity Error Flag (PF) */ +#define LPUART_STAT_FE (1 << 17) /* Bit 17: Framing Error Flag (FE) */ +#define LPUART_STAT_NF (1 << 18) /* Bit 18: Noise Flag (NF) */ +#define LPUART_STAT_OR (1 << 19) /* Bit 19: Receiver Overrun Flag (OR) */ +#define LPUART_STAT_IDLE (1 << 20) /* Bit 20: Idle Line Flag (IDLE) */ +#define LPUART_STAT_RDRF (1 << 21) /* Bit 21: Receive Data Register Full Flag (RDRF) */ +#define LPUART_STAT_TC (1 << 22) /* Bit 22: Transmission Complete Flag (TC) */ +#define LPUART_STAT_TDRE (1 << 23) /* Bit 23: Transmit Data Register Empty Flag (TDRE) */ +#define LPUART_STAT_RAF (1 << 24) /* Bit 24: Receiver Active Flag (RAF) */ +#define LPUART_STAT_LBKDE (1 << 25) /* Bit 25: LIN Break Detection Enable (LBKDE) */ +#define LPUART_STAT_BRK13 (1 << 26) /* Bit 26: Break Character Generation Length (BRK13) */ +#define LPUART_STAT_RWUID (1 << 27) /* Bit 27: Receive Wake Up Idle Detect (RWUID) */ +#define LPUART_STAT_RXINV (1 << 28) /* Bit 28: Receive Data Inversion (RXINV) */ +#define LPUART_STAT_MSBF (1 << 29) /* Bit 29: MSB First (MSBF) */ +#define LPUART_STAT_RXEDGIF (1 << 30) /* Bit 30: RXD Pin Active Edge Interrupt Flag (RXEDGIF) */ +#define LPUART_STAT_LBKDIF (1 << 31) /* Bit 31: LIN Break Detect Interrupt Flag (LBKDIF) */ + +/* LPUART Control Register (CTRL) */ + +#define LPUART_CTRL_PT (1 << 0) /* Bit 0: Parity Type */ +# define LPUART_CTRL_PT_EVEN (0 << 0) /* Even parity */ +# define LPUART_CTRL_PT_ODD (1 << 0) /* Odd parity */ +#define LPUART_CTRL_PE (1 << 1) /* Bit 1: Parity Enable */ +#define LPUART_CTRL_ILT (1 << 2) /* Bit 2: Idle Line Type Select */ +#define LPUART_CTRL_WAKE (1 << 3) /* Bit 3: Receiver Wakeup Method Select */ +#define LPUART_CTRL_M (1 << 4) /* Bit 4: 9-Bit or 8-Bit Mode Select */ +#define LPUART_CTRL_RSRC (1 << 5) /* Bit 5: Receiver Source Select */ +#define LPUART_CTRL_DOZEEN (1 << 6) /* Bit 6: Doze Enable */ +#define LPUART_CTRL_LOOPS (1 << 7) /* Bit 7: Loop Mode Select */ +#define LPUART_CTRL_IDLECFG_SHIFT (8) /* Bits 8-10: Idle Configuration */ +#define LPUART_CTRL_IDLECFG_MASK (0x07 << LPUART_CTRL_IDLECFG_SHIFT) +# define LPUART_CTRL_IDLECFG_1 (0 << LPUART_CTRL_IDLECFG_SHIFT) /* 1 idle character */ +# define LPUART_CTRL_IDLECFG_2 (1 << LPUART_CTRL_IDLECFG_SHIFT) /* 2 idle characters */ +# define LPUART_CTRL_IDLECFG_4 (2 << LPUART_CTRL_IDLECFG_SHIFT) /* 4 idle characters */ +# define LPUART_CTRL_IDLECFG_8 (3 << LPUART_CTRL_IDLECFG_SHIFT) /* 8 idle characters */ +# define LPUART_CTRL_IDLECFG_16 (4 << LPUART_CTRL_IDLECFG_SHIFT) /* 6 idle characters */ +# define LPUART_CTRL_IDLECFG_32 (5 << LPUART_CTRL_IDLECFG_SHIFT) /* 32 idle characters */ +# define LPUART_CTRL_IDLECFG_64 (6 << LPUART_CTRL_IDLECFG_SHIFT) /* 64 idle characters */ +# define LPUART_CTRL_IDLECFG_128 (7 << LPUART_CTRL_IDLECFG_SHIFT) /* 128 idle characters */ + +#define LPUART_CTRL_M7 (1 << 11) /* Bit 11: 7-Bit Mode Select (M7) */ + /* Bits 12-13: Reserved */ +#define LPUART_CTRL_MA2IE (1 << 14) /* Bit 14: Match 2 Interrupt Enable (MA2IE) */ +#define LPUART_CTRL_MA1IE (1 << 15) /* Bit 15: Match 1 Interrupt Enable (MA1IE) */ +#define LPUART_CTRL_SBK (1 << 16) /* Bit 16: Send Break (SBK) */ +#define LPUART_CTRL_RWU (1 << 17) /* Bit 17: Receiver Wakeup Control (RWU) */ +#define LPUART_CTRL_RE (1 << 18) /* Bit 18: Receiver Enable (RE) */ +#define LPUART_CTRL_TE (1 << 19) /* Bit 19: Transmitter Enable (TE) */ +#define LPUART_CTRL_ILIE (1 << 20) /* Bit 20: Idle Line Interrupt Enable (ILIE) */ +#define LPUART_CTRL_RIE (1 << 21) /* Bit 21: Receiver Interrupt Enable (RIE) */ +#define LPUART_CTRL_TCIE (1 << 22) /* Bit 22: Transmission Complete Interrupt Enable (TCIE) */ +#define LPUART_CTRL_TIE (1 << 23) /* Bit 23: Transmit Interrupt Enable (TIE) */ +#define LPUART_CTRL_PEIE (1 << 24) /* Bit 24: Parity Error Interrupt Enable (PEIE) */ +#define LPUART_CTRL_FEIE (1 << 25) /* Bit 25: Framing Error Interrupt Enable (FEIE) */ +#define LPUART_CTRL_NEIE (1 << 26) /* Bit 26: Noise Error Interrupt Enable (NEIE) */ +#define LPUART_CTRL_ORIE (1 << 27) /* Bit 27: Overrun Interrupt Enable (ORIE) */ +#define LPUART_CTRL_TXINV (1 << 28) /* Bit 28: Transmit Data Inversion (TXINV) */ +#define LPUART_CTRL_TXDIR (1 << 29) /* Bit 29: TXD Pin Direction in Single-Wire Mode (TXDIR) */ +#define LPUART_CTRL_R9T8 (1 << 30) /* Bit 30: Receive Bit 9 / Transmit Bit 8 (R9T8) */ +#define LPUART_CTRL_R8T9 (1 << 31) /* Bit 31: Receive Bit 8 / Transmit Bit 9 (R8T9) */ + +#define LPUART_ALL_INTS (LPUART_CTRL_ORIE | LPUART_CTRL_NEIE | LPUART_CTRL_FEIE | \ + LPUART_CTRL_PEIE | LPUART_CTRL_TIE | LPUART_CTRL_TCIE | \ + LPUART_CTRL_RIE | LPUART_CTRL_ILIE | LPUART_CTRL_MA1IE | \ + LPUART_CTRL_MA2IE) + +/* LPUART Data Register (DATA) */ + +#define LPUART_DATA_SHIFT (0) /* Bits 0-9: Data bits 0-9 (DATA)*/ +#define LPUART_DATA_MASK (0x03ff << LPUART_DATA_SHIFT) +#define LPUART_DATA_LINBRK (1 << 10) /* Bit 10: LIN Break (LINBRK) */ +#define LPUART_DATA_STATUS_SHIFT (11) /* Bits 11-15: Status */ +#define LPUART_DATA_IDLINE (1 << 11) /* Bit 11: Idle Line (IDLINE) */ +#define LPUART_DATA_RXEMPT (1 << 12) /* Bit 12: Receive Buffer Empty (RXEMPT) */ +#define LPUART_DATA_FRETSC (1 << 13) /* Bit 13: Frame Error / Transmit Special Character (FRETSC) */ +#define LPUART_DATA_PARITYE (1 << 14) /* Bit 14: Parity Error (PARITYE) */ +#define LPUART_DATA_NOISY (1 << 15) /* Bit 15: Noisy Data Received (NOISY) */ + /* Bits 16-31: Reserved */ + +/* LPUART Match Address Register (MATCH) */ + +#define LPUART_MATCH_MA1_SHIFT (0) /* Bits 0-9: Match Address 1 (MA1) */ +#define LPUART_MATCH_MA1_MASK (0x03ff << LPUART_MATCH_MA1_SHIFT) +# define LPUART_MATCH_MA1(n) ((n) << LPUART_MATCH_MA1_SHIFT) + /* Bits 10-15: Reserved */ +#define LPUART_MATCH_MA2_SHIFT (16) /* Bits 16-25: Match Address 2 (MA2) */ +#define LPUART_MATCH_MA2_MASK (0x03ff << LPUART_MATCH_MA2_SHIFT) +# define LPUART_MATCH_MA2(n) ((n) << LPUART_MATCH_MA2_SHIFT) + /* Bits 26-31: Reserved */ + +/* LPUART Modem IrDA Register (MODIR) */ + +#define LPUART_MODIR_TXCTSE (1 << 0) /* Bit 0: Transmitter clear-to-send enable (TXCTSE) */ +#define LPUART_MODIR_TXRTSE (1 << 1) /* Bit 1: Transmitter request-to-send enable (TXRTSE) */ +#define LPUART_MODIR_TXRTSPOL (1 << 2) /* Bit 2: Transmitter request-to-send polarity (TXRTSPOL) */ +#define LPUART_MODIR_RXRTSE (1 << 3) /* Bit 3: Receiver request-to-send enable (RXRTSE) */ +#define LPUART_MODIR_TXCTSC (1 << 4) /* Bit 4: Transmit CTS Configuration (TXCTSC) */ +# define LPUART_MODIR_TXCTSC_START (0 << 4) /* CTS sampled at start of character */ +# define LPUART_MODIR_TXCTSC_IDLE (1 << 4) /* CTS sampled when transmitter idle */ +#define LPUART_MODIR_TXCTSSRC (1 << 5) /* Bit 5: Transmit CTS Source (TXCTSSRC) */ +# define LPUART_MODIR_TXCTSSRC_CTSB (0 << 5) /* CTS input is CTS_B pin */ +# define LPUART_MODIR_TXCTSSRC_RXMAT (1 << 5) /* CTS input is receiver address match result */ + /* Bits 6-7: Reserved */ +#define LPUART_MODIR_RTSWATER_SHIFT (8) /* Bits 8-9: Receive RTS Configuration (RTSWATER) */ +#define LPUART_MODIR_RTSWATER_MASK (0x03 << LPUART_MODIR_RTSWATER_SHIFT) +# define LPUART_MODIR_RTSWATER(n) ((n) << LPUART_MODIR_RTSWATER_SHIFT) + /* Bits 10-15: Reserved */ +#define LPUART_MODIR_TNP_SHIFT (16) /* Bits 16-17: Transmitter narrow pulse (TNP) */ +#define LPUART_MODIR_TNP_MASK (0x03 << LPUART_MODIR_TNP_SHIFT) +# define LPUART_MODIR_TNP(n) (((n) - 1) << LPUART_MODIR_TNP_SHIFT) /* n/OSR */ + +#define LPUART_MODIR_IREN (1 << 18) /* Bit 18: Infrared enable (IREN) */ + /* Bits 19-31: Reserved */ + +/* LPUART FIFO Register (FIFO) */ + +#define LPUART_FIFO_RXFIFOSIZE_SHIFT (0) /* Bits 0-2: Receive FIFO Buffer Depth (RXFIFOSIZE) */ +#define LPUART_FIFO_RXFIFOSIZE_MASK (0x07 << LPUART_FIFO_RXFIFOSIZE_SHIFT) +# define LPUART_FIFO_RXFIFOSIZE_1 (0 << LPUART_FIFO_RXFIFOSIZE_SHIFT) /* 1 dataword */ +# define LPUART_FIFO_RXFIFOSIZE_4 (1 << LPUART_FIFO_RXFIFOSIZE_SHIFT) /* 4 datawords */ +# define LPUART_FIFO_RXFIFOSIZE_8 (2 << LPUART_FIFO_RXFIFOSIZE_SHIFT) /* 8 datawords */ +# define LPUART_FIFO_RXFIFOSIZE_16 (3 << LPUART_FIFO_RXFIFOSIZE_SHIFT) /* 16 datawords */ +# define LPUART_FIFO_RXFIFOSIZE_32 (4 << LPUART_FIFO_RXFIFOSIZE_SHIFT) /* 32 datawords */ +# define LPUART_FIFO_RXFIFOSIZE_64 (5 << LPUART_FIFO_RXFIFOSIZE_SHIFT) /* 64 datawords */ +# define LPUART_FIFO_RXFIFOSIZE_128 (6 << LPUART_FIFO_RXFIFOSIZE_SHIFT) /* 128 datawords */ +# define LPUART_FIFO_RXFIFOSIZE_256 (7 << LPUART_FIFO_RXFIFOSIZE_SHIFT) /* 256 datawords */ + +#define LPUART_FIFO_RXFE (1 << 3) /* Bit 3: Receive FIFO Enable (RXFE) */ +#define LPUART_FIFO_TXFIFOSIZE_SHIFT (4) /* Bits 4-6: Transmit FIFO Buffer Depth (TXFIFOSIZE) */ +#define LPUART_FIFO_TXFIFOSIZE_MASK (0x07 << LPUART_FIFO_TXFIFOSIZE_SHIFT) +# define LPUART_FIFO_TXFIFOSIZE_1 (0 << LPUART_FIFO_TXFIFOSIZE_SHIFT) /* 1 dataword */ +# define LPUART_FIFO_TXFIFOSIZE_4 (1 << LPUART_FIFO_TXFIFOSIZE_SHIFT) /* 4 datawords */ +# define LPUART_FIFO_TXFIFOSIZE_8 (2 << LPUART_FIFO_TXFIFOSIZE_SHIFT) /* 8 datawords */ +# define LPUART_FIFO_TXFIFOSIZE_16 (3 << LPUART_FIFO_TXFIFOSIZE_SHIFT) /* 16 datawords */ +# define LPUART_FIFO_TXFIFOSIZE_32 (4 << LPUART_FIFO_TXFIFOSIZE_SHIFT) /* 32 datawords */ +# define LPUART_FIFO_TXFIFOSIZE_64 (5 << LPUART_FIFO_TXFIFOSIZE_SHIFT) /* 64 datawords */ +# define LPUART_FIFO_TXFIFOSIZE_128 (6 << LPUART_FIFO_TXFIFOSIZE_SHIFT) /* 128 datawords */ +# define LPUART_FIFO_TXFIFOSIZE_256 (7 << LPUART_FIFO_TXFIFOSIZE_SHIFT) /* 256 datawords */ + +#define LPUART_FIFO_TXFE (1 << 7) /* Bit 7: Transmit FIFO Enable (TXFE) */ +#define LPUART_FIFO_RXUFE (1 << 8) /* Bit 8: Receive FIFO Underflow Interrupt Enable (RXUFE) */ +#define LPUART_FIFO_TXOFE (1 << 9) /* Bit 9: Transmit FIFO Overflow Interrupt Enable (TXOFE) */ +#define LPUART_FIFO_RXIDEN_SHIFT (10) /* Bits 10-12: Receiver Idle Empty Enable (RXIDEN) */ +#define LPUART_FIFO_RXIDEN_MASK (0x07 << LPUART_FIFO_RXIDEN_SHIFT) +# define LPUART_FIFO_RXIDEN_DISABLE (0 << LPUART_FIFO_RXIDEN_SHIFT) /* Disable RDRF assertion when receiver is idle */ +# define LPUART_FIFO_RXIDEN_1 (1 << LPUART_FIFO_RXIDEN_SHIFT) /* Enable RDRF assertion when receiver idle for 1 character */ +# define LPUART_FIFO_RXIDEN_2 (2 << LPUART_FIFO_RXIDEN_SHIFT) /* Enable RDRF assertion when receiver idle for 2 characters */ +# define LPUART_FIFO_RXIDEN_4 (3 << LPUART_FIFO_RXIDEN_SHIFT) /* Enable RDRF assertion when receiver idle for 4 characters */ +# define LPUART_FIFO_RXIDEN_8 (4 << LPUART_FIFO_RXIDEN_SHIFT) /* Enable RDRF assertion when receiver idle for 8 characters */ +# define LPUART_FIFO_RXIDEN_16 (5 << LPUART_FIFO_RXIDEN_SHIFT) /* Enable RDRF assertion when receiver idle for 16 characters */ +# define LPUART_FIFO_RXIDEN_32 (6 << LPUART_FIFO_RXIDEN_SHIFT) /* Enable RDRF assertion when receiver idle for 32 characters */ +# define LPUART_FIFO_RXIDEN_64 (7 << LPUART_FIFO_RXIDEN_SHIFT) /* Enable RDRF assertion when receiver idle for 64 characters */ + + /* Bit 13: Reserved */ +#define LPUART_FIFO_RXFLUSH (1 << 14) /* Bit 14: Receive FIFO Flush (RXFLUSH) */ +#define LPUART_FIFO_TXFLUSH (1 << 15) /* Bit 15: Transmit FIFO Flush (TXFLUSH) */ +#define LPUART_FIFO_RXUF (1 << 16) /* Bit 16: Receiver FIFO Underflow Flag (RXUF) */ +#define LPUART_FIFO_TXOF (1 << 17) /* Bit 17: Transmitter FIFO Overflow Flag (TXOF) */ + /* Bits 18-21: Reserved */ +#define LPUART_FIFO_RXEMPT (1 << 22) /* Bit 22: Receive Buffer/FIFO Empty (RXEMPT) */ +#define LPUART_FIFO_TXEMPT (1 << 23) /* Bit 23: Transmit Buffer/FIFO Empty (TXEMPT) */ + /* Bits 24-31: Reserved */ + +/* LPUART Watermark Register (WATER) */ + +#define LPUART_WATER_TXWATER_SHIFT (0) /* Bits 0-1: Transmit Watermark (TXWATER) */ +#define LPUART_WATER_TXWATER_MASK (0x03 << LPUART_WATER_TXWATER_SHIFT) +# define LPUART_WATER_TXWATER(n) ((n) << LPUART_WATER_TXWATER_SHIFT) + /* Bits 2-7: Reserved */ +#define LPUART_WATER_TXCOUNT_SHIFT (8) /* Bits 8-10: Transmit Counter (TXCOUNT) */ +#define LPUART_WATER_TXCOUNT_MASK (0x07 << LPUART_WATER_TXCOUNT_SHIFT) +# define LPUART_WATER_TXCOUNT(n) ((n) << LPUART_WATER_TXCOUNT_SHIFT) + /* Bits 11-15: Reserved */ +#define LPUART_WATER_RXWATER_SHIFT (16) /* Bits 16-17: Receive Watermark (RXWATER) */ +#define LPUART_WATER_RXWATER_MASK (0x03 << LPUART_WATER_RXWATER_SHIFT) +# define LPUART_WATER_RXWATER(n) ((n) << LPUART_WATER_RXWATER_SHIFT) + /* Bits 18-23: Reserved */ +#define LPUART_WATER_RXCOUNT_SHIFT (24) /* Bits 24-26: Receive Counter (RXCOUNT) */ +#define LPUART_WATER_RXCOUNT_MASK (0x07 << LPUART_WATER_RXCOUNT_SHIFT) +# define LPUART_WATER_RXCOUNT(n) ((n) << LPUART_WATER_RXCOUNT_SHIFT) + /* Bits 27-31: Reserved */ + +/* Data read-only Register (DATARO) */ + +#define LPUART_DATARO_DATA_SHIFT (0) /* Bits 0-15: Receive Data (DATA) */ +#define LPUART_DATARO_DATA_MASK (0xffff << LPUART_DATARO_DATA_SHIFT) + /* Bits 16-31: Reserved */ #endif /* __ARCH_ARM64_SRC_IMX9_HARDWARE_IMX9_LPUART_H */ diff --git a/arch/arm64/src/imx9/imx9_boot.c b/arch/arm64/src/imx9/imx9_boot.c index 16f19de2c6b54..260e67997428f 100644 --- a/arch/arm64/src/imx9/imx9_boot.c +++ b/arch/arm64/src/imx9/imx9_boot.c @@ -39,6 +39,7 @@ #include "arm64_mmu.h" #include "imx9_boot.h" #include "imx9_serial.h" +#include "imx9_lowputc.h" /**************************************************************************** * Private Data @@ -96,6 +97,12 @@ void arm64_chip_boot(void) arm64_mmu_init(true); + /* Do UART early initialization & pin muxing */ + +#ifdef CONFIG_IMX9_LPUART + imx9_lowsetup(); +#endif + /* Perform board-specific device initialization. This would include * configuration of board specific resources such as GPIOs, LEDs, etc. */ diff --git a/arch/arm64/src/imx9/imx9_lowputc.c b/arch/arm64/src/imx9/imx9_lowputc.c new file mode 100644 index 0000000000000..c41f6f43eed12 --- /dev/null +++ b/arch/arm64/src/imx9/imx9_lowputc.c @@ -0,0 +1,532 @@ +/**************************************************************************** + * arch/arm64/src/imx9/imx9_lowputc.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include + +#include "hardware/imx9_pinmux.h" +#include "hardware/imx9_lpuart.h" + +#include "arm64_internal.h" + +#include "imx9_lowputc.h" +#include "imx9_ccm.h" +#include "imx9_iomuxc.h" +#include "hardware/imx9_ccm.h" +#include "hardware/imx9_pinmux.h" + +#include /* Include last: has dependencies */ + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Configuration ************************************************************/ + +#if defined(CONFIG_LPUART1_SERIAL_CONSOLE) +# define IMX9_CONSOLE_DEVOFF 0 +# define IMX9_CONSOLE_BASE IMX9_LPUART1_BASE +# define IMX9_CONSOLE_BAUD CONFIG_LPUART1_BAUD +# define IMX9_CONSOLE_BITS CONFIG_LPUART1_BITS +# define IMX9_CONSOLE_PARITY CONFIG_LPUART1_PARITY +# define IMX9_CONSOLE_2STOP CONFIG_LPUART1_2STOP +#elif defined(CONFIG_LPUART2_SERIAL_CONSOLE) +# define IMX9_CONSOLE_DEVNUM 1 +# define IMX9_CONSOLE_BASE IMX9_LPUART2_BASE +# define IMX9_CONSOLE_BAUD CONFIG_LPUART2_BAUD +# define IMX9_CONSOLE_BITS CONFIG_LPUART2_BITS +# define IMX9_CONSOLE_PARITY CONFIG_LPUART2_PARITY +# define IMX9_CONSOLE_2STOP CONFIG_LPUART2_2STOP +#elif defined(CONFIG_LPUART3_SERIAL_CONSOLE) +# define IMX9_CONSOLE_DEVNUM 2 +# define IMX9_CONSOLE_BASE IMX9_LPUART3_BASE +# define IMX9_CONSOLE_BAUD CONFIG_LPUART3_BAUD +# define IMX9_CONSOLE_BITS CONFIG_LPUART3_BITS +# define IMX9_CONSOLE_PARITY CONFIG_LPUART3_PARITY +# define IMX9_CONSOLE_2STOP CONFIG_LPUART3_2STOP +#elif defined(CONFIG_LPUART4_SERIAL_CONSOLE) +# define IMX9_CONSOLE_DEVNUM 3 +# define IMX9_CONSOLE_BASE IMX9_LPUART4_BASE +# define IMX9_CONSOLE_BAUD CONFIG_LPUART4_BAUD +# define IMX9_CONSOLE_BITS CONFIG_LPUART4_BITS +# define IMX9_CONSOLE_PARITY CONFIG_LPUART4_PARITY +# define IMX9_CONSOLE_2STOP CONFIG_LPUART4_2STOP +#elif defined(CONFIG_LPUART5_SERIAL_CONSOLE) +# define IMX9_CONSOLE_DEVNUM 4 +# define IMX9_CONSOLE_BASE IMX9_LPUART5_BASE +# define IMX9_CONSOLE_BAUD CONFIG_LPUART5_BAUD +# define IMX9_CONSOLE_BITS CONFIG_LPUART5_BITS +# define IMX9_CONSOLE_PARITY CONFIG_LPUART5_PARITY +# define IMX9_CONSOLE_2STOP CONFIG_LPUART5_2STOP +#elif defined(CONFIG_LPUART6_SERIAL_CONSOLE) +# define IMX9_CONSOLE_DEVNUM 5 +# define IMX9_CONSOLE_BASE IMX9_LPUART6_BASE +# define IMX9_CONSOLE_BAUD CONFIG_LPUART6_BAUD +# define IMX9_CONSOLE_BITS CONFIG_LPUART6_BITS +# define IMX9_CONSOLE_PARITY CONFIG_LPUART6_PARITY +# define IMX9_CONSOLE_2STOP CONFIG_LPUART6_2STOP +#elif defined(CONFIG_LPUART7_SERIAL_CONSOLE) +# define IMX9_CONSOLE_DEVNUM 6 +# define IMX9_CONSOLE_BASE IMX9_LPUART7_BASE +# define IMX9_CONSOLE_BAUD CONFIG_LPUART7_BAUD +# define IMX9_CONSOLE_BITS CONFIG_LPUART7_BITS +# define IMX9_CONSOLE_PARITY CONFIG_LPUART7_PARITY +# define IMX9_CONSOLE_2STOP CONFIG_LPUART7_2STOP +#elif defined(CONFIG_LPUART8_SERIAL_CONSOLE) +# define IMX9_CONSOLE_DEVNUM 7 +# define IMX9_CONSOLE_BASE IMX9_LPUART8_BASE +# define IMX9_CONSOLE_BAUD CONFIG_LPUART8_BAUD +# define IMX9_CONSOLE_BITS CONFIG_LPUART8_BITS +# define IMX9_CONSOLE_PARITY CONFIG_LPUART8_PARITY +# define IMX9_CONSOLE_2STOP CONFIG_LPUART8_2STOP +#endif + +#define ABS(n) (((n) < 0) ? -(n) : (n)) + +/* Clocking *****************************************************************/ + +/* Functional clocking is provided via the PCC. The PCC clocking must + * be configured by board-specific logic prior to using the LPUART. + */ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +#ifdef IMX9_CONSOLE_BASE +static const struct uart_config_s g_console_config = +{ + .baud = IMX9_CONSOLE_BAUD, /* Configured baud */ + .parity = IMX9_CONSOLE_PARITY, /* 0=none, 1=odd, 2=even */ + .bits = IMX9_CONSOLE_BITS, /* Number of bits (5-9) */ + .stopbits2 = IMX9_CONSOLE_2STOP, /* true: Configure with 2 stop bits instead of 1 */ +}; +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: imx9_lowsetup + * + * Description: + * Called at the very beginning of _start. Performs low level + * initialization including setup of the console UART. This UART done + * early so that the serial console is available for debugging very early + * in the boot sequence. + * + ****************************************************************************/ + +void imx9_lowsetup(void) +{ +#ifndef CONFIG_SUPPRESS_LPUART_CONFIG + +#ifdef CONFIG_IMX9_LPUART1 + /* Configure LPUART1 pins: RXD and TXD. Also configure RTS and CTS if flow + * control is enabled. + */ + + imx9_iomux_configure(MUX_LPUART1_RX); + imx9_iomux_configure(MUX_LPUART1_TX); +#ifdef CONFIG_LPUART1_OFLOWCONTROL + imx9_iomux_configure(MUX_LPUART1_CTS); +#endif +#if ((defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART1_RS485RTSCONTROL)) || \ + (defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART1_IFLOWCONTROL))) + imx9_iomux_configure(MUX_LPUART1_RTS); +#endif +#endif + +#ifdef CONFIG_IMX9_LPUART2 + + /* Configure LPUART2 pins: RXD and TXD. Also configure RTS and CTS if flow + * control is enabled. + */ + + imx9_iomux_configure(MUX_LPUART2_RX); + imx9_iomux_configure(MUX_LPUART2_TX); +#ifdef CONFIG_LPUART2_OFLOWCONTROL + imx9_iomux_configure(MUX_LPUART2_CTS); +#endif +#if ((defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART2_RS485RTSCONTROL)) || \ + (defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART2_IFLOWCONTROL))) + imx9_iomux_configure(MUX_LPUART2_RTS); +#endif +#endif + +#ifdef CONFIG_IMX9_LPUART3 + + /* Configure LPUART3 pins: RXD and TXD. Also configure RTS and CTS if flow + * control is enabled. + */ + + imx9_iomux_configure(MUX_LPUART3_RX); + imx9_iomux_configure(MUX_LPUART3_TX); +#ifdef CONFIG_LPUART3_OFLOWCONTROL + imx9_iomux_configure(MUX_LPUART3_CTS); +#endif +#if ((defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART3_RS485RTSCONTROL)) || \ + (defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART3_IFLOWCONTROL))) + imx9_iomux_configure(MUX_LPUART3_RTS); +#endif +#endif + +#ifdef CONFIG_IMX9_LPUART4 + + /* Configure LPUART4 pins: RXD and TXD. Also configure RTS and CTS if flow + * control is enabled. + */ + + imx9_iomux_configure(LPUART4_RX); + imx9_iomux_configure(LPUART4_TX); +#ifdef CONFIG_LPUART4_OFLOWCONTROL + imx9_iomux_configure(LPUART4_CTS); +#endif +#if ((defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART4_RS485RTSCONTROL)) || \ + (defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART4_IFLOWCONTROL))) + imx9_iomux_configure(LPUART4_RTS); +#endif +#endif + +#ifdef CONFIG_IMX9_LPUART5 + + /* Configure LPUART5 pins: RXD and TXD. Also configure RTS and CTS if flow + * control is enabled. + */ + + imx9_iomux_configure(MUX_LPUART5_RX); + imx9_iomux_configure(MUX_LPUART5_TX); +#ifdef CONFIG_LPUART5_OFLOWCONTROL + imx9_iomux_configure(MUX_LPUART5_CTS); +#endif +#if ((defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART5_RS485RTSCONTROL)) || \ + (defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART5_IFLOWCONTROL))) + imx9_iomux_configure(MUX_LPUART5_RTS); +#endif +#endif + +#ifdef CONFIG_IMX9_LPUART6 + + /* Configure LPUART6 pins: RXD and TXD. Also configure RTS and CTS if flow + * control is enabled. + */ + + imx9_iomux_configure(MUX_LPUART6_RX); + imx9_iomux_configure(MUX_LPUART6_TX); +#ifdef CONFIG_LPUART6_OFLOWCONTROL + imx9_iomux_configure(MUX_LPUART6_CTS); +#endif +#if ((defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART6_RS485RTSCONTROL)) || \ + (defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART6_IFLOWCONTROL))) + imx9_iomux_configure(MUX_LPUART6_RTS); +#endif +#endif + +#ifdef CONFIG_IMX9_LPUART7 + + /* Configure LPUART7 pins: RXD and TXD. Also configure RTS and CTS if flow + * control is enabled. + */ + + imx9_iomux_configure(MUX_LPUART7_RX); + imx9_iomux_configure(MUX_LPUART7_TX); +#ifdef CONFIG_LPUART7_OFLOWCONTROL + imx9_iomux_configure(MUX_LPUART7_CTS); +#endif +#if ((defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART7_RS485RTSCONTROL)) || \ + (defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART7_IFLOWCONTROL))) + imx9_iomux_configure(MUX_LPUART7_RTS); +#endif +#endif + +#ifdef CONFIG_IMX9_LPUART8 + + /* Configure LPUART8 pins: RXD and TXD. Also configure RTS and CTS if flow + * control is enabled. + */ + + imx9_iomux_configure(MUX_LPUART8_RX); + imx9_iomux_configure(MUX_LPUART8_TX); +#ifdef CONFIG_LPUART0_OFLOWCONTROL + imx9_iomux_configure(MUX_LPUART8_CTS); +#endif +#if ((defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART8_RS485RTSCONTROL)) || \ + (defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART8_IFLOWCONTROL))) + imx9_iomux_configure(MUX_LPUART8_RTS); +#endif +#endif + +#ifdef IMX9_CONSOLE_BASE + /* Configure the serial console for initial, non-interrupt driver mode */ + + imx9_lpuart_configure(IMX9_CONSOLE_BASE, IMX9_CONSOLE_DEVOFF, + &g_console_config); +#endif + +#endif /* CONFIG_SUPPRESS_LPUART_CONFIG */ +} + +/**************************************************************************** + * Name: imx9_lpuart_configure + * + * Description: + * Configure a UART for non-interrupt driven operation + * + ****************************************************************************/ + +int imx9_lpuart_configure(uint32_t base, int uartnum, + const struct uart_config_s *config) +{ + int lpuart_freq = 24000000; + uint16_t sbr; + uint16_t temp_sbr; + uint32_t osr; + uint32_t temp_osr; + int temp_diff; + int calculated_baud; + int baud_diff; + uint32_t regval; + + /* Configure root clock to 24MHz OSC */ + + imx9_ccm_configure_root_clock(CCM_CR_LPUART1 + uartnum - 1, OSC_24M, 1); + + /* Enable peripheral clock */ + + imx9_ccm_gate_on(CCM_LPCG_LPUART1 + uartnum - 1, true); + + /* This LPUART instantiation uses a slightly different baud rate + * calculation. The idea is to use the best OSR (over-sampling rate) + * possible. + * + * NOTE: OSR is typically hard-set to 16 in other LPUART instantiations + * loop to find the best OSR value possible, one that generates minimum + * baud_diff iterate through the rest of the supported values of OSR + */ + + baud_diff = config->baud; + osr = 0; + sbr = 0; + + for (temp_osr = 4; temp_osr <= 32; temp_osr++) + { + /* Calculate the temporary sbr value */ + + temp_sbr = (lpuart_freq / (config->baud * temp_osr)); + + /* Set temp_sbr to 1 if the sourceClockInHz can not satisfy the + * desired baud rate. + */ + + if (temp_sbr == 0) + { + temp_sbr = 1; + } + + /* Calculate the baud rate based on the temporary OSR and SBR values */ + + calculated_baud = (lpuart_freq / (temp_osr * temp_sbr)); + temp_diff = ABS(calculated_baud - config->baud); + + /* Select the better value between srb and (sbr + 1) */ + + calculated_baud = (lpuart_freq / (temp_osr * (temp_sbr + 1))); + if (temp_diff > + ABS(calculated_baud - config->baud)) + { + temp_diff = ABS(calculated_baud - config->baud); + temp_sbr++; + } + + if (temp_diff <= baud_diff) + { + baud_diff = temp_diff; + osr = temp_osr; + sbr = temp_sbr; + } + } + + if (baud_diff > ((config->baud * 3) / 100)) + { + /* Unacceptable baud rate difference of more than 3% */ + + return ERROR; + } + + /* Reset all internal logic and registers, except the Global Register */ + + regval = getreg32(base + IMX9_LPUART_GLOBAL_OFFSET); + regval |= LPUART_GLOBAL_RST; + putreg32(regval, base + IMX9_LPUART_GLOBAL_OFFSET); + + regval &= ~LPUART_GLOBAL_RST; + putreg32(regval, base + IMX9_LPUART_GLOBAL_OFFSET); + + /* Construct MODIR register */ + + regval = 0; + + if (config->userts) + { + regval |= LPUART_MODIR_RXRTSE; + } + else if (config->users485) + { + /* Both TX and RX side can't control RTS, so this gives + * the RX side precedence. This should have been filtered + * in layers above anyway, but it's just a precaution. + */ + + regval |= LPUART_MODIR_TXRTSE; + } + + if (config->usects) + { + regval |= LPUART_MODIR_TXCTSE; + } + + if (config->invrts) + { + regval |= LPUART_MODIR_TXRTSPOL; + } + + putreg32(regval, base + IMX9_LPUART_MODIR_OFFSET); + + regval = 0; + + if ((osr > 3) && (osr < 8)) + { + regval |= LPUART_BAUD_BOTHEDGE; + } + + if (config->stopbits2) + { + regval |= LPUART_BAUD_SBNS; + } + + regval |= LPUART_BAUD_OSR(osr) | LPUART_BAUD_SBR(sbr); + putreg32(regval, base + IMX9_LPUART_BAUD_OFFSET); + + regval = 0; + if (config->parity == 1) + { + regval |= LPUART_CTRL_PE | LPUART_CTRL_PT_ODD; + } + else if (config->parity == 2) + { + regval |= LPUART_CTRL_PE | LPUART_CTRL_PT_EVEN; + } + + if (config->bits == 9 || (config->bits == 8 && config->parity != 0)) + { + regval |= LPUART_CTRL_M; + } + else if ((config->bits == 8)) + { + regval &= ~LPUART_CTRL_M; + } + else + { + /* REVISIT: Here should be added support of other bit modes. */ + + return -ENOSYS; + } + + regval |= LPUART_CTRL_RE | LPUART_CTRL_TE; + putreg32(regval, base + IMX9_LPUART_CTRL_OFFSET); + + return OK; +} + +/**************************************************************************** + * Name: arm64_earlyprintinit + * + * Description: + * Configure LPUART1 for non-interrupt driven operation + * + ****************************************************************************/ + +void arm64_earlyprintinit(char ch) +{ + /* Assume bootloader has already set up the LPUART1 */ +} + +/**************************************************************************** + * Name: arm64_lowputc + * + * Description: + * Output a byte with as few system dependencies as possible. This will + * even work BEFORE the console is initialized if we are booting from U- + * Boot (and the same UART is used for the console, of course.) + * + ****************************************************************************/ + +void arm64_lowputc(char ch) +{ +#ifdef IMX9_CONSOLE_BASE + while ((getreg32(IMX9_CONSOLE_BASE + IMX9_LPUART_STAT_OFFSET) & + LPUART_STAT_TDRE) == 0) + { + } + + /* If the character to output is a newline, + * then pre-pend a carriage return + */ + + if (ch == '\n') + { + /* Send the carriage return by writing it into the UART_TXD register. */ + + putreg32((uint32_t)'\r', + IMX9_CONSOLE_BASE + IMX9_LPUART_DATA_OFFSET); + + /* Wait for the transmit register to be emptied. When the TXFE bit is + * non-zero, the TX Buffer FIFO is empty. + */ + + while ((getreg32(IMX9_CONSOLE_BASE + IMX9_LPUART_STAT_OFFSET) & + LPUART_STAT_TDRE) == 0) + { + } + } + + /* Send the character by writing it into the UART_TXD register. */ + + putreg32((uint32_t)ch, IMX9_CONSOLE_BASE + IMX9_LPUART_DATA_OFFSET); + + /* Wait for the transmit register to be emptied. When the TXFE bit is + * non-zero, the TX Buffer FIFO is empty. + */ + + while ((getreg32(IMX9_CONSOLE_BASE + IMX9_LPUART_STAT_OFFSET) & + LPUART_STAT_TDRE) == 0) + { + } +#endif +} diff --git a/arch/arm64/src/imx9/imx93_lowputc.S b/arch/arm64/src/imx9/imx9_lowputc.h similarity index 50% rename from arch/arm64/src/imx9/imx93_lowputc.S rename to arch/arm64/src/imx9/imx9_lowputc.h index f3311810e05dd..30aceed681c28 100644 --- a/arch/arm64/src/imx9/imx93_lowputc.S +++ b/arch/arm64/src/imx9/imx9_lowputc.h @@ -1,5 +1,5 @@ /**************************************************************************** - * arch/arm64/src/imx9/imx93_lowputs.S + * arch/arm64/src/imx9/imx9_lowputc.h * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with @@ -16,70 +16,69 @@ * License for the specific language governing permissions and limitations * under the License. * - **************************************************************************** - * - * DESCRIPTION - * Wrapper for early printk - * - ***************************************************************************/ + ****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_IMX9_IMX9_LOWPUTC_H +#define __ARCH_ARM_SRC_IMX9_IMX9_LOWPUTC_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ #include +#include -#include "arm64_macro.inc" +#include +#include +#include -#include "hardware/imx9_lpuart.h" -#include "hardware/imx93/imx93_memorymap.h" +#include "arm64_internal.h" /**************************************************************************** - * Public Symbols + * Public Types ****************************************************************************/ - .file "imx93_lowputc.S" +/* This structure describes the configuration of an UART */ -/**************************************************************************** - * Assembly Macros - ****************************************************************************/ +struct uart_config_s +{ + uint32_t baud; /* Configured baud */ + uint8_t parity; /* 0=none, 1=odd, 2=even */ + uint8_t bits; /* Number of bits (5-9) */ + bool stopbits2; /* true: Configure with 2 stop bits instead of 1 */ + bool userts; /* True: Assert RTS when there are data to be sent */ + bool invrts; /* True: Invert sense of RTS pin (true=active high) */ + bool usects; /* True: Condition transmission on CTS asserted */ + bool users485; /* True: Assert RTS while transmission progresses */ +}; /**************************************************************************** - * Private Functions + * Public Function Prototypes ****************************************************************************/ /**************************************************************************** - * Public Functions + * Name: imx9_lowsetup + * + * Description: + * Called at the very beginning of _start. Performs low level + * initialization including setup of the console UART. This UART done + * early so that the serial console is available for debugging very early + * in the boot sequence. + * ****************************************************************************/ -/* PL011 UART initialization */ - -GTEXT(arm64_earlyprintinit) -SECTION_FUNC(text, arm64_earlyprintinit) - /* TODO: Assumes u-boot has set us up, assumption is fine for now */ - ret +void imx9_lowsetup(void); -/* i.MX93 wait LPUART to be ready to transmit - * rb: register which contains the UART base address - * rc: scratch register - */ -.macro early_uart_ready rb, rc -1: - ldr \rc, [\rb, #UARTSTAT] /* <- Flag register */ - tst \rc, #UARTSTAT_TDRE /* Check FIFO EMPTY bit */ - beq 1b /* Wait for the UART to be ready */ -.endm +/**************************************************************************** + * Name: imx9_lpuart_configure + * + * Description: + * Configure a UART for non-interrupt driven operation + * + ****************************************************************************/ -/* i.MX93 LPUART transmit character - * rb: register which contains the UART base address - * rt: register which contains the character to transmit */ -.macro early_uart_transmit rb, rt - str \rt, [\rb, #UARTDATA] /* -> Data Register */ -.endm +int imx9_lpuart_configure(uint32_t base, + int uartnum, + const struct uart_config_s *config); -/* - * Print a character on the UART - this function is called by C - * w0: character to print - */ -GTEXT(arm64_lowputc) -SECTION_FUNC(text, arm64_lowputc) - ldr x15, =IMX9_LPUART1_BASE - early_uart_ready x15, w2 - early_uart_transmit x15, w0 - ret +#endif /* __ARCH_ARM_SRC_IMX9_IMX9_LOWPUTC_H */ diff --git a/arch/arm64/src/imx9/imx9_lpuart.c b/arch/arm64/src/imx9/imx9_lpuart.c index 2fa4307f4c259..6743d32ae3944 100644 --- a/arch/arm64/src/imx9/imx9_lpuart.c +++ b/arch/arm64/src/imx9/imx9_lpuart.c @@ -23,6 +23,7 @@ ****************************************************************************/ #include + #include #include #include @@ -33,838 +34,2663 @@ #include #ifdef CONFIG_SERIAL_TERMIOS -# include -#endif +# include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "arm64_internal.h" +#include "hardware/imx9_lpuart.h" +#include "hardware/imx9_pinmux.h" +#include "imx9_lowputc.h" +#include "imx9_serial.h" + +#if defined(SERIAL_HAVE_TXDMA) || defined(SERIAL_HAVE_RXDMA) +# include "chip.h" +# include "imx9_edma.h" +# include "hardware/imx9_dmamux.h" +#endif + +#ifdef USE_SERIALDRIVER + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* The DMA buffer size when using RX DMA to emulate a FIFO. + * + * When streaming data, the generic serial layer will be called every time + * the FIFO receives half this number of bytes. + * + * This buffer size should be an even multiple of the Cortex-A55 D-Cache line + * size, ARMV8M_DCACHE_LINESIZE, so that it can be individually invalidated. + */ + +# if !defined(ARMV8M_DCACHE_LINESIZE) || ARMV8M_DCACHE_LINESIZE == 0 +# undef ARMV8M_DCACHE_LINESIZE +# define ARMV8M_DCACHE_LINESIZE 64 +# endif + +# if !defined(CONFIG_IMX9_SERIAL_RXDMA_BUFFER_SIZE) || \ + (CONFIG_IMX9_SERIAL_RXDMA_BUFFER_SIZE < ARMV8M_DCACHE_LINESIZE) +# undef CONFIG_IMX9_SERIAL_RXDMA_BUFFER_SIZE +# define CONFIG_IMX9_SERIAL_RXDMA_BUFFER_SIZE ARMV8M_DCACHE_LINESIZE +# endif + +# define RXDMA_BUFFER_MASK (ARMV8M_DCACHE_LINESIZE - 1) +# define RXDMA_BUFFER_SIZE ((CONFIG_IMX9_SERIAL_RXDMA_BUFFER_SIZE \ + + RXDMA_BUFFER_MASK) & ~RXDMA_BUFFER_MASK) + +/* The DMA buffer size when using TX DMA. + * + * This TX buffer size should be an even multiple of the Cortex-A55 D-Cache + * line size, ARMV8M_DCACHE_LINESIZE, so that it can be individually + * invalidated. + */ + +#define TXDMA_BUFFER_MASK (ARMV8M_DCACHE_LINESIZE - 1) +#define TXDMA_BUFFER_SIZE ((CONFIG_IMX9_SERIAL_RXDMA_BUFFER_SIZE \ + + RXDMA_BUFFER_MASK) & ~RXDMA_BUFFER_MASK) + +/* Buffers need to be aligned and multiples of ARMV8M_DCACHE_LINESIZE */ + +#if defined(CONFIG_ARM64_DCACHE_DISABLE) +# define TXDMA_BUF_SIZE(b) (b) +# define TXDMA_BUF_ALIGN +#else +# define TXDMA_BUF_SIZE(b) (((b) + TXDMA_BUFFER_MASK) & ~TXDMA_BUFFER_MASK) +# define TXDMA_BUF_ALIGN aligned_data(ARMV8M_DCACHE_LINESIZE); +#endif + +#if !defined(CONFIG_LPUART1_TXDMA) +# define LPUART1_TXBUFSIZE_ADJUSTED CONFIG_LPUART1_TXBUFSIZE +# define LPUART1_TXBUFSIZE_ALGN +#else +# define LPUART1_TXBUFSIZE_ADJUSTED TXDMA_BUF_SIZE(CONFIG_LPUART1_TXBUFSIZE) +# define LPUART1_TXBUFSIZE_ALGN TXDMA_BUF_ALIGN +#endif + +#if !defined(CONFIG_LPUART2_TXDMA) +# define LPUART2_TXBUFSIZE_ADJUSTED CONFIG_LPUART2_TXBUFSIZE +# define LPUART2_TXBUFSIZE_ALGN +#else +# define LPUART2_TXBUFSIZE_ADJUSTED TXDMA_BUF_SIZE(CONFIG_LPUART2_TXBUFSIZE) +# define LPUART2_TXBUFSIZE_ALGN TXDMA_BUF_ALIGN +#endif + +#if !defined(CONFIG_LPUART3_TXDMA) +# define LPUART3_TXBUFSIZE_ADJUSTED CONFIG_LPUART3_TXBUFSIZE +# define LPUART3_TXBUFSIZE_ALGN +#else +# define LPUART3_TXBUFSIZE_ADJUSTED TXDMA_BUF_SIZE(CONFIG_LPUART3_TXBUFSIZE) +# define LPUART3_TXBUFSIZE_ALGN TXDMA_BUF_ALIGN +#endif + +#if !defined(CONFIG_LPUART4_TXDMA) +# define LPUART4_TXBUFSIZE_ADJUSTED CONFIG_LPUART4_TXBUFSIZE +# define LPUART4_TXBUFSIZE_ALGN +#else +# define LPUART4_TXBUFSIZE_ADJUSTED TXDMA_BUF_SIZE(CONFIG_LPUART4_TXBUFSIZE) +# define LPUART4_TXBUFSIZE_ALGN TXDMA_BUF_ALIGN +#endif + +#if !defined(CONFIG_LPUART5_TXDMA) +# define LPUART5_TXBUFSIZE_ADJUSTED CONFIG_LPUART5_TXBUFSIZE +# define LPUART5_TXBUFSIZE_ALGN +#else +# define LPUART5_TXBUFSIZE_ADJUSTED TXDMA_BUF_SIZE(CONFIG_LPUART5_TXBUFSIZE) +# define LPUART5_TXBUFSIZE_ALGN TXDMA_BUF_ALIGN +#endif + +#if !defined(CONFIG_LPUART6_TXDMA) +# define LPUART6_TXBUFSIZE_ADJUSTED CONFIG_LPUART6_TXBUFSIZE +# define LPUART6_TXBUFSIZE_ALGN +#else +# define LPUART6_TXBUFSIZE_ADJUSTED TXDMA_BUF_SIZE(CONFIG_LPUART6_TXBUFSIZE) +# define LPUART6_TXBUFSIZE_ALGN TXDMA_BUF_ALIGN +#endif + +#if !defined(CONFIG_LPUART7_TXDMA) +# define LPUART7_TXBUFSIZE_ADJUSTED CONFIG_LPUART7_TXBUFSIZE +# define LPUART7_TXBUFSIZE_ALGN +#else +# define LPUART7_TXBUFSIZE_ADJUSTED TXDMA_BUF_SIZE(CONFIG_LPUART7_TXBUFSIZE) +# define LPUART7_TXBUFSIZE_ALGN TXDMA_BUF_ALIGN +#endif + +#if !defined(CONFIG_LPUART8_TXDMA) +# define LPUART8_TXBUFSIZE_ADJUSTED CONFIG_LPUART8_TXBUFSIZE +# define LPUART8_TXBUFSIZE_ALGN +#else +# define LPUART8_TXBUFSIZE_ADJUSTED TXDMA_BUF_SIZE(CONFIG_LPUART8_TXBUFSIZE) +# define LPUART8_TXBUFSIZE_ALGN TXDMA_BUF_ALIGN +#endif + +/* Which LPUART with be console? */ + +#if defined(CONFIG_LPUART1_SERIAL_CONSOLE) +# define CONSOLE_DEV g_lpuart1priv /* LPUART1 is console */ +# if defined(CONFIG_LPUART1_RXDMA) +# define SERIAL_HAVE_CONSOLE_RXDMA 1 +# endif +# if defined(CONFIG_LPUART1_TXDMA) +# define SERIAL_HAVE_CONSOLE_TXDMA 1 +# endif +#elif defined(CONFIG_LPUART2_SERIAL_CONSOLE) +# define CONSOLE_DEV g_lpuart2priv /* LPUART2 is console */ +# if defined(CONFIG_LPUART2_RXDMA) +# define SERIAL_HAVE_CONSOLE_RXDMA 1 +# endif +# if defined(CONFIG_LPUART2_TXDMA) +# define SERIAL_HAVE_CONSOLE_TXDMA 1 +# endif +#elif defined(CONFIG_LPUART3_SERIAL_CONSOLE) +# define CONSOLE_DEV g_lpuart3priv /* LPUART3 is console */ +# if defined(CONFIG_LPUART3_RXDMA) +# define SERIAL_HAVE_CONSOLE_RXDMA 1 +# endif +# if defined(CONFIG_LPUART3_TXDMA) +# define SERIAL_HAVE_CONSOLE_TXDMA 1 +# endif +#elif defined(CONFIG_LPUART4_SERIAL_CONSOLE) +# define CONSOLE_DEV g_lpuart4priv /* LPUART4 is console */ +# if defined(CONFIG_LPUART4_RXDMA) +# define SERIAL_HAVE_CONSOLE_RXDMA 1 +# endif +# if defined(CONFIG_LPUART4_TXDMA) +# define SERIAL_HAVE_CONSOLE_TXDMA 1 +# endif +#elif defined(CONFIG_LPUART5_SERIAL_CONSOLE) +# define CONSOLE_DEV g_lpuart5priv /* LPUART5 is console */ +# if defined(CONFIG_LPUART5_RXDMA) +# define SERIAL_HAVE_CONSOLE_RXDMA 1 +# endif +# if defined(CONFIG_LPUART5_TXDMA) +# define SERIAL_HAVE_CONSOLE_TXDMA 1 +# endif +#elif defined(CONFIG_LPUART6_SERIAL_CONSOLE) +# define CONSOLE_DEV g_lpuart6priv /* LPUART6 is console */ +# if defined(CONFIG_LPUART6_RXDMA) +# define SERIAL_HAVE_CONSOLE_RXDMA 1 +# endif +# if defined(CONFIG_LPUART6_TXDMA) +# define SERIAL_HAVE_CONSOLE_TXDMA 1 +# endif +#elif defined(CONFIG_LPUART7_SERIAL_CONSOLE) +# define CONSOLE_DEV g_lpuart7priv /* LPUART7 is console */ +# if defined(CONFIG_LPUART7_RXDMA) +# define SERIAL_HAVE_CONSOLE_RXDMA 1 +# endif +# if defined(CONFIG_LPUART7_TXDMA) +# define SERIAL_HAVE_CONSOLE_TXDMA 1 +# endif +#elif defined(CONFIG_LPUART8_SERIAL_CONSOLE) +# define CONSOLE_DEV g_lpuart8priv /* LPUART8 is console */ +# if defined(CONFIG_LPUART8_RXDMA) +# define SERIAL_HAVE_CONSOLE_RXDMA 1 +# endif +# if defined(CONFIG_LPUART8_TXDMA) +# define SERIAL_HAVE_CONSOLE_TXDMA 1 +# endif +#endif + +#if defined(SERIAL_HAVE_CONSOLE_RXDMA) || defined(SERIAL_HAVE_CONSOLE_TXDMA) +# define SERIAL_HAVE_CONSOLE_DMA +#endif + +#ifdef CONFIG_IMX9_LPUART1 +#define TTYS0_DEV g_lpuart1priv /* LPUART1 is ttyS0 */ +#endif + +#ifdef CONFIG_IMX9_LPUART2 +#define TTYS1_DEV g_lpuart2priv /* LPUART2 is ttyS1 */ +#endif + +#ifdef CONFIG_IMX9_LPUART3 +#define TTYS2_DEV g_lpuart3priv /* LPUART3 is ttyS2 */ +#endif + +#ifdef CONFIG_IMX9_LPUART4 +#define TTYS3_DEV g_lpuart4priv /* LPUART4 is ttyS3 */ +#endif + +#ifdef CONFIG_IMX9_LPUART5 +#define TTYS4_DEV g_lpuart5priv /* LPUART5 is ttyS4 */ +#endif + +#ifdef CONFIG_IMX9_LPUART6 +#define TTYS5_DEV g_lpuart6priv /* LPUART6 is ttyS5 */ +#endif + +#ifdef CONFIG_IMX9_LPUART7 +#define TTYS6_DEV g_lpuart7priv /* LPUART7 is ttyS6 */ +#endif + +#ifdef CONFIG_IMX9_LPUART8 +#define TTYS7_DEV g_lpuart8priv /* LPUART8 is ttyS7 */ +#endif + +/* Power management definitions */ + +#if defined(CONFIG_PM) && !defined(CONFIG_IMX9_PM_SERIAL_ACTIVITY) +# define CONFIG_IMX9_PM_SERIAL_ACTIVITY 10 +#endif + +#if defined(CONFIG_PM) +# define PM_IDLE_DOMAIN 0 /* Revisit */ +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct imx9_uart_s +{ + struct uart_dev_s dev; /* Generic UART device */ + const uint32_t uartbase; /* Base address of UART registers */ + const int uartnum; /* LPUART number 1-8 */ + uint32_t baud; /* Configured baud */ + uint32_t ie; /* Saved enabled interrupts */ + uint8_t irq; /* IRQ associated with this UART */ + uint8_t parity; /* 0=none, 1=odd, 2=even */ + uint8_t bits; /* Number of bits (7 or 8) */ +#if defined(CONFIG_SERIAL_RS485CONTROL) || defined(CONFIG_SERIAL_IFLOWCONTROL) + uint8_t inviflow:1; /* Invert RTS sense */ + const uint32_t rts_gpio; /* LPUART RTS GPIO pin configuration */ +#endif +#ifdef CONFIG_SERIAL_OFLOWCONTROL + const uint32_t cts_gpio; /* LPUART CTS GPIO pin configuration */ +#endif + uint8_t stopbits2:1; /* 1: Configure with 2 stop bits vs 1 */ +#ifdef CONFIG_SERIAL_IFLOWCONTROL + uint8_t iflow:1; /* input flow control (RTS) enabled */ +#endif +#ifdef CONFIG_SERIAL_OFLOWCONTROL + uint8_t oflow:1; /* output flow control (CTS) enabled */ +#endif +#ifdef CONFIG_SERIAL_RS485CONTROL + uint8_t rs485mode:1; /* We are in RS485 (RTS on TX) mode */ +#endif + /* TX DMA state */ + +#ifdef SERIAL_HAVE_TXDMA + const unsigned int txch; /* DMAMUX source of TX DMA request */ + DMACH_HANDLE txdma; /* currently-open transmit DMA stream */ +#endif + + /* RX DMA state */ + +#ifdef SERIAL_HAVE_RXDMA + const unsigned int rxch; /* DMAMUX source of RX DMA request */ + DMACH_HANDLE rxdma; /* currently-open receive DMA stream */ + bool rxenable; /* DMA-based reception en/disable */ + uint32_t rxdmanext; /* Next byte in the DMA buffer to be read */ +#ifndef CONFIG_ARM64_DCACHE_DISABLE + uint32_t rxdmaavail; /* Number of bytes available without need to + * to invalidate the data cache */ +#endif + char *const rxfifo; /* Receive DMA buffer */ +#endif +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static inline uint32_t imx9_serialin(struct imx9_uart_s *priv, + uint32_t offset); +static inline void imx9_serialout(struct imx9_uart_s *priv, + uint32_t offset, uint32_t value); +static inline void imx9_disableuartint(struct imx9_uart_s *priv, + uint32_t *ie); +static inline void imx9_restoreuartint(struct imx9_uart_s *priv, + uint32_t ie); + +static int imx9_setup(struct uart_dev_s *dev); +static void imx9_shutdown(struct uart_dev_s *dev); +static int imx9_attach(struct uart_dev_s *dev); +static void imx9_detach(struct uart_dev_s *dev); +static int imx9_interrupt(int irq, void *context, void *arg); +static int imx9_ioctl(struct file *filep, int cmd, unsigned long arg); +#if !defined(SERIAL_HAVE_ONLY_RXDMA) +static int imx9_receive(struct uart_dev_s *dev, unsigned int *status); +static void imx9_rxint(struct uart_dev_s *dev, bool enable); +static bool imx9_rxavailable(struct uart_dev_s *dev); +#endif +#if !defined(SERIAL_HAVE_ONLY_TXDMA) +static void imx9_txint(struct uart_dev_s *dev, bool enable); +#endif + +#ifdef CONFIG_SERIAL_IFLOWCONTROL +static bool imx9_rxflowcontrol(struct uart_dev_s *dev, + unsigned int nbuffered, bool upper); +#endif +static void imx9_send(struct uart_dev_s *dev, int ch); + +static bool imx9_txready(struct uart_dev_s *dev); + +#ifdef SERIAL_HAVE_TXDMA +static void imx9_dma_send(struct uart_dev_s *dev); +static void imx9_dma_txint(struct uart_dev_s *dev, bool enable); +static void imx9_dma_txavailable(struct uart_dev_s *dev); +static void imx9_dma_txcallback(DMACH_HANDLE handle, void *arg, bool done, + int result); +#endif + +#if defined(SERIAL_HAVE_RXDMA) || defined(SERIAL_HAVE_TXDMA) +static int imx9_dma_setup(struct uart_dev_s *dev); +static void imx9_dma_shutdown(struct uart_dev_s *dev); +#endif + +#ifdef SERIAL_HAVE_RXDMA +static int imx9_dma_receive(struct uart_dev_s *dev, + unsigned int *status); +#ifdef CONFIG_PM +static void imx9_dma_reenable(struct imx9_uart_s *priv); +#endif +static void imx9_dma_rxint(struct uart_dev_s *dev, bool enable); +static bool imx9_dma_rxavailable(struct uart_dev_s *dev); + +static void imx9_dma_rxcallback(DMACH_HANDLE handle, void *arg, bool done, + int result); +#endif + +static bool imx9_txempty(struct uart_dev_s *dev); + +#ifdef CONFIG_PM +static void up_pm_notify(struct pm_callback_s *cb, int dowmin, + enum pm_state_e pmstate); +static int up_pm_prepare(struct pm_callback_s *cb, int domain, + enum pm_state_e pmstate); +#endif + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Serial driver UART operations */ + +#if !defined(SERIAL_HAVE_ONLY_TXDMA) && !defined(SERIAL_HAVE_ONLY_RXDMA) +static const struct uart_ops_s g_lpuart_ops = +{ + .setup = imx9_setup, + .shutdown = imx9_shutdown, + .attach = imx9_attach, + .detach = imx9_detach, + .ioctl = imx9_ioctl, + .receive = imx9_receive, + .rxint = imx9_rxint, + .rxavailable = imx9_rxavailable, +#ifdef CONFIG_SERIAL_IFLOWCONTROL + .rxflowcontrol = imx9_rxflowcontrol, +#endif + .send = imx9_send, + .txint = imx9_txint, + .txready = imx9_txready, + .txempty = imx9_txempty, +}; +#endif + +#if defined(SERIAL_HAVE_RXDMA) && defined(SERIAL_HAVE_TXDMA) +static const struct uart_ops_s g_lpuart_rxtxdma_ops = +{ + .setup = imx9_dma_setup, + .shutdown = imx9_dma_shutdown, + .attach = imx9_attach, + .detach = imx9_detach, + .ioctl = imx9_ioctl, + .receive = imx9_dma_receive, + .rxint = imx9_dma_rxint, + .rxavailable = imx9_dma_rxavailable, +#ifdef CONFIG_SERIAL_IFLOWCONTROL + .rxflowcontrol = imx9_rxflowcontrol, +#endif + .send = imx9_send, + .txint = imx9_dma_txint, + .txready = imx9_txready, + .txempty = imx9_txempty, + .dmatxavail = imx9_dma_txavailable, + .dmasend = imx9_dma_send, +}; +#endif + +#if !defined(SERIAL_HAVE_ONLY_DMA) && defined(SERIAL_HAVE_RXDMA) +static const struct uart_ops_s g_lpuart_rxdma_ops = +{ + .setup = imx9_dma_setup, + .shutdown = imx9_dma_shutdown, + .attach = imx9_attach, + .detach = imx9_detach, + .ioctl = imx9_ioctl, + .receive = imx9_dma_receive, + .rxint = imx9_dma_rxint, + .rxavailable = imx9_dma_rxavailable, +#ifdef CONFIG_SERIAL_IFLOWCONTROL + .rxflowcontrol = imx9_rxflowcontrol, +#endif + .send = imx9_send, + .txint = imx9_txint, + .txready = imx9_txready, + .txempty = imx9_txempty, +}; +#endif + +#if !defined(SERIAL_HAVE_ONLY_DMA) && defined(SERIAL_HAVE_TXDMA) +static const struct uart_ops_s g_lpuart_txdma_ops = +{ + .setup = imx9_dma_setup, + .shutdown = imx9_dma_shutdown, + .attach = imx9_attach, + .detach = imx9_detach, + .ioctl = imx9_ioctl, + .receive = imx9_receive, + .rxint = imx9_rxint, + .rxavailable = imx9_rxavailable, + #ifdef CONFIG_SERIAL_IFLOWCONTROL + .rxflowcontrol = imx9_rxflowcontrol, + #endif + .send = imx9_send, + .txint = imx9_dma_txint, + .txready = imx9_txready, + .txempty = imx9_txempty, + .dmatxavail = imx9_dma_txavailable, + .dmasend = imx9_dma_send, +}; +#endif + +/* Avoid unused warning */ +#if !defined(SERIAL_HAVE_ONLY_DMA) && defined(SERIAL_HAVE_RXDMA) +const struct uart_ops_s *g_o0 = &g_lpuart_rxdma_ops; +#endif +#if !defined(SERIAL_HAVE_ONLY_DMA) && defined(SERIAL_HAVE_TXDMA) +const struct uart_ops_s *g_o1 = &g_lpuart_txdma_ops; +#endif + +/* I/O buffers */ + +#ifdef CONFIG_LPUART1_RXDMA +static char g_lpuart1rxfifo[RXDMA_BUFFER_SIZE] + aligned_data(ARMV8M_DCACHE_LINESIZE); +#endif + +# ifdef CONFIG_LPUART2_RXDMA +static char g_lpuart2rxfifo[RXDMA_BUFFER_SIZE] + aligned_data(ARMV8M_DCACHE_LINESIZE); +#endif + +#ifdef CONFIG_LPUART3_RXDMA +static char g_lpuart3rxfifo[RXDMA_BUFFER_SIZE] + aligned_data(ARMV8M_DCACHE_LINESIZE); +#endif + +#ifdef CONFIG_LPUART4_RXDMA +static char g_lpuart4rxfifo[RXDMA_BUFFER_SIZE] + aligned_data(ARMV8M_DCACHE_LINESIZE); +#endif + +#ifdef CONFIG_LPUART5_RXDMA +static char g_lpuart5rxfifo[RXDMA_BUFFER_SIZE] + aligned_data(ARMV8M_DCACHE_LINESIZE); +#endif + +#ifdef CONFIG_LPUART6_RXDMA +static char g_lpuart6rxfifo[RXDMA_BUFFER_SIZE] + aligned_data(ARMV8M_DCACHE_LINESIZE); +#endif + +#ifdef CONFIG_LPUART7_RXDMA +static char g_lpuart7rxfifo[RXDMA_BUFFER_SIZE] + aligned_data(ARMV8M_DCACHE_LINESIZE); +#endif + +#ifdef CONFIG_LPUART8_RXDMA +static char g_lpuart8rxfifo[RXDMA_BUFFER_SIZE] + aligned_data(ARMV8M_DCACHE_LINESIZE); +#endif + +#ifdef CONFIG_IMX9_LPUART1 +static char g_lpuart1rxbuffer[CONFIG_LPUART1_RXBUFSIZE]; +static char g_lpuart1txbuffer[LPUART1_TXBUFSIZE_ADJUSTED] + LPUART1_TXBUFSIZE_ALGN; +#endif + +#ifdef CONFIG_IMX9_LPUART2 +static char g_lpuart2rxbuffer[CONFIG_LPUART2_RXBUFSIZE]; +static char g_lpuart2txbuffer[LPUART2_TXBUFSIZE_ADJUSTED] + LPUART2_TXBUFSIZE_ALGN; +#endif + +#ifdef CONFIG_IMX9_LPUART3 +static char g_lpuart3rxbuffer[CONFIG_LPUART3_RXBUFSIZE]; +static char g_lpuart3txbuffer[LPUART3_TXBUFSIZE_ADJUSTED] + LPUART3_TXBUFSIZE_ALGN; +#endif + +#ifdef CONFIG_IMX9_LPUART4 +static char g_lpuart4rxbuffer[CONFIG_LPUART4_RXBUFSIZE]; +static char g_lpuart4txbuffer[LPUART4_TXBUFSIZE_ADJUSTED] + LPUART4_TXBUFSIZE_ALGN; +#endif + +#ifdef CONFIG_IMX9_LPUART5 +static char g_lpuart5rxbuffer[CONFIG_LPUART5_RXBUFSIZE]; +static char g_lpuart5txbuffer[LPUART5_TXBUFSIZE_ADJUSTED] + LPUART5_TXBUFSIZE_ALGN; +#endif + +#ifdef CONFIG_IMX9_LPUART6 +static char g_lpuart6rxbuffer[CONFIG_LPUART6_RXBUFSIZE]; +static char g_lpuart6txbuffer[LPUART6_TXBUFSIZE_ADJUSTED] + LPUART6_TXBUFSIZE_ALGN; +#endif + +#ifdef CONFIG_IMX9_LPUART7 +static char g_lpuart7rxbuffer[CONFIG_LPUART7_RXBUFSIZE]; +static char g_lpuart7txbuffer[LPUART7_TXBUFSIZE_ADJUSTED] + LPUART7_TXBUFSIZE_ALGN; +#endif + +#ifdef CONFIG_IMX9_LPUART8 +static char g_lpuart8rxbuffer[CONFIG_LPUART8_RXBUFSIZE]; +static char g_lpuart8txbuffer[LPUART8_TXBUFSIZE_ADJUSTED] \ + LPUART8_TXBUFSIZE_ALGN; +#endif + +#ifdef CONFIG_IMX9_LPUART1 +static struct imx9_uart_s g_lpuart1priv = +{ + .dev = + { + .recv = + { + .size = CONFIG_LPUART1_RXBUFSIZE, + .buffer = g_lpuart1rxbuffer, + }, + .xmit = + { + .size = CONFIG_LPUART1_TXBUFSIZE, + .buffer = g_lpuart1txbuffer, + }, +# if defined(CONFIG_LPUART1_RXDMA) && defined(CONFIG_LPUART1_TXDMA) + .ops = &g_lpuart_rxtxdma_ops, +# elif defined(CONFIG_LPUART1_RXDMA) && !defined(CONFIG_LPUART1_TXDMA) + .ops = &g_lpuart_rxdma_ops, +# elif !defined(CONFIG_LPUART1_RXDMA) && defined(CONFIG_LPUART1_TXDMA) + .ops = &g_lpuart_txdma_ops, +# else + .ops = &g_lpuart_ops, +# endif + }, + + .uartbase = IMX9_LPUART1_BASE, + .uartnum = 1, + .baud = CONFIG_LPUART1_BAUD, + .irq = IMX9_IRQ_LPUART1, + .parity = CONFIG_LPUART1_PARITY, + .bits = CONFIG_LPUART1_BITS, + .stopbits2 = CONFIG_LPUART1_2STOP, +# if defined(CONFIG_SERIAL_OFLOWCONTROL) && defined(CONFIG_LPUART1_OFLOWCONTROL) + .oflow = 1, + .cts_gpio = GPIO_LPUART1_CTS, +# endif +# if defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART1_IFLOWCONTROL) + .iflow = 1, +# endif +# if ((defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART1_RS485RTSCONTROL)) || \ + (defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART1_IFLOWCONTROL))) + .rts_gpio = GPIO_LPUART1_RTS, +# endif + +# if (defined(CONFIG_SERIAL_RS485CONTROL) || defined(CONFIG_SERIAL_IFLOWCONTROL)) && \ + defined(CONFIG_LPUART1_INVERTIFLOWCONTROL) + .inviflow = 1, +# endif + +# if defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART1_RS485RTSCONTROL) + .rs485mode = 1, +# endif + +# ifdef CONFIG_LPUART1_TXDMA + .txch = DMA_REQUEST_MUXLPUART1TX, +# endif +# ifdef CONFIG_LPUART1_RXDMA + .rxch = DMA_REQUEST_MUXLPUART1RX, + .rxfifo = g_lpuart1rxfifo, +# endif +}; +#endif + +#ifdef CONFIG_IMX9_LPUART2 +static struct imx9_uart_s g_lpuart2priv = +{ + .dev = + { + .recv = + { + .size = CONFIG_LPUART2_RXBUFSIZE, + .buffer = g_lpuart2rxbuffer, + }, + .xmit = + { + .size = CONFIG_LPUART2_TXBUFSIZE, + .buffer = g_lpuart2txbuffer, + }, +# if defined(CONFIG_LPUART2_RXDMA) && defined(CONFIG_LPUART2_TXDMA) + .ops = &g_lpuart_rxtxdma_ops, +# elif defined(CONFIG_LPUART2_RXDMA) && !defined(CONFIG_LPUART2_TXDMA) + .ops = &g_lpuart_rxdma_ops, +# elif !defined(CONFIG_LPUART2_RXDMA) && defined(CONFIG_LPUART2_TXDMA) + .ops = &g_lpuart_txdma_ops, +# else + .ops = &g_lpuart_ops, +# endif + }, + + .uartbase = IMX9_LPUART2_BASE, + .uartnum = 2, + .baud = CONFIG_LPUART2_BAUD, + .irq = IMX9_IRQ_LPUART2, + .parity = CONFIG_LPUART2_PARITY, + .bits = CONFIG_LPUART2_BITS, + .stopbits2 = CONFIG_LPUART2_2STOP, +# if defined(CONFIG_SERIAL_OFLOWCONTROL) && defined(CONFIG_LPUART2_OFLOWCONTROL) + .oflow = 1, + .cts_gpio = GPIO_LPUART2_CTS, +# endif +# if defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART2_IFLOWCONTROL) + .iflow = 1, +# endif +# if ((defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART2_RS485RTSCONTROL)) || \ + (defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART2_IFLOWCONTROL))) + .rts_gpio = GPIO_LPUART2_RTS, +# endif + +# if (defined(CONFIG_SERIAL_RS485CONTROL) || defined(CONFIG_SERIAL_IFLOWCONTROL)) && \ + defined(CONFIG_LPUART2_INVERTIFLOWCONTROL) + .inviflow = 1, +# endif + +# if defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART2_RS485RTSCONTROL) + .rs485mode = 1, +# endif + +# ifdef CONFIG_LPUART2_TXDMA + .txch = DMA_REQUEST_MUXLPUART2TX, +# endif +# ifdef CONFIG_LPUART2_RXDMA + .rxch = DMA_REQUEST_MUXLPUART2RX, + .rxfifo = g_lpuart2rxfifo, +# endif +}; +#endif + +#ifdef CONFIG_IMX9_LPUART3 +static struct imx9_uart_s g_lpuart3priv = +{ + .dev = + { + .recv = + { + .size = CONFIG_LPUART3_RXBUFSIZE, + .buffer = g_lpuart3rxbuffer, + }, + .xmit = + { + .size = CONFIG_LPUART3_TXBUFSIZE, + .buffer = g_lpuart3txbuffer, + }, +# if defined(CONFIG_LPUART3_RXDMA) && defined(CONFIG_LPUART3_TXDMA) + .ops = &g_lpuart_rxtxdma_ops, +# elif defined(CONFIG_LPUART3_RXDMA) && !defined(CONFIG_LPUART3_TXDMA) + .ops = &g_lpuart_rxdma_ops, +# elif !defined(CONFIG_LPUART3_RXDMA) && defined(CONFIG_LPUART3_TXDMA) + .ops = &g_lpuart_txdma_ops, +# else + .ops = &g_lpuart_ops, +# endif + }, + + .uartbase = IMX9_LPUART3_BASE, + .uartnum = 3, + .baud = CONFIG_LPUART3_BAUD, + .irq = IMX9_IRQ_LPUART3, + .parity = CONFIG_LPUART3_PARITY, + .bits = CONFIG_LPUART3_BITS, + .stopbits2 = CONFIG_LPUART3_2STOP, +# if defined(CONFIG_SERIAL_OFLOWCONTROL) && defined(CONFIG_LPUART3_OFLOWCONTROL) + .oflow = 1, + .cts_gpio = GPIO_LPUART3_CTS, +# endif +# if defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART3_IFLOWCONTROL) + .iflow = 1, +# endif +# if ((defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART3_RS485RTSCONTROL)) || \ + (defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART3_IFLOWCONTROL))) + .rts_gpio = GPIO_LPUART3_RTS, +# endif + +# if (defined(CONFIG_SERIAL_RS485CONTROL) || defined(CONFIG_SERIAL_IFLOWCONTROL)) && \ + defined(CONFIG_LPUART3_INVERTIFLOWCONTROL) + .inviflow = 1, +# endif + +# if defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART3_RS485RTSCONTROL) + .rs485mode = 1, +# endif + +# ifdef CONFIG_LPUART3_TXDMA + .txch = DMA_REQUEST_MUXLPUART3TX, +# endif +# ifdef CONFIG_LPUART3_RXDMA + .rxch = DMA_REQUEST_MUXLPUART3RX, + .rxfifo = g_lpuart3rxfifo, +# endif +}; +#endif + +#ifdef CONFIG_IMX9_LPUART4 +static struct imx9_uart_s g_lpuart4priv = +{ + .dev = + { + .recv = + { + .size = CONFIG_LPUART4_RXBUFSIZE, + .buffer = g_lpuart4rxbuffer, + }, + .xmit = + { + .size = CONFIG_LPUART4_TXBUFSIZE, + .buffer = g_lpuart4txbuffer, + }, +# if defined(CONFIG_LPUART4_RXDMA) && defined(CONFIG_LPUART4_TXDMA) + .ops = &g_lpuart_rxtxdma_ops, +# elif defined(CONFIG_LPUART4_RXDMA) && !defined(CONFIG_LPUART4_TXDMA) + .ops = &g_lpuart_rxdma_ops, +# elif !defined(CONFIG_LPUART4_RXDMA) && defined(CONFIG_LPUART4_TXDMA) + .ops = &g_lpuart_txdma_ops, +# else + .ops = &g_lpuart_ops, +# endif + }, + + .uartbase = IMX9_LPUART4_BASE, + .uartnum = 4, + .baud = CONFIG_LPUART4_BAUD, + .irq = IMX9_IRQ_LPUART4, + .parity = CONFIG_LPUART4_PARITY, + .bits = CONFIG_LPUART4_BITS, + .stopbits2 = CONFIG_LPUART4_2STOP, +# if defined(CONFIG_SERIAL_OFLOWCONTROL) && defined(CONFIG_LPUART4_OFLOWCONTROL) + .oflow = 1, + .cts_gpio = GPIO_LPUART4_CTS, +# endif +# if defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART4_IFLOWCONTROL) + .iflow = 1, +# endif +# if ((defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART4_RS485RTSCONTROL)) || \ + (defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART4_IFLOWCONTROL))) + .rts_gpio = GPIO_LPUART4_RTS, +# endif + +# if (defined(CONFIG_SERIAL_RS485CONTROL) || defined(CONFIG_SERIAL_IFLOWCONTROL)) && \ + defined(CONFIG_LPUART4_INVERTIFLOWCONTROL) + .inviflow = 1, +# endif + +# if defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART4_RS485RTSCONTROL) + .rs485mode = 1, +# endif + +# ifdef CONFIG_LPUART4_TXDMA + .txch = DMA_REQUEST_MUXLPUART4TX, +# endif +# ifdef CONFIG_LPUART4_RXDMA + .rxch = DMA_REQUEST_MUXLPUART4RX, + .rxfifo = g_lpuart4rxfifo, +# endif +}; +#endif + +#ifdef CONFIG_IMX9_LPUART5 +static struct imx9_uart_s g_lpuart5priv = +{ + .dev = + { + .recv = + { + .size = CONFIG_LPUART5_RXBUFSIZE, + .buffer = g_lpuart5rxbuffer, + }, + .xmit = + { + .size = CONFIG_LPUART5_TXBUFSIZE, + .buffer = g_lpuart5txbuffer, + }, +# if defined(CONFIG_LPUART5_RXDMA) && defined(CONFIG_LPUART5_TXDMA) + .ops = &g_lpuart_rxtxdma_ops, +# elif defined(CONFIG_LPUART5_RXDMA) && !defined(CONFIG_LPUART5_TXDMA) + .ops = &g_lpuart_rxdma_ops, +# elif !defined(CONFIG_LPUART5_RXDMA) && defined(CONFIG_LPUART5_TXDMA) + .ops = &g_lpuart_txdma_ops, +# else + .ops = &g_lpuart_ops, +# endif + }, + + .uartbase = IMX9_LPUART5_BASE, + .uartnum = 5, + .baud = CONFIG_LPUART5_BAUD, + .irq = IMX9_IRQ_LPUART5, + .parity = CONFIG_LPUART5_PARITY, + .bits = CONFIG_LPUART5_BITS, + .stopbits2 = CONFIG_LPUART5_2STOP, +# if defined(CONFIG_SERIAL_OFLOWCONTROL) && defined(CONFIG_LPUART5_OFLOWCONTROL) + .oflow = 1, + .cts_gpio = GPIO_LPUART5_CTS, +# endif +# if defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART5_IFLOWCONTROL) + .iflow = 1, +# endif +# if ((defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART5_RS485RTSCONTROL)) || \ + (defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART5_IFLOWCONTROL))) + .rts_gpio = GPIO_LPUART5_RTS, +# endif + +# if (defined(CONFIG_SERIAL_RS485CONTROL) || defined(CONFIG_SERIAL_IFLOWCONTROL)) && \ + defined(CONFIG_LPUART5_INVERTIFLOWCONTROL) + .inviflow = 1, +# endif + +# if defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART5_RS485RTSCONTROL) + .rs485mode = 1, +# endif + +# ifdef CONFIG_LPUART5_TXDMA + .txch = DMA_REQUEST_MUXLPUART5TX, +# endif +# ifdef CONFIG_LPUART5_RXDMA + .rxch = DMA_REQUEST_MUXLPUART5RX, + .rxfifo = g_lpuart5rxfifo, +# endif +}; +#endif + +#ifdef CONFIG_IMX9_LPUART6 +static struct imx9_uart_s g_lpuart6priv = +{ + .dev = + { + .recv = + { + .size = CONFIG_LPUART6_RXBUFSIZE, + .buffer = g_lpuart6rxbuffer, + }, + .xmit = + { + .size = CONFIG_LPUART6_TXBUFSIZE, + .buffer = g_lpuart6txbuffer, + }, +# if defined(CONFIG_LPUART6_RXDMA) && defined(CONFIG_LPUART6_TXDMA) + .ops = &g_lpuart_rxtxdma_ops, +# elif defined(CONFIG_LPUART6_RXDMA) && !defined(CONFIG_LPUART6_TXDMA) + .ops = &g_lpuart_rxdma_ops, +# elif !defined(CONFIG_LPUART6_RXDMA) && defined(CONFIG_LPUART6_TXDMA) + .ops = &g_lpuart_txdma_ops, +# else + .ops = &g_lpuart_ops, +# endif + }, + + .uartbase = IMX9_LPUART6_BASE, + .uartnum = 6, + .baud = CONFIG_LPUART6_BAUD, + .irq = IMX9_IRQ_LPUART6, + .parity = CONFIG_LPUART6_PARITY, + .bits = CONFIG_LPUART6_BITS, + .stopbits2 = CONFIG_LPUART6_2STOP, +# if defined(CONFIG_SERIAL_OFLOWCONTROL) && defined(CONFIG_LPUART6_OFLOWCONTROL) + .oflow = 1, + .cts_gpio = GPIO_LPUART6_CTS, +# endif +# if defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART6_IFLOWCONTROL) + .iflow = 1, +# endif +# if ((defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART6_RS485RTSCONTROL)) || \ + (defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART6_IFLOWCONTROL))) + .rts_gpio = GPIO_LPUART6_RTS, +# endif + +# if (defined(CONFIG_SERIAL_RS485CONTROL) || defined(CONFIG_SERIAL_IFLOWCONTROL)) && \ + defined(CONFIG_LPUART6_INVERTIFLOWCONTROL) + .inviflow = 1, +# endif + +# if defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART6_RS485RTSCONTROL) + .rs485mode = 1, +# endif + +# ifdef CONFIG_LPUART6_TXDMA + .txch = DMA_REQUEST_MUXLPUART6TX, +# endif +# ifdef CONFIG_LPUART6_RXDMA + .rxch = DMA_REQUEST_MUXLPUART6RX, + .rxfifo = g_lpuart6rxfifo, +# endif +}; +#endif + +#ifdef CONFIG_IMX9_LPUART7 +static struct imx9_uart_s g_lpuart7priv = +{ + .dev = + { + .recv = + { + .size = CONFIG_LPUART7_RXBUFSIZE, + .buffer = g_lpuart7rxbuffer, + }, + .xmit = + { + .size = CONFIG_LPUART7_TXBUFSIZE, + .buffer = g_lpuart7txbuffer, + }, +# if defined(CONFIG_LPUART7_RXDMA) && defined(CONFIG_LPUART7_TXDMA) + .ops = &g_lpuart_rxtxdma_ops, +# elif defined(CONFIG_LPUART7_RXDMA) && !defined(CONFIG_LPUART7_TXDMA) + .ops = &g_lpuart_rxdma_ops, +# elif !defined(CONFIG_LPUART7_RXDMA) && defined(CONFIG_LPUART7_TXDMA) + .ops = &g_lpuart_txdma_ops, +# else + .ops = &g_lpuart_ops, +# endif + }, + + .uartbase = IMX9_LPUART7_BASE, + .uartnum = 7, + .baud = CONFIG_LPUART7_BAUD, + .irq = IMX9_IRQ_LPUART7, + .parity = CONFIG_LPUART7_PARITY, + .bits = CONFIG_LPUART7_BITS, + .stopbits2 = CONFIG_LPUART7_2STOP, +# if defined(CONFIG_SERIAL_OFLOWCONTROL) && defined(CONFIG_LPUART7_OFLOWCONTROL) + .oflow = 1, + .cts_gpio = GPIO_LPUART7_CTS, +# endif +# if defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART7_IFLOWCONTROL) + .iflow = 1, +# endif +# if ((defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART7_RS485RTSCONTROL)) || \ + (defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART7_IFLOWCONTROL))) + .rts_gpio = GPIO_LPUART7_RTS, +# endif + +# if (defined(CONFIG_SERIAL_RS485CONTROL) || defined(CONFIG_SERIAL_IFLOWCONTROL)) && \ + defined(CONFIG_LPUART7_INVERTIFLOWCONTROL) + .inviflow = 1, +# endif + +# if defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART7_RS485RTSCONTROL) + .rs485mode = 1, +# endif + +# ifdef CONFIG_LPUART7_TXDMA + .txch = DMA_REQUEST_MUXLPUART7TX, +# endif +# ifdef CONFIG_LPUART7_RXDMA + .rxch = DMA_REQUEST_MUXLPUART7RX, + .rxfifo = g_lpuart7rxfifo, +# endif +}; +#endif + +#ifdef CONFIG_IMX9_LPUART8 +static struct imx9_uart_s g_lpuart8priv = +{ + .dev = + { + .recv = + { + .size = CONFIG_LPUART8_RXBUFSIZE, + .buffer = g_lpuart8rxbuffer, + }, + .xmit = + { + .size = CONFIG_LPUART8_TXBUFSIZE, + .buffer = g_lpuart8txbuffer, + }, + #if defined(CONFIG_LPUART8_RXDMA) && defined(CONFIG_LPUART8_TXDMA) + .ops = &g_lpuart_rxtxdma_ops, + #elif defined(CONFIG_LPUART8_RXDMA) && !defined(CONFIG_LPUART8_TXDMA) + .ops = &g_lpuart_rxdma_ops, + #elif !defined(CONFIG_LPUART8_RXDMA) && defined(CONFIG_LPUART8_TXDMA) + .ops = &g_lpuart_txdma_ops, + #else + .ops = &g_lpuart_ops, + #endif + }, + + .uartbase = IMX9_LPUART8_BASE, + .uartnum = 8, + .baud = CONFIG_LPUART8_BAUD, + .irq = IMX9_IRQ_LPUART8, + .parity = CONFIG_LPUART8_PARITY, + .bits = CONFIG_LPUART8_BITS, + .stopbits2 = CONFIG_LPUART8_2STOP, +# if defined(CONFIG_SERIAL_OFLOWCONTROL) && defined(CONFIG_LPUART8_OFLOWCONTROL) + .oflow = 1, + .cts_gpio = GPIO_LPUART8_CTS, +# endif +# if defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART8_IFLOWCONTROL) + .iflow = 1, +# endif +# if ((defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART8_RS485RTSCONTROL)) || \ + (defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART8_IFLOWCONTROL))) + .rts_gpio = GPIO_LPUART8_RTS, +# endif + +# if (defined(CONFIG_SERIAL_RS485CONTROL) || defined(CONFIG_SERIAL_IFLOWCONTROL)) && \ + defined(CONFIG_LPUART8_INVERTIFLOWCONTROL) + .inviflow = 1, +# endif + +# if defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART8_RS485RTSCONTROL) + .rs485mode = 1, +# endif + +# ifdef CONFIG_LPUART8_TXDMA + .txch = DMA_REQUEST_MUXLPUART8TX, +# endif +# ifdef CONFIG_LPUART8_RXDMA + .rxch = DMA_REQUEST_MUXLPUART8RX, + .rxfifo = g_lpuart8rxfifo, +# endif +}; +#endif + +#ifdef CONFIG_PM +static struct pm_callback_s g_serial_pmcb = +{ + .notify = up_pm_notify, + .prepare = up_pm_prepare, +}; +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: imx9_serialin + ****************************************************************************/ + +static inline uint32_t imx9_serialin(struct imx9_uart_s *priv, + uint32_t offset) +{ + return getreg32(priv->uartbase + offset); +} + +/**************************************************************************** + * Name: imx9_serialout + ****************************************************************************/ + +static inline void imx9_serialout(struct imx9_uart_s *priv, + uint32_t offset, uint32_t value) +{ + putreg32(value, priv->uartbase + offset); +} + +/**************************************************************************** + * Name: imx9_dma_nextrx + * + * Description: + * Returns the index into the RX FIFO where the DMA will place the next + * byte that it receives. + * + ****************************************************************************/ + +#ifdef SERIAL_HAVE_RXDMA +static int imx9_dma_nextrx(struct imx9_uart_s *priv) +{ + int dmaresidual = imx9_dmach_getcount(priv->rxdma); + DEBUGASSERT(dmaresidual <= RXDMA_BUFFER_SIZE); + + return (RXDMA_BUFFER_SIZE - dmaresidual) % RXDMA_BUFFER_SIZE; +} +#endif + +/**************************************************************************** + * Name: imx9_disableuartint + ****************************************************************************/ + +static inline void imx9_disableuartint(struct imx9_uart_s *priv, + uint32_t *ie) +{ + irqstate_t flags; + uint32_t regval; + + flags = spin_lock_irqsave(NULL); + regval = imx9_serialin(priv, IMX9_LPUART_CTRL_OFFSET); + + /* Return the current Rx and Tx interrupt state */ + + if (ie != NULL) + { + *ie = regval & LPUART_ALL_INTS; + } + + regval &= ~LPUART_ALL_INTS; + imx9_serialout(priv, IMX9_LPUART_CTRL_OFFSET, regval); + spin_unlock_irqrestore(NULL, flags); +} + +/**************************************************************************** + * Name: imx9_restoreuartint + ****************************************************************************/ + +static inline void imx9_restoreuartint(struct imx9_uart_s *priv, + uint32_t ie) +{ + irqstate_t flags; + uint32_t regval; + + /* Enable/disable any interrupts that are currently disabled but should be + * enabled/disabled. + */ + + flags = spin_lock_irqsave(NULL); + regval = imx9_serialin(priv, IMX9_LPUART_CTRL_OFFSET); + regval &= ~LPUART_ALL_INTS; + regval |= ie; + imx9_serialout(priv, IMX9_LPUART_CTRL_OFFSET, regval); + spin_unlock_irqrestore(NULL, flags); +} + +/**************************************************************************** + * Name: imx9_dma_setup + * + * Description: + * Configure the LPUART baud, bits, parity, etc. This method is called the + * first time that the serial port is opened. + * + ****************************************************************************/ + +#if defined(SERIAL_HAVE_RXDMA) || defined(SERIAL_HAVE_TXDMA) +static int imx9_dma_setup(struct uart_dev_s *dev) +{ + struct imx9_uart_s *priv = (struct imx9_uart_s *)dev; +#if defined(SERIAL_HAVE_RXDMA) + struct imx9_edma_xfrconfig_s config; +#endif + int result; + + /* Do the basic UART setup first, unless we are the console */ + + if (!dev->isconsole) + { + result = imx9_setup(dev); + if (result != OK) + { + return result; + } + } + +#if defined(SERIAL_HAVE_TXDMA) + /* Acquire the Tx DMA channel. This should always succeed. */ + + if (priv->txch != 0) + { + if (priv->txdma == NULL) + { + priv->txdma = imx9_dmach_alloc(priv->txch, 0); + if (priv->txdma == NULL) + { + return -EBUSY; + } + } + + /* Enable Tx DMA for the UART */ + + modifyreg32(priv->uartbase + IMX9_LPUART_BAUD_OFFSET, + 0, LPUART_BAUD_TDMAE); + } +#endif + +#if defined(SERIAL_HAVE_RXDMA) + /* Acquire the Rx DMA channel. This should always succeed. */ + + if (priv->rxch != 0) + { + if (priv->rxdma == NULL) + { + priv->rxdma = imx9_dmach_alloc(priv->rxch, 0); + + if (priv->rxdma == NULL) + { + return -EBUSY; + } + } + else + { + imx9_dmach_stop(priv->rxdma); + } + + /* Configure for circular DMA reception into the RX FIFO */ + + config.saddr = priv->uartbase + IMX9_LPUART_DATA_OFFSET; + config.daddr = (uintptr_t)priv->rxfifo; + config.soff = 0; + config.doff = 1; + config.iter = RXDMA_BUFFER_SIZE; + config.flags = EDMA_CONFIG_LINKTYPE_LINKNONE | + EDMA_CONFIG_LOOPDEST | + EDMA_CONFIG_INTHALF | + EDMA_CONFIG_INTMAJOR; + config.ssize = EDMA_8BIT; + config.dsize = EDMA_8BIT; + config.nbytes = 1; +#ifdef CONFIG_IMX9_EDMA_ELINK + config.linkch = 0; +#endif + + imx9_dmach_xfrsetup(priv->rxdma , &config); + + /* Reset our DMA shadow pointer and Rx data availability count to + * match the address just programmed above. + */ + + priv->rxdmanext = 0; + +#ifndef CONFIG_ARM64_DCACHE_DISABLE + + /* Make sure the rx buffer area is all invalid or clean */ + + up_invalidate_dcache((uintptr_t)priv->rxfifo, + (uintptr_t)priv->rxfifo + RXDMA_BUFFER_SIZE); + priv->rxdmaavail = 0; +#endif + + /* Enable receive Rx DMA for the UART */ + + modifyreg32(priv->uartbase + IMX9_LPUART_BAUD_OFFSET, + 0, LPUART_BAUD_RDMAE); + + /* Enable interrupt on idle and erros */ + + modifyreg32(priv->uartbase + IMX9_LPUART_CTRL_OFFSET, 0, + LPUART_CTRL_PEIE | + LPUART_CTRL_FEIE | + LPUART_CTRL_NEIE | + LPUART_CTRL_ILIE); + + /* Start the DMA channel, and arrange for callbacks at the half and + * full points in the FIFO. This ensures that we have half a FIFO + * worth of time to claim bytes before they are overwritten. + */ + + imx9_dmach_start(priv->rxdma, imx9_dma_rxcallback, (void *)priv); + } +#endif + + return OK; +} +#endif + +/**************************************************************************** + * Name: imx9_setup + * + * Description: + * Configure the UART baud, bits, parity, fifos, etc. This + * method is called the first time that the serial priv is + * opened. + * + ****************************************************************************/ + +static int imx9_setup(struct uart_dev_s *dev) +{ + struct imx9_uart_s *priv = (struct imx9_uart_s *)dev; +#ifndef CONFIG_SUPPRESS_LPUART_CONFIG + struct uart_config_s config = + { + 0 + }; + + int ret; + + /* Configure the UART */ + + config.baud = priv->baud; /* Configured baud */ + config.parity = priv->parity; /* 0=none, 1=odd, 2=even */ + config.bits = priv->bits; /* Number of bits (5-9) */ + config.stopbits2 = priv->stopbits2; /* true: Configure with 2 stop bits instead of 1 */ +#ifdef CONFIG_SERIAL_OFLOWCONTROL + config.usects = priv->oflow; /* Flow control on outbound side */ +#endif +#ifdef CONFIG_SERIAL_IFLOWCONTROL + /* Flow control on outbound side if not GPIO based */ + + if (priv->rts_gpio == 0) + { + config.userts = priv->iflow; + } + +#endif +#ifdef CONFIG_SERIAL_RS485CONTROL + config.users485 = priv->rs485mode; /* Switch into RS485 mode */ +#endif +#if defined(CONFIG_SERIAL_RS485CONTROL) || defined(CONFIG_SERIAL_IFLOWCONTROL) + config.invrts = priv->inviflow; /* Inversion of outbound flow control */ +#endif + + ret = imx9_lpuart_configure(priv->uartbase, priv->uartnum, &config); + + priv->ie = imx9_serialin(priv, IMX9_LPUART_CTRL_OFFSET) & \ + LPUART_ALL_INTS; + return ret; + +#else + priv->ie = imx9_serialin(priv, IMX9_LPUART_CTRL_OFFSET) & \ + LPUART_ALL_INTS; + return OK; +#endif +} + +/**************************************************************************** + * Name: imx9_shutdown + * + * Description: + * Disable the UART. This method is called when the serial + * priv is closed + * + ****************************************************************************/ + +static void imx9_shutdown(struct uart_dev_s *dev) +{ + struct imx9_uart_s *priv = (struct imx9_uart_s *)dev; + + /* Disable the UART */ + + imx9_serialout(priv, IMX9_LPUART_GLOBAL_OFFSET, LPUART_GLOBAL_RST); +} + +/**************************************************************************** + * Name: imx9_dma_shutdown + * + * Description: + * Disable the LPUART. This method is called when the serial + * port is closed + * + ****************************************************************************/ + +#if defined(SERIAL_HAVE_RXDMA) || defined(SERIAL_HAVE_TXDMA) +static void imx9_dma_shutdown(struct uart_dev_s *dev) +{ + struct imx9_uart_s *priv = (struct imx9_uart_s *)dev; + + /* Perform the normal UART shutdown */ + + imx9_shutdown(dev); + +#if defined(SERIAL_HAVE_RXDMA) + /* Stop the RX DMA channel */ + + if (priv->rxch != 0) + { + imx9_dmach_stop(priv->rxdma); + + /* Release the RX DMA channel */ + + imx9_dmach_free(priv->rxdma); + priv->rxdma = NULL; + } +#endif + +#if defined(SERIAL_HAVE_TXDMA) + /* Stop the TX DMA channel */ + + if (priv->txch != 0) + { + imx9_dmach_stop(priv->txdma); + + /* Release the TX DMA channel */ + + imx9_dmach_free(priv->txdma); + priv->txdma = NULL; + } +#endif +} +#endif + +/**************************************************************************** + * Name: imx9_attach + * + * Description: + * Configure the UART to operation in interrupt driven mode. This method + * is called when the serial priv is opened. Normally, this is just after + * the setup() method is called, however, the serial console may operate + * in a non-interrupt driven mode during the boot phase. + * + * RX and TX interrupts are not enabled when by the attach method (unless + * the hardware supprivs multiple levels of interrupt enabling). The RX + * and TX interrupts are not enabled until the txint() and rxint() methods + * are called. + * + ****************************************************************************/ + +static int imx9_attach(struct uart_dev_s *dev) +{ + struct imx9_uart_s *priv = (struct imx9_uart_s *)dev; + int ret; + + /* Attach and enable the IRQ */ + + ret = irq_attach(priv->irq, imx9_interrupt, dev); + if (ret == OK) + { + /* Enable the interrupt (RX and TX interrupts are still disabled + * in the UART + */ + + up_enable_irq(priv->irq); + } + + return ret; +} + +/**************************************************************************** + * Name: imx9_detach + * + * Description: + * Detach UART interrupts. This method is called when the serial priv is + * closed normally just before the shutdown method is called. The + * exception is the serial console which is never shutdown. + * + ****************************************************************************/ + +static void imx9_detach(struct uart_dev_s *dev) +{ + struct imx9_uart_s *priv = (struct imx9_uart_s *)dev; + + up_disable_irq(priv->irq); + irq_detach(priv->irq); +} + +/**************************************************************************** + * Name: imx9_interrupt (and front-ends) + * + * Description: + * This is the common UART interrupt handler. It will be invoked when an + * interrupt is received on the 'irq'. It should call uart_xmitchars or + * uart_recvchars to perform the appropriate data transfers. The + * interrupt handling logic must be able to map the 'arg' to the + * appropriate uart_dev_s structure in order to call these functions. + * + ****************************************************************************/ + +static int imx9_interrupt(int irq, void *context, void *arg) +{ + struct uart_dev_s *dev = (struct uart_dev_s *)arg; + struct imx9_uart_s *priv; + uint32_t usr; + uint32_t lsr; + int passes = 0; + bool handled; + + DEBUGASSERT(dev != NULL && dev != NULL); + priv = (struct imx9_uart_s *)dev; + +#if defined(CONFIG_PM) && CONFIG_IMX9_PM_SERIAL_ACTIVITY > 0 + /* Repriv serial activity to the power management logic */ + + pm_activity(PM_IDLE_DOMAIN, CONFIG_IMX9_PM_SERIAL_ACTIVITY); +#endif + + /* Loop until there are no characters to be transferred or, + * until we have been looping for a long time. + */ + + handled = true; + for (passes = 0; passes < 256 && handled; passes++) + { + handled = false; + + /* Get the current UART status and check for loop + * termination conditions + */ + + usr = imx9_serialin(priv, IMX9_LPUART_STAT_OFFSET); + + /* Removed all W1C from the last sr */ + + lsr = usr & ~(LPUART_STAT_LBKDIF | LPUART_STAT_RXEDGIF | + LPUART_STAT_IDLE | LPUART_STAT_OR | + LPUART_STAT_NF | LPUART_STAT_FE | + LPUART_STAT_PF | LPUART_STAT_MA1F | + LPUART_STAT_MA2F); + + /* Keep what we will service */ + + usr &= (LPUART_STAT_RDRF | LPUART_STAT_TDRE | LPUART_STAT_OR | + LPUART_STAT_FE | LPUART_STAT_NF | LPUART_STAT_PF | + LPUART_STAT_IDLE); + + /* Clear serial overrun, parity and framing errors */ + + if ((usr & LPUART_STAT_OR) != 0) + { + imx9_serialout(priv, IMX9_LPUART_STAT_OFFSET, + LPUART_STAT_OR | lsr); + } + + if ((usr & LPUART_STAT_NF) != 0) + { + imx9_serialout(priv, IMX9_LPUART_STAT_OFFSET, + LPUART_STAT_NF | lsr); + } + + if ((usr & LPUART_STAT_PF) != 0) + { + imx9_serialout(priv, IMX9_LPUART_STAT_OFFSET, + LPUART_STAT_PF | lsr); + } + + if ((usr & LPUART_STAT_FE) != 0) + { + imx9_serialout(priv, IMX9_LPUART_STAT_OFFSET, + LPUART_STAT_FE | lsr); + } + + if ((usr & (LPUART_STAT_FE | LPUART_STAT_PF | LPUART_STAT_NF)) != 0) + { + /* Discard data */ + + imx9_serialin(priv, IMX9_LPUART_DATA_OFFSET); + } + +#ifdef SERIAL_HAVE_RXDMA + /* The line going to idle, deliver any fractions of RX data */ + + if ((usr & LPUART_STAT_IDLE) != 0) + { + imx9_serialout(priv, IMX9_LPUART_STAT_OFFSET, + LPUART_STAT_IDLE | lsr); + imx9_dma_rxcallback(priv->rxdma, priv, false, LPUART_STAT_IDLE); + } +#endif + + /* Handle incoming, receive bytes */ + + if ((usr & LPUART_STAT_RDRF) != 0 && + (priv->ie & LPUART_CTRL_RIE) != 0) + { + uart_recvchars(dev); + handled = true; + } + + /* Handle outgoing, transmit bytes */ + + if ((usr & LPUART_STAT_TDRE) != 0 && + (priv->ie & LPUART_CTRL_TIE) != 0) + { + uart_xmitchars(dev); + handled = true; + } + } + + return OK; +} + +/**************************************************************************** + * Name: imx9_ioctl + * + * Description: + * All ioctl calls will be routed through this method + * + ****************************************************************************/ + +static int imx9_ioctl(struct file *filep, int cmd, unsigned long arg) +{ +#if defined(CONFIG_SERIAL_TIOCSERGSTRUCT) || defined(CONFIG_SERIAL_TERMIOS) + struct inode *inode = filep->f_inode; + struct uart_dev_s *dev = inode->i_private; + irqstate_t flags; +#endif + int ret = OK; + + switch (cmd) + { +#ifdef CONFIG_SERIAL_TIOCSERGSTRUCT + case TIOCSERGSTRUCT: + { + struct imx9_uart_s *user = (struct imx9_uart_s *)arg; + if (!user) + { + ret = -EINVAL; + } + else + { + memcpy(user, dev, sizeof(struct imx9_uart_s)); + } + } + break; +#endif + +#ifdef CONFIG_SERIAL_TERMIOS + case TCGETS: + { + struct termios *termiosp = (struct termios *)arg; + struct imx9_uart_s *priv = (struct imx9_uart_s *)dev; -#include -#include -#include -#include -#include -#include -#include + if (!termiosp) + { + ret = -EINVAL; + break; + } -#include "chip.h" -#include "arm64_arch.h" -#include "arm64_internal.h" -#include "arm64_gic.h" + /* Return parity */ -#include "imx9_serial.h" -#include "imx9_boot.h" + termiosp->c_cflag = ((priv->parity != 0) ? PARENB : 0) | + ((priv->parity == 1) ? PARODD : 0); -#include "hardware/imx9_lpuart.h" -#include "hardware/imx9_memorymap.h" + /* Return stop bits */ -#ifdef USE_SERIALDRIVER + termiosp->c_cflag |= (priv->stopbits2) ? CSTOPB : 0; -/**************************************************************************** - * Pre-processor Definitions - ****************************************************************************/ + /* Return flow control */ -/* Which UART with be tty0/console and which tty1-4? The console will - * always be ttyS0. If there is no console then will use the lowest - * numbered UART. - */ +#ifdef CONFIG_SERIAL_OFLOWCONTROL + termiosp->c_cflag |= ((priv->oflow) ? CCTS_OFLOW : 0); +#endif +#ifdef CONFIG_SERIAL_IFLOWCONTROL + termiosp->c_cflag |= ((priv->iflow) ? CRTS_IFLOW : 0); +#endif + /* Return baud */ -/* First pick the console and ttys0. This could be any of UART1-5 */ + cfsetispeed(termiosp, priv->baud); + + /* Return number of bits */ + + switch (priv->bits) + { + case 5: + termiosp->c_cflag |= CS5; + break; + + case 6: + termiosp->c_cflag |= CS6; + break; + + case 7: + termiosp->c_cflag |= CS7; + break; + + default: + case 8: + termiosp->c_cflag |= CS8; + break; + +#if defined(CS9) + case 9: + termiosp->c_cflag |= CS9; + break; +#endif + } + } + break; -#if defined(CONFIG_UART1_SERIAL_CONSOLE) -# define CONSOLE_DEV g_uart1port /* UART1 is console */ -# define TTYS0_DEV g_uart1port /* UART1 is ttyS0 */ -# define UART1_ASSIGNED 1 + case TCSETS: + { + struct termios *termiosp = (struct termios *)arg; + struct imx9_uart_s *priv = (struct imx9_uart_s *)dev; + uint32_t baud; + uint32_t ie; + uint8_t parity; + uint8_t nbits; + bool stop2; + + if ((!termiosp) +#ifdef CONFIG_SERIAL_OFLOWCONTROL + || ((termiosp->c_cflag & CCTS_OFLOW) && (priv->cts_gpio == 0)) +#endif +#ifdef CONFIG_SERIAL_IFLOWCONTROL + || ((termiosp->c_cflag & CRTS_IFLOW) && (priv->rts_gpio == 0)) #endif + ) + { + ret = -EINVAL; + break; + } -/* Rx DMA timeout in ms, which is used to calculate Rx ring buffer size */ -#define DMA_RX_TIMEOUT (10) -#define UART_AUTOSUSPEND_TIMEOUT 3000 + /* Decode baud. */ -/**************************************************************************** - * Private Types - ****************************************************************************/ + ret = OK; + baud = cfgetispeed(termiosp); -struct imx9_uart_port_s -{ - unsigned long iobase; - unsigned int baud; /* Configured baud */ - unsigned int irq_num; - unsigned int txfifo_size; - unsigned int rxfifo_size; - int is_console; -}; + /* Decode number of bits */ -static inline uint32_t imx9_read(struct imx9_uart_port_s *port, uint32_t off) -{ - return getreg32(port->iobase + off); -} + switch (termiosp->c_cflag & CSIZE) + { + case CS5: + nbits = 5; + break; -static inline void imx9_write(struct imx9_uart_port_s *port, uint32_t val, - uint32_t off) -{ - putreg32(val, port->iobase + off); -} + case CS6: + nbits = 6; + break; -static int imx9_lpuart_stop_tx(struct imx9_uart_port_s *sport) -{ - unsigned long temp; + case CS7: + nbits = 7; + break; - temp = imx9_read(sport, UARTCTRL); - temp &= ~(UARTCTRL_TIE | UARTCTRL_TCIE); - imx9_write(sport, temp, UARTCTRL); + case CS8: + nbits = 8; + break; - return 0; -} +#if defined(CS9) + case CS9: + nbits = 9; + break; +#endif + default: + ret = -EINVAL; + break; + } -static int imx9_lpuart_stop_rx(struct imx9_uart_port_s *sport) -{ - unsigned long temp; + /* Decode parity */ - temp = imx9_read(sport, UARTCTRL); - imx9_write(sport, temp & ~UARTCTRL_RE, UARTCTRL); + if ((termiosp->c_cflag & PARENB) != 0) + { + parity = (termiosp->c_cflag & PARODD) ? 1 : 2; + } + else + { + parity = 0; + } - return 0; -} + /* Decode stop bits */ -static int imx9_lpuart_start_tx(struct imx9_uart_port_s *sport) -{ - unsigned long temp; + stop2 = (termiosp->c_cflag & CSTOPB) != 0; - temp = imx9_read(sport, UARTCTRL); - imx9_write(sport, temp | UARTCTRL_TIE, UARTCTRL); + /* Verify that all settings are valid before committing */ - return 0; -} + if (ret == OK) + { + /* Commit */ -static int imx9_lpuart_start_rx(struct imx9_uart_port_s *sport) -{ - unsigned long temp; + priv->baud = baud; + priv->parity = parity; + priv->bits = nbits; + priv->stopbits2 = stop2; +#ifdef CONFIG_SERIAL_OFLOWCONTROL + priv->oflow = (termiosp->c_cflag & CCTS_OFLOW) != 0; +#endif +#ifdef CONFIG_SERIAL_IFLOWCONTROL + priv->iflow = (termiosp->c_cflag & CRTS_IFLOW) != 0; +#endif + /* effect the changes immediately - note that we do not + * implement TCSADRAIN / TCSAFLUSH + */ + + flags = spin_lock_irqsave(NULL); + imx9_disableuartint(priv, &ie); + ret = dev->ops->setup(dev); + + /* Restore the interrupt state */ + + imx9_restoreuartint(priv, ie); + priv->ie = ie; + spin_unlock_irqrestore(NULL, flags); + } + } + break; +#endif /* CONFIG_SERIAL_TERMIOS */ - temp = imx9_read(sport, UARTCTRL); - imx9_write(sport, temp | UARTCTRL_RE, UARTCTRL); +#ifdef CONFIG_IMX9_LPUART_SINGLEWIRE + case TIOCSSINGLEWIRE: + { + uint32_t regval; + irqstate_t flags; + struct imx9_uart_s *priv = (struct imx9_uart_s *)dev; + + flags = spin_lock_irqsave(NULL); + regval = imx9_serialin(priv, IMX9_LPUART_CTRL_OFFSET); + + if ((arg & SER_SINGLEWIRE_ENABLED) != 0) + { + regval |= LPUART_CTRL_LOOPS | LPUART_CTRL_RSRC; + } + else + { + regval &= ~(LPUART_CTRL_LOOPS | LPUART_CTRL_RSRC); + } + + imx9_serialout(priv, IMX9_LPUART_CTRL_OFFSET, regval); + + spin_unlock_irqrestore(NULL, flags); + } + break; +#endif + +#ifdef CONFIG_IMX9_LPUART_INVERT + case TIOCSINVERT: + { + uint32_t ctrl; + uint32_t stat; + uint32_t regval; + irqstate_t flags; + struct imx9_uart_s *priv = (struct imx9_uart_s *)dev; + + flags = spin_lock_irqsave(NULL); + ctrl = imx9_serialin(priv, IMX9_LPUART_CTRL_OFFSET); + stat = imx9_serialin(priv, IMX9_LPUART_STAT_OFFSET); + regval = ctrl; + + /* {R|T}XINV bit field can only be written when the receiver is + * disabled (RE=0). + */ + + regval &= ~LPUART_CTRL_RE; + + imx9_serialout(priv, IMX9_LPUART_CTRL_OFFSET, regval); + + /* Enable/disable signal inversion. */ + + if (arg & SER_INVERT_ENABLED_RX) + { + stat |= LPUART_STAT_RXINV; + } + else + { + stat &= ~LPUART_STAT_RXINV; + } + + /* Do not invert TX when in TIOCSSINGLEWIRE */ + + if ((arg & SER_INVERT_ENABLED_TX) && + ((ctrl & LPUART_CTRL_LOOPS) != LPUART_CTRL_LOOPS)) + { + ctrl |= LPUART_CTRL_TXINV; + } + else + { + ctrl &= ~LPUART_CTRL_TXINV; + } + + imx9_serialout(priv, IMX9_LPUART_STAT_OFFSET, stat); + imx9_serialout(priv, IMX9_LPUART_CTRL_OFFSET, ctrl); + + spin_unlock_irqrestore(NULL, flags); + } + break; +#endif + + case TIOCSBRK: /* BSD compatibility: Turn break on, unconditionally */ + case TIOCCBRK: /* BSD compatibility: Turn break off, unconditionally */ + default: + ret = -ENOTTY; + break; + } - return 0; + return ret; } /**************************************************************************** - * Name: imx9_rxint + * Name: imx9_receive * * Description: - * Call to enable or disable RX interrupts + * Called (usually) from the interrupt level to receive one + * character from the UART. Error bits associated with the + * receipt are provided in the return 'status'. * ****************************************************************************/ -static void imx9_rxint(struct uart_dev_s *dev, bool enable) +#ifndef SERIAL_HAVE_ONLY_RXDMA +static int imx9_receive(struct uart_dev_s *dev, unsigned int *status) { - struct imx9_uart_port_s *sport = (struct imx9_uart_port_s *)dev->priv; + struct imx9_uart_s *priv = (struct imx9_uart_s *)dev; + uint32_t rxd; - if (enable) - { - imx9_lpuart_start_rx(sport); - } - else - { - imx9_lpuart_stop_rx(sport); - } + rxd = imx9_serialin(priv, IMX9_LPUART_DATA_OFFSET); + *status = rxd >> LPUART_DATA_STATUS_SHIFT; + return (rxd & LPUART_DATA_MASK) >> LPUART_DATA_SHIFT; } +#endif /**************************************************************************** - * Name: imx9_txint + * Name: imx9_rxint * * Description: - * Call to enable or disable TX interrupts + * Call to enable or disable RX interrupts * ****************************************************************************/ -static void imx9_txint(struct uart_dev_s *dev, bool enable) +#ifndef SERIAL_HAVE_ONLY_RXDMA +static void imx9_rxint(struct uart_dev_s *dev, bool enable) { - struct imx9_uart_port_s *sport = (struct imx9_uart_port_s *)dev->priv; + struct imx9_uart_s *priv = (struct imx9_uart_s *)dev; + irqstate_t flags; + uint32_t regval; + /* Enable interrupts for data available at Rx */ + + flags = spin_lock_irqsave(NULL); if (enable) { - imx9_lpuart_start_tx(sport); +#ifndef CONFIG_SUPPRESS_SERIAL_INTS + priv->ie |= LPUART_CTRL_RIE | LPUART_CTRL_FEIE | LPUART_CTRL_ORIE; +#endif } else { - imx9_lpuart_stop_tx(sport); + priv->ie &= ~(LPUART_CTRL_RIE | LPUART_CTRL_FEIE | LPUART_CTRL_ORIE); } + + regval = imx9_serialin(priv, IMX9_LPUART_CTRL_OFFSET); + regval &= ~LPUART_ALL_INTS; + regval |= priv->ie; + imx9_serialout(priv, IMX9_LPUART_CTRL_OFFSET, regval); + spin_unlock_irqrestore(NULL, flags); } +#endif /**************************************************************************** - * Name: imx9_send + * Name: imx9_rxavailable * * Description: - * This method will send one byte on the UART + * Return true if the receive fifo is not empty * ****************************************************************************/ -static void imx9_send(struct uart_dev_s *dev, int ch) +#ifndef SERIAL_HAVE_ONLY_RXDMA +static bool imx9_rxavailable(struct uart_dev_s *dev) { - struct imx9_uart_port_s *sport = (struct imx9_uart_port_s *)dev->priv; + struct imx9_uart_s *priv = (struct imx9_uart_s *)dev; + uint32_t regval; - imx9_write(sport, ch, UARTDATA); + /* Return true is data is ready in the Rx FIFO */ + + regval = imx9_serialin(priv, IMX9_LPUART_STAT_OFFSET); + return ((regval & LPUART_STAT_RDRF) != 0); } +#endif /**************************************************************************** - * Name: imx9_receive + * Name: imx9_rxflowcontrol * * Description: - * Called (usually) from the interrupt level to receive one - * character from the UART. Error bits associated with the - * receipt are provided in the return 'status'. + * Called when Rx buffer is full (or exceeds configured watermark levels + * if CONFIG_SERIAL_IFLOWCONTROL_WATERMARKS is defined). + * Return true if UART activated RX flow control to block more incoming + * data + * + * Input Parameters: + * dev - UART device instance + * nbuffered - the number of characters currently buffered + * (if CONFIG_SERIAL_IFLOWCONTROL_WATERMARKS is + * not defined the value will be 0 for an empty buffer or the + * defined buffer size for a full buffer) + * upper - true indicates the upper watermark was crossed where + * false indicates the lower watermark has been crossed + * + * Returned Value: + * true if RX flow control activated. * ****************************************************************************/ -static int imx9_receive(struct uart_dev_s *dev, unsigned int *status) +#ifdef CONFIG_SERIAL_IFLOWCONTROL +static bool imx9_rxflowcontrol(struct uart_dev_s *dev, + unsigned int nbuffered, bool upper) { - struct imx9_uart_port_s *sport = (struct imx9_uart_port_s *)dev->priv; - unsigned int rx; - unsigned int sr; + struct imx9_uart_s *priv = (struct imx9_uart_s *)dev; - /* to clear the FE, OR, NF, FE, PE flags, - * read STAT then read DATA reg - */ + if (priv->iflow && (priv->rts_gpio != 0)) + { + /* Assert/de-assert nRTS set it high resume/stop sending */ + + imx9_gpiowrite(priv->rts_gpio, upper); + + if (upper) + { + /* With heavy Rx traffic, RXNE might be set and data pending. + * Returning 'true' in such case would cause RXNE left unhandled + * and causing interrupt storm. Sending end might be also be slow + * to react on nRTS, and returning 'true' here would prevent + * processing that data. + * + * Therefore, return 'false' so input data is still being processed + * until sending end reacts on nRTS signal and stops sending more. + */ + + return false; + } + + return upper; + } + else + { + /* Is the RX buffer full? */ + + if (upper) + { + /* Disable Rx interrupt to prevent more data being from + * peripheral. When hardware RTS is enabled, this will + * prevent more data from coming in. + * + * This function is only called when UART recv buffer is full, + * that is: "dev->recv.head + 1 == dev->recv.tail". + * + * Logic in "uart_read" will automatically toggle Rx interrupts + * when buffer is read empty and thus we do not have to re- + * enable Rx interrupts. + */ + + uart_disablerxint(dev); + return true; + } + + /* No.. The RX buffer is empty */ - sr = imx9_read(sport, UARTSTAT); - rx = imx9_read(sport, UARTDATA); + else + { + /* We might leave Rx interrupt disabled if full recv buffer was + * read empty. Enable Rx interrupt to make sure that more input is + * received. + */ - *status = sr; + uart_enablerxint(dev); + } + } - return rx; + return false; } +#endif /**************************************************************************** - * Name: imx9_rxavailable + * Name: imx9_dma_receive * * Description: - * Return true if the receive fifo is not empty + * Called (usually) from the interrupt level to receive one + * character from the LPUART. Error bits associated with the + * receipt are provided in the return 'status'. * ****************************************************************************/ -static bool imx9_rxavailable(struct uart_dev_s *dev) +#ifdef SERIAL_HAVE_RXDMA +static int imx9_dma_receive(struct uart_dev_s *dev, unsigned int *status) { - struct imx9_uart_port_s *sport = (struct imx9_uart_port_s *)dev->priv; - unsigned long sfifo = imx9_read(sport, UARTFIFO); + struct imx9_uart_s *priv = (struct imx9_uart_s *)dev; + uint32_t nextrx = imx9_dma_nextrx(priv); + int c = 0; - if (sfifo & UARTFIFO_RXEMPT) - { - return false; - } - else + /* Check if more data is available */ + + if (nextrx != priv->rxdmanext) { - return true; +#ifndef CONFIG_ARM64_DCACHE_DISABLE + /* If the data cache is enabled, then we will also need to manage + * cache coherency. Are any bytes available in the currently coherent + * region of the data cache? + */ + + if (priv->rxdmaavail == 0) + { + uint32_t rxdmaavail; + uintptr_t addr; + + /* No.. then we will have to invalidate additional space in the Rx + * DMA buffer. + */ + + if (nextrx > priv->rxdmanext) + { + /* Number of available bytes */ + + rxdmaavail = nextrx - priv->rxdmanext; + } + else + { + /* Number of available bytes up to the end of RXDMA buffer */ + + rxdmaavail = RXDMA_BUFFER_SIZE - priv->rxdmanext; + } + + /* Invalidate the DMA buffer range */ + + addr = (uintptr_t)&priv->rxfifo[priv->rxdmanext]; + up_invalidate_dcache(addr, addr + rxdmaavail); + + /* We don't need to invalidate the data cache for the next + * rxdmaavail number of next bytes. + */ + + priv->rxdmaavail = rxdmaavail; + } + + priv->rxdmaavail--; +#endif + + /* Now read from the DMA buffer */ + + c = priv->rxfifo[priv->rxdmanext]; + + priv->rxdmanext++; + + if (priv->rxdmanext == RXDMA_BUFFER_SIZE) + { + priv->rxdmanext = 0; + } } + + /* NOTE: If no data is available, then we would return NULL which is, + * of course, valid binary data. The protocol is that the upper half + * driver must call imx9_dma_rxavailable prior to calling this + * function to assure that this never happens. + */ + + return c; } +#endif /**************************************************************************** - * Name: imx9_txready + * Name: imx9_dma_reenable * * Description: - * Return true if the tranmsit fifo is not full + * Call to re-enable RX DMA. * ****************************************************************************/ -static bool imx9_txready(struct uart_dev_s *dev) +#if defined(SERIAL_HAVE_RXDMA) && defined(CONFIG_PM) +static void imx9_dma_reenable(struct imx9_uart_s *priv) { - struct imx9_uart_port_s *sport = (struct imx9_uart_port_s *)dev->priv; - unsigned long txcnt; + struct imx9_edma_xfrconfig_s config; + + /* Stop an reset the RX DMA */ + + imx9_dmach_stop(priv->rxdma); + + /* Configure for circular DMA reception into the RX FIFO */ + + config.saddr = priv->uartbase + IMX9_LPUART_DATA_OFFSET; + config.daddr = (uint32_t) priv->rxfifo; + config.soff = 0; + config.doff = 1; + config.iter = RXDMA_BUFFER_SIZE; + config.flags = EDMA_CONFIG_LINKTYPE_LINKNONE | + EDMA_CONFIG_LOOPDEST | + EDMA_CONFIG_INTHALF | + EDMA_CONFIG_INTMAJOR; + config.ssize = EDMA_8BIT; + config.dsize = EDMA_8BIT; + config.nbytes = 1; +#ifdef CONFIG_IMX9_EDMA_ELINK + config.linkch = 0; +#endif - /* When TXFULL is set, there is no space in the Tx FIFO */ + imx9_dmach_xfrsetup(priv->rxdma, &config); - txcnt = imx9_read(sport, UARTWATER); - txcnt = txcnt >> UARTWATER_TXCNT_OFF; - txcnt &= UARTWATER_COUNT_MASK; + /* Reset our DMA shadow pointer and Rx data availability count to match + * the address just programmed above. + */ - if (txcnt < sport->txfifo_size) - { - return true; - } - else - { - return false; - } + priv->rxdmanext = 0; +#ifndef CONFIG_ARM64_DCACHE_DISABLE + priv->rxdmaavail = 0; +#endif + + /* Start the DMA channel, and arrange for callbacks at the half and + * full points in the FIFO. This ensures that we have half a FIFO + * worth of time to claim bytes before they are overwritten. + */ + + imx9_dmach_start(priv->rxdma, imx9_dma_rxcallback, (void *)priv); + + /* Clear DMA suspended flag. */ + + priv->rxdmasusp = false; } +#endif /**************************************************************************** - * Name: imx9_txempty + * Name: imx9_dma_rxint * * Description: - * Return true if the transmit fifo is empty + * Call to enable or disable RX interrupts * ****************************************************************************/ -static bool imx9_txempty(struct uart_dev_s *dev) +#ifdef SERIAL_HAVE_RXDMA +static void imx9_dma_rxint(struct uart_dev_s *dev, bool enable) { - struct imx9_uart_port_s *sport = (struct imx9_uart_port_s *)dev->priv; - unsigned long stat = imx9_read(sport, UARTSTAT); - unsigned long sfifo = imx9_read(sport, UARTFIFO); + struct imx9_uart_s *priv = (struct imx9_uart_s *)dev; + + /* Enable/disable DMA reception. + * + * Note that it is not safe to check for available bytes and immediately + * pass them to uart_recvchars as that could potentially recurse back + * to us again. Instead, bytes must wait until the next up_dma_poll or + * DMA event. + */ - if (stat & UARTSTAT_TC && sfifo & UARTFIFO_TXEMPT) - { - return true; - } - else - { - return false; - } + priv->rxenable = enable; } +#endif /**************************************************************************** - * Name: imx9_irq_handler (and front-ends) + * Name: imx9_dma_rxavailable * * Description: - * This is the common UART interrupt handler. It should cal - * uart_transmitchars or uart_receivechar to perform the appropriate data - * transfers. + * Return true if the receive register is not empty * ****************************************************************************/ -static int imx9_uart_irq_handler(int irq, void *context, void *arg) +#ifdef SERIAL_HAVE_RXDMA +static bool imx9_dma_rxavailable(struct uart_dev_s *dev) { - struct uart_dev_s *dev = (struct uart_dev_s *)arg; - struct imx9_uart_port_s *sport; - unsigned long sts; - unsigned long rxcount; - - DEBUGASSERT(dev != NULL && dev->priv != NULL); - sport = (struct imx9_uart_port_s *)dev->priv; - - sts = imx9_read(sport, UARTSTAT); - rxcount = imx9_read(sport, UARTWATER); - rxcount = rxcount >> UARTWATER_RXCNT_OFF; + struct imx9_uart_s *priv = (struct imx9_uart_s *)dev; - if (sts & UARTSTAT_RDRF || rxcount > 0) - { - uart_recvchars(dev); - } - - if (sts & UARTSTAT_TDRE) - { - uart_xmitchars(dev); - } + /* Compare our receive pointer to the current DMA pointer, if they + * do not match, then there are bytes to be received. + */ - imx9_write(sport, sts, UARTSTAT); - return OK; + return (imx9_dma_nextrx(priv) != priv->rxdmanext); } +#endif -static int imx9_lpuart_hw_reset(struct imx9_uart_port_s *sport) -{ - if (sport->is_console) - { - return 0; - } - - /* Toggle the reset signal to reset and release the peripheral */ +/**************************************************************************** + * Name: imx9_dma_txcallback + * + * Description: + * This function clears dma buffer at complete of DMA transfer and wakes up + * threads waiting for space in buffer. + * + ****************************************************************************/ - putreg32(UARTGLOBAL_RST, sport->iobase + UARTGLOBAL); - putreg32(0, sport->iobase + UARTGLOBAL); +#ifdef SERIAL_HAVE_TXDMA +static void imx9_dma_txcallback(DMACH_HANDLE handle, void *arg, bool done, + int result) +{ + struct imx9_uart_s *priv = (struct imx9_uart_s *)arg; - return 0; -} + /* Update 'nbytes' indicating number of bytes actually transferred by DMA. + * This is important to free TX buffer space by 'uart_xmitchars_done'. + */ -static int imx9_setup(struct uart_dev_s *dev) -{ - irqstate_t i_flags; - int ret; - struct imx9_uart_port_s *sport = (struct imx9_uart_port_s *)dev->priv; + priv->dev.dmatx.nbytes = priv->dev.dmatx.length; +#if CONFIG_IMX9_EDMA_NTCD > 1 + priv->dev.dmatx.nbytes += priv->dev.dmatx.nlength; +#endif - i_flags = up_irq_save(); + /* Adjust the pointers */ - ret = imx9_lpuart_hw_reset(sport); - if (ret < 0) - { - serr("failed to reset hw: %d\n", ret); - } + uart_xmitchars_done(&priv->dev); - up_irq_restore(i_flags); + /* Send more data if available */ - return ret; + imx9_dma_txavailable(&priv->dev); } +#endif /**************************************************************************** - * Name: imx9_shutdown + * Name: imx9_dma_txavailable * * Description: - * Disable the UART. This method is called when the serial - * port is closed + * Informs DMA that Tx data is available and is ready for transfer. * ****************************************************************************/ -static void imx9_shutdown(struct uart_dev_s *dev) +#ifdef SERIAL_HAVE_TXDMA +static void imx9_dma_txavailable(struct uart_dev_s *dev) { - struct imx9_uart_port_s *sport = (struct imx9_uart_port_s *)dev->priv; - unsigned long temp; - irqstate_t i_flags; - - i_flags = up_irq_save(); - - /* clear statue */ - - temp = imx9_read(sport, UARTSTAT); - imx9_write(sport, temp, UARTSTAT); - - /* disable Rx/Tx DMA */ - - temp = imx9_read(sport, UARTBAUD); - temp &= ~(UARTBAUD_TDMAE | UARTBAUD_RDMAE | UARTBAUD_RIDMAE); - imx9_write(sport, temp, UARTBAUD); + struct imx9_uart_s *priv = (struct imx9_uart_s *)dev; - /* disable Rx/Tx and interrupts */ + /* Only send when the DMA is idle */ - temp = imx9_read(sport, UARTCTRL); - temp &= ~(UARTCTRL_TE | UARTCTRL_RE | UARTCTRL_TIE | - UARTCTRL_TCIE | UARTCTRL_RIE | UARTCTRL_ILIE | - UARTCTRL_LOOPS); - imx9_write(sport, temp, UARTCTRL); - imx9_write(sport, 0, UARTMODIR); - - up_irq_restore(i_flags); + if (imx9_dmach_idle(priv->txdma) == 0) + { + uart_xmitchars_dma(dev); + } } +#endif + +/**************************************************************************** + * Name: imx9_dma_send + * + * Description: + * Called (usually) from the interrupt level to start DMA transfer. + * (Re-)Configures DMA Stream updating buffer and buffer length. + * + ****************************************************************************/ -static void imx9_setup_watermark(struct imx9_uart_port_s *sport) +#ifdef SERIAL_HAVE_TXDMA +static void imx9_dma_send(struct uart_dev_s *dev) { - unsigned long val; - unsigned long ctrl; - unsigned long ctrl_saved; - unsigned long rxiden_cnt; + struct imx9_uart_s *priv = (struct imx9_uart_s *)dev; + struct imx9_edma_xfrconfig_s config; - ctrl = imx9_read(sport, UARTCTRL); - ctrl_saved = ctrl; - ctrl &= ~(UARTCTRL_TIE | UARTCTRL_TCIE | UARTCTRL_TE | - UARTCTRL_RIE | UARTCTRL_RE); - imx9_write(sport, ctrl, UARTCTRL); + /* We need to stop DMA before reconfiguration */ - /* enable FIFO mode */ + imx9_dmach_stop(priv->txdma); - val = imx9_read(sport, UARTFIFO); - val |= UARTFIFO_TXFE | UARTFIFO_RXFE; - val |= UARTFIFO_TXFLUSH | UARTFIFO_RXFLUSH; - val &= ~(UARTFIFO_RXIDEN_MASK << UARTFIFO_RXIDEN_OFF); - rxiden_cnt = 0; - val |= ((rxiden_cnt & UARTFIFO_RXIDEN_MASK) << UARTFIFO_RXIDEN_OFF); + /* Reset the number sent */ - imx9_write(sport, val, UARTFIFO); + dev->dmatx.nbytes = 0; - /* set the watermark - * rx_watermark = 1; + /* Make use of setup function to update buffer and its length for next + * transfer */ - val = (1 << UARTWATER_RXWATER_OFF) | (0x0 << UARTWATER_TXWATER_OFF); - imx9_write(sport, val, UARTWATER); - - /* Restore cr2 */ - - imx9_write(sport, ctrl_saved, UARTCTRL); -} - -static void imx9_setup_watermark_enable(struct imx9_uart_port_s *sport) -{ - uint32_t temp; + config.iter = dev->dmatx.length; + config.flags = EDMA_CONFIG_LINKTYPE_LINKNONE; + config.ssize = EDMA_8BIT; + config.dsize = EDMA_8BIT; + config.nbytes = 1; + config.saddr = (uintptr_t)dev->dmatx.buffer; + config.daddr = priv->uartbase + IMX9_LPUART_DATA_OFFSET; + config.soff = 1; + config.doff = 0; +#ifdef CONFIG_IMX9_EDMA_ELINK + config.linkch = 0; +#endif - imx9_setup_watermark(sport); + /* Flush the contents of the TX buffer into physical memory */ - temp = imx9_read(sport, UARTCTRL); - temp |= UARTCTRL_RE | UARTCTRL_TE; - temp |= UARTCTRL_IDLECFG << UARTCTRL_IDLECFG_OFF; - imx9_write(sport, temp, UARTCTRL); -} + up_clean_dcache((uintptr_t)dev->dmatx.buffer, + (uintptr_t)dev->dmatx.buffer + dev->dmatx.length); -static void imx9_hw_disable(struct imx9_uart_port_s *sport) -{ - unsigned long temp; + /* Setup first half */ - temp = imx9_read(sport, UARTCTRL); - temp &= ~(UARTCTRL_RIE | UARTCTRL_ILIE | UARTCTRL_RE | - UARTCTRL_TIE | UARTCTRL_TE); - imx9_write(sport, temp, UARTCTRL); -} + imx9_dmach_xfrsetup(priv->txdma, &config); -static void imx9_configure(struct imx9_uart_port_s *sport) -{ - unsigned long temp; +#if CONFIG_IMX9_EDMA_NTCD > 1 + /* Is this a split transfer? */ - temp = imx9_read(sport, UARTCTRL); - temp |= UARTCTRL_RIE | UARTCTRL_ILIE; - temp |= UARTCTRL_TIE; - imx9_write(sport, temp, UARTCTRL); -} + if (dev->dmatx.nbuffer) + { + config.iter = priv->dev.dmatx.nlength; + config.saddr = (uintptr_t)priv->dev.dmatx.nbuffer; -static void imx9_hw_setup(struct imx9_uart_port_s *sport) -{ - irqstate_t i_flags; + /* Flush the contents of the next TX buffer into physical memory */ - i_flags = up_irq_save(); + up_clean_dcache((uintptr_t)dev->dmatx.nbuffer, + (uintptr_t)dev->dmatx.nbuffer + dev->dmatx.nlength); - imx9_hw_disable(sport); + imx9_dmach_xfrsetup(priv->txdma, &config); + } +#endif - imx9_setup_watermark_enable(sport); - imx9_configure(sport); - up_enable_irq(sport->irq_num); + /* Start transmission with the callback on DMA completion */ - up_irq_restore(i_flags); + imx9_dmach_start(priv->txdma, imx9_dma_txcallback, (void *)priv); } +#endif /**************************************************************************** - * Name: imx9_attach + * Name: imx9_send * * Description: - * Configure the UART to operation in interrupt driven mode. This method - * is called when the serial port is opened. Normally, this is just after - * the setup() method is called, however, the serial console may operate in - * a non-interrupt driven mode during the boot phase. - * - * RX and TX interrupts are not enabled when by the attach method (unless - * the hardware supports multiple levels of interrupt enabling). The RX - * and TX interrupts are not enabled until the txint() and rxint() methods - * are called. + * This method will send one byte on the UART * ****************************************************************************/ -static int imx9_attach(struct uart_dev_s *dev) +static void imx9_send(struct uart_dev_s *dev, int ch) { - struct imx9_uart_port_s *sport = (struct imx9_uart_port_s *)dev->priv; - int ret; - unsigned long temp; - - /* determine FIFO size */ - - temp = imx9_read(sport, UARTFIFO); - - sport->txfifo_size = UARTFIFO_DEPTH( - (temp >> UARTFIFO_TXSIZE_OFF) & UARTFIFO_FIFOSIZE_MASK); - sport->rxfifo_size = UARTFIFO_DEPTH( - (temp >> UARTFIFO_RXSIZE_OFF) & UARTFIFO_FIFOSIZE_MASK); - - ret = irq_attach(sport->irq_num, imx9_uart_irq_handler, dev); + struct imx9_uart_s *priv = (struct imx9_uart_s *)dev; - if (ret == OK) - { - imx9_hw_setup(sport); - } - else +#ifdef CONSOLE_DEV + if (dev == &CONSOLE_DEV.dev && !dev->isconsole) { - sinfo("error ret=%d\n", ret); + return; } +#endif - return ret; + imx9_serialout(priv, IMX9_LPUART_DATA_OFFSET, (uint32_t)ch); } /**************************************************************************** - * Name: imx_detach + * Name: imx9_dma_txint * * Description: - * Detach UART interrupts. This method is called when the serial port is - * closed normally just before the shutdown method is called. The - * exception is the serial console which is never shutdown. + * Call to enable or disable TX interrupts from the UART. * ****************************************************************************/ -static void imx9_detach(struct uart_dev_s *dev) +#ifdef SERIAL_HAVE_TXDMA +static void imx9_dma_txint(struct uart_dev_s *dev, bool enable) { - struct imx9_uart_port_s *sport = (struct imx9_uart_port_s *)dev->priv; + /* Nothing to do. */ - up_disable_irq(sport->irq_num); - irq_detach(sport->irq_num); + /* In case of DMA transfer we do not want to make use of UART interrupts. + * Instead, we use DMA interrupts that are activated once during boot + * sequence. Furthermore we can use imx9_dma_txcallback() to handle + * stuff at half DMA transfer or after transfer completion (depending + * on the configuration). + */ } +#endif /**************************************************************************** - * Name: imx9_ioctl + * Name: imx9_txint * * Description: - * All ioctl calls will be routed through this method - * for current imx9 configure. + * Call to enable or disable TX interrupts * ****************************************************************************/ -static int imx9_ioctl(struct file *filep, int cmd, unsigned long arg) +#if !defined(SERIAL_HAVE_ONLY_TXDMA) +static void imx9_txint(struct uart_dev_s *dev, bool enable) { -#if defined(CONFIG_SERIAL_TIOCSERGSTRUCT) || defined(CONFIG_SERIAL_TERMIOS) - struct inode *inode = filep->f_inode; - struct uart_dev_s *dev = inode->i_private; - irqstate_t flags; -#endif - int ret = OK; + struct imx9_uart_s *priv = (struct imx9_uart_s *)dev; + irqstate_t flags; + uint32_t regval; - switch (cmd) - { -#ifdef CONFIG_SERIAL_TIOCSERGSTRUCT - case TIOCSERGSTRUCT: + /* Enable interrupt for TX complete */ + + flags = spin_lock_irqsave(NULL); + if (enable) { - struct imx_uart_s *user = (struct imx_uart_s *)arg; - if (!user) - { - ret = -EINVAL; - } - else - { - memcpy(user, dev, sizeof(struct imx_uart_s)); - } - } - break; +#ifndef CONFIG_SUPPRESS_SERIAL_INTS + priv->ie |= LPUART_CTRL_TIE; #endif - -#ifdef CONFIG_SERIAL_TERMIOS - case TCGETS: + } + else { - struct termios *termiosp = (struct termios *)arg; - struct imx_uart_s *priv = (struct imx_uart_s *)dev->priv; - - if (!termiosp) - { - ret = -EINVAL; - break; - } - - /* Return parity */ - - termiosp->c_cflag = ((priv->parity != 0) ? PARENB : 0) | - ((priv->parity == 1) ? PARODD : 0); - - /* Return stop bits */ - - termiosp->c_cflag |= (priv->stopbits2) ? CSTOPB : 0; - - /* Return flow control */ + priv->ie &= ~LPUART_CTRL_TIE; + } -#ifdef CONFIG_SERIAL_OFLOWCONTROL - termiosp->c_cflag |= ((priv->oflow) ? CCTS_OFLOW : 0); -#endif -#ifdef CONFIG_SERIAL_IFLOWCONTROL - termiosp->c_cflag |= ((priv->iflow) ? CRTS_IFLOW : 0); + regval = imx9_serialin(priv, IMX9_LPUART_CTRL_OFFSET); + regval &= ~LPUART_ALL_INTS; + regval |= priv->ie; + imx9_serialout(priv, IMX9_LPUART_CTRL_OFFSET, regval); + spin_unlock_irqrestore(NULL, flags); +} #endif - /* Return baud */ +/**************************************************************************** + * Name: imx9_txready + * + * Description: + * Return true if the transmit register is available to be written to + * + ****************************************************************************/ - cfsetispeed(termiosp, priv->baud); +static bool imx9_txready(struct uart_dev_s *dev) +{ + struct imx9_uart_s *priv = (struct imx9_uart_s *)dev; + uint32_t regval; - /* Return number of bits */ + regval = imx9_serialin(priv, IMX9_LPUART_STAT_OFFSET); + return ((regval & LPUART_STAT_TDRE) != 0); +} - switch (priv->bits) - { - case 5: - { - termiosp->c_cflag |= CS5; - break; - } +/**************************************************************************** + * Name: imx9_txempty + * + * Description: + * Return true if the transmit reg is empty + * + ****************************************************************************/ - case 6: - { - termiosp->c_cflag |= CS6; - break; - } +static bool imx9_txempty(struct uart_dev_s *dev) +{ + struct imx9_uart_s *priv = (struct imx9_uart_s *)dev; + uint32_t regval; - case 7: - { - termiosp->c_cflag |= CS7; - break; - } + regval = imx9_serialin(priv, IMX9_LPUART_STAT_OFFSET); + return ((regval & LPUART_STAT_TDRE) != 0); +} - default: - case 8: - { - termiosp->c_cflag |= CS8; - break; - } +/**************************************************************************** + * Name: imx9_dma_rxcallback + * + * Description: + * This function checks the current DMA state and calls the generic + * serial stack when bytes appear to be available. + * + ****************************************************************************/ -#if defined(CS9) - case 9: - { - termiosp->c_cflag |= CS9; - break; - } -#endif - } - } - break; +#ifdef SERIAL_HAVE_RXDMA +static void imx9_dma_rxcallback(DMACH_HANDLE handle, void *arg, bool done, + int result) +{ + struct imx9_uart_s *priv = (struct imx9_uart_s *)arg; + uint32_t sr; - case TCSETS: + if (priv->rxenable && imx9_dma_rxavailable(&priv->dev)) { - struct termios *termiosp = (struct termios *)arg; - struct imx_uart_s *priv = (struct imx_uart_s *)dev->priv; - uint32_t baud; - uint32_t ie; - uint8_t parity; - uint8_t nbits; - bool stop2; - - if ((!termiosp) -#ifdef CONFIG_SERIAL_OFLOWCONTROL - || ((termiosp->c_cflag & CCTS_OFLOW) && (priv->cts_gpio == 0)) -#endif -#ifdef CONFIG_SERIAL_IFLOWCONTROL - || ((termiosp->c_cflag & CRTS_IFLOW) && (priv->rts_gpio == 0)) -#endif - ) - { - ret = -EINVAL; - break; - } - - /* Decode baud. */ - - ret = OK; - baud = cfgetispeed(termiosp); + uart_recvchars(&priv->dev); + } - /* Decode number of bits */ + /* Get the masked LPUART status word to check and clear error flags. + * + * When wake-up from low power mode was not fast enough, UART is resumed + * too late and sometimes exactly when character was coming over UART, + * resulting to frame error. + * If error flag is not cleared, Rx DMA will be stuck. Clearing errors + * will release Rx DMA. + */ - switch (termiosp->c_cflag & CSIZE) - { - case CS5: - { - nbits = 5; - break; - } + sr = imx9_serialin(priv, IMX9_LPUART_STAT_OFFSET); - case CS6: - { - nbits = 6; - break; - } + if ((sr & (LPUART_STAT_OR | LPUART_STAT_NF | LPUART_STAT_FE)) != 0) + { + imx9_serialout(priv, IMX9_LPUART_STAT_OFFSET, + sr & (LPUART_STAT_OR | + LPUART_STAT_NF | + LPUART_STAT_FE)); + } +} +#endif - case CS7: - { - nbits = 7; - break; - } +/**************************************************************************** + * Name: up_pm_notify + * + * Description: + * Notify the driver of new power state. This callback is called after + * all drivers have had the opprivunity to prepare for the new power state. + * + * Input Parameters: + * + * cb - Returned to the driver. The driver version of the callback + * structure may include additional, driver-specific state data at + * the end of the structure. + * + * pmstate - Identifies the new PM state + * + * Returned Value: + * None - The driver already agreed to transition to the low power + * consumption state when when it returned OK to the prepare() call. + * + ****************************************************************************/ - case CS8: +#ifdef CONFIG_PM +static void up_pm_notify(struct pm_callback_s *cb, int domain, + enum pm_state_e pmstate) +{ + switch (pmstate) + { + case(PM_NORMAL): { - nbits = 8; - break; + /* Logic for PM_NORMAL goes here */ } + break; -#if defined(CS9) - case CS9: + case(PM_IDLE): { - nbits = 9; - break; + /* Logic for PM_IDLE goes here */ } + break; -#endif - default: + case(PM_STANDBY): { - ret = -EINVAL; - break; + /* Logic for PM_STANDBY goes here */ } - } - - /* Decode parity */ + break; - if ((termiosp->c_cflag & PARENB) != 0) + case(PM_SLEEP): { - parity = (termiosp->c_cflag & PARODD) ? 1 : 2; + /* Logic for PM_SLEEP goes here */ } - else - { - parity = 0; - } - - /* Decode stop bits */ - - stop2 = (termiosp->c_cflag & CSTOPB) != 0; - - /* Verify that all settings are valid before committing */ - - if (ret == OK) - { - /* Commit */ + break; - priv->baud = baud; - priv->parity = parity; - priv->bits = nbits; - priv->stopbits2 = stop2; -#ifdef CONFIG_SERIAL_OFLOWCONTROL - priv->oflow = (termiosp->c_cflag & CCTS_OFLOW) != 0; -#endif -#ifdef CONFIG_SERIAL_IFLOWCONTROL - priv->iflow = (termiosp->c_cflag & CRTS_IFLOW) != 0; -#endif - - /* effect the changes immediately - note that we do not - * implement TCSADRAIN / TCSAFLUSH - */ - - flags = spin_lock_irqsave(NULL); - imx9_disableuartint(priv, &ie); - ret = imx9_setup(dev); - - /* Restore the interrupt state */ + default: - imx_restoreuartint(priv, ie); - priv->ie = ie; - spin_unlock_irqrestore(NULL, flags); - } - } - break; -#endif /* CONFIG_SERIAL_TERMIOS */ + /* Should not get here */ - case TIOCSBRK: /* BSD compatibility: Turn break on, unconditionally */ - case TIOCCBRK: /* BSD compatibility: Turn break off, unconditionally */ - default: - { - ret = -ENOTTY; - break; - } + break; } - - return ret; } +#endif /**************************************************************************** - * Private Data + * Name: up_pm_prepare + * + * Description: + * Request the driver to prepare for a new power state. This is a warning + * that the system is about to enter into a new power state. The driver + * should begin whatever operations that may be required to enter power + * state. The driver may abort the state change mode by returning a + * non-zero value from the callback function. + * + * Input Parameters: + * + * cb - Returned to the driver. The driver version of the callback + * structure may include additional, driver-specific state data at + * the end of the structure. + * + * pmstate - Identifies the new PM state + * + * Returned Value: + * Zero - (OK) means the event was successfully processed and that the + * driver is prepared for the PM state change. + * + * Non-zero - means that the driver is not prepared to perform the tasks + * needed achieve this power setting and will cause the state + * change to be aborted. NOTE: The prepare() method will also + * be called when reverting from lower back to higher power + * consumption modes (say because another driver refused a + * lower power state change). Drivers are not permitted to + * return non-zero values when reverting back to higher power + * consumption modes! + * + * ****************************************************************************/ -/* Serial driver UART operations */ - -static const struct uart_ops_s g_uart_ops = -{ - .setup = imx9_setup, - .shutdown = imx9_shutdown, - .attach = imx9_attach, - .detach = imx9_detach, - .ioctl = imx9_ioctl, - .receive = imx9_receive, - .rxint = imx9_rxint, - .rxavailable = imx9_rxavailable, -#ifdef CONFIG_SERIAL_IFLOWCONTROL - .rxflowcontrol = NULL, -#endif - .send = imx9_send, - .txint = imx9_txint, - .txready = imx9_txready, - .txempty = imx9_txempty, -}; - -/* I/O buffers */ - -#ifdef CONFIG_IMX9_UART1 -static char g_uart1rxbuffer[CONFIG_UART1_RXBUFSIZE]; -static char g_uart1txbuffer[CONFIG_UART1_TXBUFSIZE]; - -/* This describes the state of the IMX uart1 port. */ - -static struct imx9_uart_port_s g_uart1priv = +#ifdef CONFIG_PM +static int up_pm_prepare(struct pm_callback_s *cb, int domain, + enum pm_state_e pmstate) { - .iobase = IMX9_LPUART1_BASE, - .baud = CONFIG_UART1_BAUD, - .irq_num = IMX9_IRQ_LPUART1, - .is_console = 1, -}; - -static struct uart_dev_s g_uart1port = -{ - .recv = - { - .size = CONFIG_UART1_RXBUFSIZE, - .buffer = g_uart1rxbuffer, - }, - .xmit = - { - .size = CONFIG_UART1_TXBUFSIZE, - .buffer = g_uart1txbuffer, - }, - .ops = &g_uart_ops, - .priv = &g_uart1priv, -}; + /* Logic to prepare for a reduced power state goes here. */ + return OK; +} #endif /**************************************************************************** @@ -875,9 +2701,9 @@ static struct uart_dev_s g_uart1port = * Name: arm64_earlyserialinit * * Description: - * Performs the low level UART initialization early in - * debug so that the serial console will be available - * during bootup. This must be called before arm64_serialinit. + * Performs the low level UART initialization early in debug so that the + * serial console will be available during bootup. This must be called + * before arm64_serialinit. * ****************************************************************************/ @@ -891,60 +2717,105 @@ void arm64_earlyserialinit(void) /* Enable the console UART. The other UARTs will be initialized if and * when they are first opened. */ + #ifdef CONSOLE_DEV - CONSOLE_DEV.isconsole = true; - imx9_setup(&CONSOLE_DEV); + CONSOLE_DEV.dev.isconsole = true; + imx9_setup(&CONSOLE_DEV.dev); #endif } /**************************************************************************** - * Name: up_putc + * Name: arm64_serialinit * * Description: - * Provide priority, low-level access to support OS debug - * writes + * Register serial console and serial privs. This assumes + * that imx9_earlyserialinit was called previously. * ****************************************************************************/ -int up_putc(int ch) +void arm64_serialinit(void) { - /* Check for LF */ +#ifdef CONFIG_PM + int ret; - if (ch == '\n') - { - /* Add CR */ + /* Register to receive power management callbacks */ - arm64_lowputc('\r'); - } + ret = pm_register(&g_serial_pmcb); + DEBUGASSERT(ret == OK); + UNUSED(ret); +#endif - arm64_lowputc((uint8_t)ch); - return ch; +#ifdef CONSOLE_DEV + uart_register("/dev/console", &CONSOLE_DEV.dev); +#if defined(SERIAL_HAVE_CONSOLE_DMA) + imx9_dma_setup(&CONSOLE_DEV.dev); +#endif +#endif + + /* Register all UARTs */ + +#ifdef TTYS0_DEV + uart_register("/dev/ttyS0", &TTYS0_DEV.dev); +#endif +#ifdef TTYS1_DEV + uart_register("/dev/ttyS1", &TTYS1_DEV.dev); +#endif +#ifdef TTYS2_DEV + uart_register("/dev/ttyS2", &TTYS2_DEV.dev); +#endif +#ifdef TTYS3_DEV + uart_register("/dev/ttyS3", &TTYS3_DEV.dev); +#endif +#ifdef TTYS4_DEV + uart_register("/dev/ttyS4", &TTYS4_DEV.dev); +#endif +#ifdef TTYS5_DEV + uart_register("/dev/ttyS5", &TTYS5_DEV.dev); +#endif +#ifdef TTYS6_DEV + uart_register("/dev/ttyS6", &TTYS6_DEV.dev); +#endif +#ifdef TTYS7_DEV + uart_register("/dev/ttyS7", &TTYS7_DEV.dev); +#endif } +#endif /* USE_SERIALDRIVER */ + /**************************************************************************** - * Name: arm64_serialinit + * Name: up_putc * * Description: - * Register serial console and serial ports. This assumes - * that imx_earlyserialinit was called previously. + * Provide priority, low-level access to suppriv OS debug writes * ****************************************************************************/ -void arm64_serialinit(void) +int up_putc(int ch) { - int ret; +#ifdef CONSOLE_DEV + struct imx9_uart_s *priv = (struct imx9_uart_s *)&CONSOLE_DEV; + uint32_t ie; - ret = uart_register("/dev/console", &CONSOLE_DEV); - if (ret < 0) + if (!CONSOLE_DEV.dev.isconsole) { - sinfo("error at register dev/console, ret =%d\n", ret); + return ch; } - ret = uart_register("/dev/ttyS0", &TTYS0_DEV); - if (ret < 0) + imx9_disableuartint(priv, &ie); +#endif + + /* Check for LF */ + + if (ch == '\n') { - sinfo("error at register dev/ttyS0, ret =%d\n", ret); + /* Add CR */ + + arm64_lowputc('\r'); } -} -#endif /* USE_SERIALDRIVER */ + arm64_lowputc(ch); +#ifdef CONSOLE_DEV + imx9_restoreuartint(priv, ie); +#endif + return ch; +} diff --git a/arch/arm64/src/imx9/imx9_serial.h b/arch/arm64/src/imx9/imx9_serial.h index 854c6367aee77..e9c175a8ce14d 100644 --- a/arch/arm64/src/imx9/imx9_serial.h +++ b/arch/arm64/src/imx9/imx9_serial.h @@ -27,70 +27,216 @@ #include -#include "arm64_internal.h" - /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ -/**************************************************************************** - * Public Types - ****************************************************************************/ +#if defined(CONFIG_IMX9_LPUART1) || defined(CONFIG_IMX9_LPUART2) || \ + defined(CONFIG_IMX9_LPUART3) || defined(CONFIG_IMX9_LPUART4) || \ + defined(CONFIG_IMX9_LPUART5) || defined(CONFIG_IMX9_LPUART6) || \ + defined(CONFIG_IMX9_LPUART7) || defined(CONFIG_IMX9_LPUART8) +# define HAVE_UART 1 +#endif -/**************************************************************************** - * Inline Functions - ****************************************************************************/ +/* Assume DMA is not used on the console UART */ + +#undef SERIAL_HAVE_CONSOLE_RXDMA +#undef SERIAL_HAVE_CONSOLE_TXDMA + +#if !defined(HAVE_UART) || !defined(CONFIG_ARCH_DMA) +# undef CONFIG_LPUART1_RXDMA +# undef CONFIG_LPUART1_TXDMA +# undef CONFIG_LPUART2_RXDMA +# undef CONFIG_LPUART2_TXDMA +# undef CONFIG_LPUART3_RXDMA +# undef CONFIG_LPUART3_TXDMA +# undef CONFIG_LPUART4_RXDMA +# undef CONFIG_LPUART4_TXDMA +# undef CONFIG_LPUART5_RXDMA +# undef CONFIG_LPUART5_TXDMA +# undef CONFIG_LPUART6_RXDMA +# undef CONFIG_LPUART6_TXDMA +# undef CONFIG_LPUART7_RXDMA +# undef CONFIG_LPUART7_TXDMA +# undef CONFIG_LPUART8_RXDMA +# undef CONFIG_LPUART8_TXDMA +#endif -#ifndef __ASSEMBLY__ +/* Disable the DMA configuration on all unused LPUARTs */ -/**************************************************************************** - * Public Data - ****************************************************************************/ +#ifndef CONFIG_IMX9_LPUART1 +# undef CONFIG_LPUART1_RXDMA +# undef CONFIG_LPUART1_TXDMA +#endif + +#ifndef CONFIG_IMX9_LPUART2 +# undef CONFIG_LPUART2_RXDMA +# undef CONFIG_LPUART2_TXDMA +#endif + +#ifndef CONFIG_IMX9_LPUART3 +# undef CONFIG_LPUART3_RXDMA +# undef CONFIG_LPUART3_TXDMA +#endif + +#ifndef CONFIG_IMX9_LPUART4 +# undef CONFIG_LPUART4_RXDMA +# undef CONFIG_LPUART4_TXDMA +#endif + +#ifndef CONFIG_IMX9_LPUART5 +# undef CONFIG_LPUART5_RXDMA +# undef CONFIG_LPUART5_TXDMA +#endif + +#ifndef CONFIG_IMX9_LPUART6 +# undef CONFIG_LPUART6_RXDMA +# undef CONFIG_LPUART6_TXDMA +#endif + +#ifndef CONFIG_IMX9_LPUART8 +# undef CONFIG_LPUART7_RXDMA +# undef CONFIG_LPUART7_TXDMA +#endif + +/* Is RX DMA available on any (enabled) LPUART? */ + +#undef SERIAL_HAVE_RXDMA +#if defined(CONFIG_LPUART1_RXDMA) || defined(CONFIG_LPUART2_RXDMA) || \ + defined(CONFIG_LPUART3_RXDMA) || defined(CONFIG_LPUART4_RXDMA) || \ + defined(CONFIG_LPUART5_RXDMA) || defined(CONFIG_LPUART6_RXDMA) || \ + defined(CONFIG_LPUART7_RXDMA) || defined(CONFIG_LPUART8_RXDMA) +# define SERIAL_HAVE_RXDMA 1 +#endif + +/* Is TX DMA available on any (enabled) LPUART? */ +#undef SERIAL_HAVE_TXDMA +#if defined(CONFIG_LPUART1_TXDMA) || defined(CONFIG_LPUART2_TXDMA) || \ + defined(CONFIG_LPUART3_TXDMA) || defined(CONFIG_LPUART4_TXDMA) || \ + defined(CONFIG_LPUART5_TXDMA) || defined(CONFIG_LPUART6_TXDMA) || \ + defined(CONFIG_LPUART7_TXDMA) || defined(CONFIG_LPUART8_TXDMA) +# define SERIAL_HAVE_TXDMA 1 +#endif + +/* Is RX DMA used on all (enabled) LPUARTs */ + +#define SERIAL_HAVE_ONLY_RXDMA 1 +#if defined(CONFIG_IMX9_LPUART1) && !defined(CONFIG_LPUART1_RXDMA) +# undef SERIAL_HAVE_ONLY_RXDMA +#elif defined(CONFIG_IMX9_LPUART2) && !defined(CONFIG_LPUART2_RXDMA) +# undef SERIAL_HAVE_ONLY_RXDMA +#elif defined(CONFIG_IMX9_LPUART3) && !defined(CONFIG_LPUART3_RXDMA) +# undef SERIAL_HAVE_ONLY_RXDMA +#elif defined(CONFIG_IMX9_LPUART4) && !defined(CONFIG_LPUART4_RXDMA) +# undef SERIAL_HAVE_ONLY_RXDMA +#elif defined(CONFIG_IMX9_LPUART5) && !defined(CONFIG_LPUART5_RXDMA) +# undef SERIAL_HAVE_ONLY_RXDMA +#elif defined(CONFIG_IMX9_LPUART6) && !defined(CONFIG_LPUART6_RXDMA) +# undef SERIAL_HAVE_ONLY_RXDMA +#elif defined(CONFIG_IMX9_LPUART7) && !defined(CONFIG_LPUART7_RXDMA) +# undef SERIAL_HAVE_ONLY_RXDMA +#elif defined(CONFIG_IMX9_LPUART8) && !defined(CONFIG_LPUART8_RXDMA) +# undef SERIAL_HAVE_ONLY_RXDMA +#endif + +/* Is TX DMA used on all (enabled) LPUARTs */ + +#define SERIAL_HAVE_ONLY_TXDMA 1 +#if defined(CONFIG_IMX9_LPUART1) && !defined(CONFIG_LPUART1_TXDMA) +# undef SERIAL_HAVE_ONLY_TXDMA +#elif defined(CONFIG_IMX9_LPUART2) && !defined(CONFIG_LPUART2_TXDMA) +# undef SERIAL_HAVE_ONLY_TXDMA +#elif defined(CONFIG_IMX9_LPUART3) && !defined(CONFIG_LPUART3_TXDMA) +# undef SERIAL_HAVE_ONLY_TXDMA +#elif defined(CONFIG_IMX9_LPUART4) && !defined(CONFIG_LPUART4_TXDMA) +# undef SERIAL_HAVE_ONLY_TXDMA +#elif defined(CONFIG_IMX9_LPUART5) && !defined(CONFIG_LPUART5_TXDMA) +# undef SERIAL_HAVE_ONLY_TXDMA +#elif defined(CONFIG_IMX9_LPUART6) && !defined(CONFIG_LPUART6_TXDMA) +# undef SERIAL_HAVE_ONLY_TXDMA +#elif defined(CONFIG_IMX9_LPUART7) && !defined(CONFIG_LPUART7_TXDMA) +# undef SERIAL_HAVE_ONLY_TXDMA +#elif defined(CONFIG_IMX9_LPUART8) && !defined(CONFIG_LPUART8_TXDMA) +# undef SERIAL_HAVE_ONLY_TXDMA +#endif + +#undef SERIAL_HAVE_ONLY_DMA +#if defined(SERIAL_HAVE_ONLY_RXDMA) && defined(SERIAL_HAVE_ONLY_TXDMA) +#define SERIAL_HAVE_ONLY_DMA +#endif + +/* Verify that DMA has been enabled and the DMA channel has been defined. + */ + +#if defined(SERIAL_HAVE_TXDMA) || defined(SERIAL_HAVE_RXDMA) +# ifndef CONFIG_IMX9_EDMA +# error IMXRT LPUART receive or transmit DMA requires CONFIG_IMX9_EDMA +# endif +#endif + +#if defined(SERIAL_HAVE_RXDMA) +/* Currently RS-485 support cannot be enabled when RXDMA is in use due to + * lack of testing. + */ + +# if (defined(CONFIG_LPUART1_RXDMA) && defined(CONFIG_LPUART1_RS485)) || \ + (defined(CONFIG_LPUART2_RXDMA) && defined(CONFIG_LPUART2_RS485)) || \ + (defined(CONFIG_LPUART3_RXDMA) && defined(CONFIG_LPUART3_RS485)) || \ + (defined(CONFIG_LPUART4_RXDMA) && defined(CONFIG_LPUART4_RS485)) || \ + (defined(CONFIG_LPUART5_RXDMA) && defined(CONFIG_LPUART5_RS485)) || \ + (defined(CONFIG_LPUART6_RXDMA) && defined(CONFIG_LPUART6_RS485)) || \ + (defined(CONFIG_LPUART7_RXDMA) && defined(CONFIG_LPUART7_RS485)) || \ + (defined(CONFIG_LPUART8_RXDMA) && defined(CONFIG_LPUART8_RS485)) +# error "RXDMA and RS-485 cannot be enabled at the same time for the same LPUART" +# endif +#endif /* SERIAL_HAVE_RXDMA */ + +/* Currently RS-485 support cannot be enabled when TXDMA is in use due to + * lack of testing. + */ + +# if (defined(CONFIG_LPUART1_TXDMA) && defined(CONFIG_LPUART1_RS485)) || \ + (defined(CONFIG_LPUART2_TXDMA) && defined(CONFIG_LPUART2_RS485)) || \ + (defined(CONFIG_LPUART3_TXDMA) && defined(CONFIG_LPUART3_RS485)) || \ + (defined(CONFIG_LPUART4_TXDMA) && defined(CONFIG_LPUART4_RS485)) || \ + (defined(CONFIG_LPUART5_TXDMA) && defined(CONFIG_LPUART5_RS485)) || \ + (defined(CONFIG_LPUART6_TXDMA) && defined(CONFIG_LPUART6_RS485)) || \ + (defined(CONFIG_LPUART7_TXDMA) && defined(CONFIG_LPUART7_RS485)) || \ + (defined(CONFIG_LPUART8_TXDMA) && defined(CONFIG_LPUART8_RS485)) +# error "TXDMA and RS-485 cannot be enabled at the same time for the same LPUART" +#endif /**************************************************************************** - * Public Function Prototypes + * Public Types ****************************************************************************/ /**************************************************************************** - * Name: imx_earlyserialinit - * - * Description: - * Performs the low level UART initialization early in debug so that the - * serial console will be available during bootup. This must be called - * before arm_serialinit. - * + * Inline Functions ****************************************************************************/ -#ifdef USE_EARLYSERIALINIT -void imx9_earlyserialinit(void); -#endif +#ifndef __ASSEMBLY__ /**************************************************************************** - * Name: uart_earlyserialinit - * - * Description: - * Performs the low level UART initialization early in debug so that the - * serial console will be available during bootup. This must be called - * before arm_serialinit. - * + * Public Data ****************************************************************************/ -#if defined(USE_EARLYSERIALINIT) && defined(IMX9_HAVE_UART) -void uart_earlyserialinit(void); +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern #endif /**************************************************************************** - * Name: uart_serialinit - * - * Description: - * Register the UART serial console and serial ports. This assumes that - * uart_earlyserialinit was called previously. - * + * Public Function Prototypes ****************************************************************************/ -#ifdef IMX9_HAVE_UART -void uart_serialinit(void); +#undef EXTERN +#if defined(__cplusplus) +} #endif #endif /* __ASSEMBLY__ */ -#endif /* __ARCH_ARM_SRC_IMX9_IMX_SERIAL_H */ +#endif /* __ARCH_ARM64_SRC_IMX9_IMX9_SERIAL_H */ diff --git a/boards/arm64/imx9/imx93-evk/configs/nsh/defconfig b/boards/arm64/imx9/imx93-evk/configs/nsh/defconfig index 64c7f53357551..b4ae81e0a023c 100644 --- a/boards/arm64/imx9/imx93-evk/configs/nsh/defconfig +++ b/boards/arm64/imx9/imx93-evk/configs/nsh/defconfig @@ -36,12 +36,13 @@ CONFIG_IMX9_GPIO_IRQ=y CONFIG_IMX9_LPI2C1=y CONFIG_IMX9_LPI2C_DYNTIMEO=y CONFIG_IMX9_LPI2C_DYNTIMEO_STARTSTOP=10 +CONFIG_IMX9_LPUART1=y CONFIG_IMX9_TPM3_PWM=y CONFIG_IMX9_TPM3_PWM_CHMUX=0x00000003 -CONFIG_IMX9_UART1=y CONFIG_IMX9_USBDEV_USBC1=y CONFIG_INIT_ENTRYPOINT="nsh_main" CONFIG_INTELHEX_BINARY=y +CONFIG_LPUART1_SERIAL_CONSOLE=y CONFIG_NSH_ARCHINIT=y CONFIG_NSH_BUILTIN_APPS=y CONFIG_NSH_FILEIOSIZE=512 @@ -71,4 +72,3 @@ CONFIG_SYSTEM_SYSTEM=y CONFIG_SYSTEM_TIME64=y CONFIG_TESTING_GETPRIME=y CONFIG_TESTING_OSTEST=y -CONFIG_UART1_SERIAL_CONSOLE=y diff --git a/boards/arm64/imx9/imx93-evk/include/board.h b/boards/arm64/imx9/imx93-evk/include/board.h index 6cd4c26e56143..37d46536eca7b 100644 --- a/boards/arm64/imx9/imx93-evk/include/board.h +++ b/boards/arm64/imx9/imx93-evk/include/board.h @@ -33,6 +33,11 @@ #define IOMUX_LPI2C_DEFAULT (IOMUXC_PAD_OD_ENABLE | IOMUXC_PAD_FSEL_SFAST | IOMUXC_PAD_DSE_X6) +/* UART pin muxings */ + +#define MUX_LPUART1_RX IOMUX_CFG(IOMUXC_PAD_UART1_RXD_LPUART1_RX, 0, IOMUXC_MUX_SION_ON) +#define MUX_LPUART1_TX IOMUX_CFG(IOMUXC_PAD_UART1_TXD_LPUART1_TX, IOMUXC_PAD_FSEL_SLOW | IOMUXC_PAD_DSE_X4, 0) + /* FLEXIO to PWM pin muxings */ /* EVK signals @@ -47,6 +52,8 @@ #define FLEXIO1_PWM2_MUX IOMUX_CFG(IOMUXC_PAD_GPIO_IO06_FLEXIO1_FLEXIO06, IOMUXC_PAD_FSEL_SFAST | IOMUXC_PAD_DSE_X6, 0) #define FLEXIO1_PWM3_MUX IOMUX_CFG(IOMUXC_PAD_GPIO_IO07_FLEXIO1_FLEXIO07, IOMUXC_PAD_FSEL_SFAST | IOMUXC_PAD_DSE_X6, 0) +/* LPI2Cs */ + /* TPM3 ch3 to PWM pin GPIO_IO24 muxing */ #define TPM3_PWM3_MUX IOMUX_CFG(IOMUXC_PAD_GPIO_IO24_TPM3_CH3, IOMUXC_PAD_FSEL_SFAST | IOMUXC_PAD_DSE_X6, 0)