This patchset enhances the LINFlexD UART driver and its device tree bindings to support DMA transfers, configurable clock inputs, dynamic baudrate changes, and termios features. It also includes a series of fixes and improvements to ensure reliable operation across various modes and configurations.
The changes added can be summarized as follows: 1. Fixes with respect to FIFO handling, locking, interrupt related registers and INITM mode transition. 2. Removal of the earlycon workaround, as proper FIFO handling and INITM transitions now ensure stable behavior. 3. Support for configurable stop bits and dynamic baudrate changes based on clock inputs and termios settings. 4. Optional DMA support for RX and TX paths, preventing character loss during high-throughput operations like copy-paste. Cyclic DMA is used for RX to avoid gaps between transactions.
Larisa Grigore (8): serial: linflexuart: Clean SLEEP bit in LINCR1 after suspend serial: linflexuart: Check FIFO full before writing serial: linflexuart: Correctly clear UARTSR in buffer mode serial: linflexuart: Update RXEN/TXEN outside INITM mode serial: linflexuart: Ensure FIFO is empty when entering INITM serial: linflexuart: Revert earlycon workaround serial: linflexuart: Add support for configurable stop bits serial: linflexuart: Add DMA support
Radu Pirea (5): serial: linflexuart: Fix locking in set_termios dt-bindings: serial: fsl-linflexuart: add clock input properties dt-bindings: serial: fsl-linflexuart: add dma properties serial: linflexuart: Add support for changing baudrate serial: linflexuart: Avoid stopping DMA during receive operations
.../bindings/serial/fsl,s32-linflexuart.yaml | 31 + drivers/tty/serial/fsl_linflexuart.c | 972 +++++++++++++++--- 2 files changed, 846 insertions(+), 157 deletions(-)
From: Radu Pirea radu-nicolae.pirea@nxp.com
Take the port->lock when set_termios is called, otherwise if characters are sent while IP is in init mode, the IP will hang in an uncertain state.
Fixes: 09864c1cdf5c ("tty: serial: Add linflexuart driver for S32V234") Signed-off-by: Radu Pirea radu-nicolae.pirea@nxp.com Signed-off-by: Larisa Grigore larisa.grigore@oss.nxp.com --- drivers/tty/serial/fsl_linflexuart.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/tty/serial/fsl_linflexuart.c b/drivers/tty/serial/fsl_linflexuart.c index e70a56de1fce..5a410e2d56ac 100644 --- a/drivers/tty/serial/fsl_linflexuart.c +++ b/drivers/tty/serial/fsl_linflexuart.c @@ -407,6 +407,8 @@ linflex_set_termios(struct uart_port *port, struct ktermios *termios, unsigned long cr, old_cr, cr1; unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8;
+ uart_port_lock_irqsave(port, &flags); + cr = readl(port->membase + UARTCR); old_cr = cr;
@@ -475,8 +477,6 @@ linflex_set_termios(struct uart_port *port, struct ktermios *termios, cr &= ~LINFLEXD_UARTCR_PCE; }
- uart_port_lock_irqsave(port, &flags); - port->read_status_mask = 0;
if (termios->c_iflag & INPCK)
On Mon, Feb 16, 2026 at 04:01:53PM +0100, Larisa Grigore wrote:
From: Radu Pirea radu-nicolae.pirea@nxp.com
Take the port->lock when set_termios is called, otherwise if characters are sent while IP is in init mode, the IP will hang in an uncertain state.
According to patch, you move it before read(UARTCR). can explain why hang?
Frank
Fixes: 09864c1cdf5c ("tty: serial: Add linflexuart driver for S32V234") Signed-off-by: Radu Pirea radu-nicolae.pirea@nxp.com Signed-off-by: Larisa Grigore larisa.grigore@oss.nxp.com
drivers/tty/serial/fsl_linflexuart.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/tty/serial/fsl_linflexuart.c b/drivers/tty/serial/fsl_linflexuart.c index e70a56de1fce..5a410e2d56ac 100644 --- a/drivers/tty/serial/fsl_linflexuart.c +++ b/drivers/tty/serial/fsl_linflexuart.c @@ -407,6 +407,8 @@ linflex_set_termios(struct uart_port *port, struct ktermios *termios, unsigned long cr, old_cr, cr1; unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8;
- uart_port_lock_irqsave(port, &flags);
- cr = readl(port->membase + UARTCR); old_cr = cr;
@@ -475,8 +477,6 @@ linflex_set_termios(struct uart_port *port, struct ktermios *termios, cr &= ~LINFLEXD_UARTCR_PCE; }
uart_port_lock_irqsave(port, &flags);
port->read_status_mask = 0;
if (termios->c_iflag & INPCK)
-- 2.47.0
On 2/16/2026 10:16 PM, Frank Li wrote:
On Mon, Feb 16, 2026 at 04:01:53PM +0100, Larisa Grigore wrote:
From: Radu Pirea radu-nicolae.pirea@nxp.com
Take the port->lock when set_termios is called, otherwise if characters are sent while IP is in init mode, the IP will hang in an uncertain state.
According to patch, you move it before read(UARTCR). can explain why hang?
Frank
Hello Frank,
Thanks for the review! This change was made to not let anyone send characters (for example calling `linflex_console_putchar`) while LINFlexD is entering INIT mode. The INIT mode is entered when setting LINFLEXD_LINCR1_INIT in LINCR1. UARTCR should also be protected with a lock since it can be modified from different other places. I will update the commit description.
Regards, Larisa
Fixes: 09864c1cdf5c ("tty: serial: Add linflexuart driver for S32V234") Signed-off-by: Radu Pirea radu-nicolae.pirea@nxp.com Signed-off-by: Larisa Grigore larisa.grigore@oss.nxp.com
drivers/tty/serial/fsl_linflexuart.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/tty/serial/fsl_linflexuart.c b/drivers/tty/serial/fsl_linflexuart.c index e70a56de1fce..5a410e2d56ac 100644 --- a/drivers/tty/serial/fsl_linflexuart.c +++ b/drivers/tty/serial/fsl_linflexuart.c @@ -407,6 +407,8 @@ linflex_set_termios(struct uart_port *port, struct ktermios *termios, unsigned long cr, old_cr, cr1; unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8;
- uart_port_lock_irqsave(port, &flags);
- cr = readl(port->membase + UARTCR); old_cr = cr;
@@ -475,8 +477,6 @@ linflex_set_termios(struct uart_port *port, struct ktermios *termios, cr &= ~LINFLEXD_UARTCR_PCE; }
uart_port_lock_irqsave(port, &flags);
port->read_status_mask = 0;
if (termios->c_iflag & INPCK)
-- 2.47.0
When coming back from reset, we need to re-initialize LINCR1 register. SLEEP bit should be cleared, otherwise we can't enter INITM mode.
Fixes: 09864c1cdf5c ("tty: serial: Add linflexuart driver for S32V234") Signed-off-by: Larisa Grigore larisa.grigore@oss.nxp.com --- drivers/tty/serial/fsl_linflexuart.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/drivers/tty/serial/fsl_linflexuart.c b/drivers/tty/serial/fsl_linflexuart.c index 5a410e2d56ac..016011fd8760 100644 --- a/drivers/tty/serial/fsl_linflexuart.c +++ b/drivers/tty/serial/fsl_linflexuart.c @@ -413,8 +413,7 @@ linflex_set_termios(struct uart_port *port, struct ktermios *termios, old_cr = cr;
/* Enter initialization mode by setting INIT bit */ - cr1 = readl(port->membase + LINCR1); - cr1 |= LINFLEXD_LINCR1_INIT; + cr1 = LINFLEXD_LINCR1_INIT | LINFLEXD_LINCR1_MME; writel(cr1, port->membase + LINCR1);
/* wait for init mode entry */
On Mon, Feb 16, 2026 at 04:01:54PM +0100, Larisa Grigore wrote:
When coming back from reset, we need to re-initialize LINCR1 register. SLEEP bit should be cleared, otherwise we can't enter INITM mode.
serial: linflexuart: Clean SLEEP bit in LINCR1 at linflex_set_termios()
Re-initialize LINCR1 register (Clear the SLEEP bit) at linflex_set_termios(), otherwise the controller cannot enter INITM mode after suspend/resume.
Frank
Fixes: 09864c1cdf5c ("tty: serial: Add linflexuart driver for S32V234") Signed-off-by: Larisa Grigore larisa.grigore@oss.nxp.com
drivers/tty/serial/fsl_linflexuart.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/drivers/tty/serial/fsl_linflexuart.c b/drivers/tty/serial/fsl_linflexuart.c index 5a410e2d56ac..016011fd8760 100644 --- a/drivers/tty/serial/fsl_linflexuart.c +++ b/drivers/tty/serial/fsl_linflexuart.c @@ -413,8 +413,7 @@ linflex_set_termios(struct uart_port *port, struct ktermios *termios, old_cr = cr;
/* Enter initialization mode by setting INIT bit */
- cr1 = readl(port->membase + LINCR1);
- cr1 |= LINFLEXD_LINCR1_INIT;
cr1 = LINFLEXD_LINCR1_INIT | LINFLEXD_LINCR1_MME; writel(cr1, port->membase + LINCR1);
/* wait for init mode entry */
-- 2.47.0
On 2/16/2026 10:22 PM, Frank Li wrote:
On Mon, Feb 16, 2026 at 04:01:54PM +0100, Larisa Grigore wrote:
When coming back from reset, we need to re-initialize LINCR1 register. SLEEP bit should be cleared, otherwise we can't enter INITM mode.
serial: linflexuart: Clean SLEEP bit in LINCR1 at linflex_set_termios()
Re-initialize LINCR1 register (Clear the SLEEP bit) at linflex_set_termios(), otherwise the controller cannot enter INITM mode after suspend/resume.
Frank
Thanks!
Fixes: 09864c1cdf5c ("tty: serial: Add linflexuart driver for S32V234") Signed-off-by: Larisa Grigore larisa.grigore@oss.nxp.com
drivers/tty/serial/fsl_linflexuart.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/drivers/tty/serial/fsl_linflexuart.c b/drivers/tty/serial/fsl_linflexuart.c index 5a410e2d56ac..016011fd8760 100644 --- a/drivers/tty/serial/fsl_linflexuart.c +++ b/drivers/tty/serial/fsl_linflexuart.c @@ -413,8 +413,7 @@ linflex_set_termios(struct uart_port *port, struct ktermios *termios, old_cr = cr;
/* Enter initialization mode by setting INIT bit */
- cr1 = readl(port->membase + LINCR1);
- cr1 |= LINFLEXD_LINCR1_INIT;
cr1 = LINFLEXD_LINCR1_INIT | LINFLEXD_LINCR1_MME; writel(cr1, port->membase + LINCR1);
/* wait for init mode entry */
-- 2.47.0
In FIFO mode, the transmitter FIFO should be checked to ensure it is not full before writing a character.
Fixes: 09864c1cdf5c ("tty: serial: Add linflexuart driver for S32V234") Signed-off-by: Larisa Grigore larisa.grigore@oss.nxp.com --- drivers/tty/serial/fsl_linflexuart.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-)
diff --git a/drivers/tty/serial/fsl_linflexuart.c b/drivers/tty/serial/fsl_linflexuart.c index 016011fd8760..9111e7af62ea 100644 --- a/drivers/tty/serial/fsl_linflexuart.c +++ b/drivers/tty/serial/fsl_linflexuart.c @@ -555,22 +555,24 @@ static struct uart_port *linflex_ports[UART_NR]; static void linflex_console_putchar(struct uart_port *port, unsigned char ch) { unsigned long cr; + bool fifo_mode;
cr = readl(port->membase + UARTCR); + fifo_mode = cr & LINFLEXD_UARTCR_TFBM; + + if (fifo_mode) + while (readl(port->membase + UARTSR) & + LINFLEXD_UARTSR_DTFTFF) + ;
writeb(ch, port->membase + BDRL);
- if (!(cr & LINFLEXD_UARTCR_TFBM)) + if (!fifo_mode) { while ((readl(port->membase + UARTSR) & LINFLEXD_UARTSR_DTFTFF) != LINFLEXD_UARTSR_DTFTFF) ; - else - while (readl(port->membase + UARTSR) & - LINFLEXD_UARTSR_DTFTFF) - ;
- if (!(cr & LINFLEXD_UARTCR_TFBM)) { writel((readl(port->membase + UARTSR) | LINFLEXD_UARTSR_DTFTFF), port->membase + UARTSR);
The TX interrupt handler should not clear RX-related fields in UARTSR, and vice versa. The handler checks UARTSR.DRFRFE before invoking linflex_rxint, and UARTSR.DTFTFF before invoking linflex_txint. Incorrectly clearing these bits may cause the interrupt handler to miss characters. Same applies to linflex_console_putchar which should clear only UARTSR.DTFTFF.
Fixes: 09864c1cdf5c ("tty: serial: Add linflexuart driver for S32V234") Signed-off-by: Larisa Grigore larisa.grigore@oss.nxp.com --- drivers/tty/serial/fsl_linflexuart.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/drivers/tty/serial/fsl_linflexuart.c b/drivers/tty/serial/fsl_linflexuart.c index 9111e7af62ea..a48240b0a5f2 100644 --- a/drivers/tty/serial/fsl_linflexuart.c +++ b/drivers/tty/serial/fsl_linflexuart.c @@ -169,7 +169,7 @@ static void linflex_put_char(struct uart_port *sport, unsigned char c) LINFLEXD_UARTSR_DTFTFF) ;
- writel(status | LINFLEXD_UARTSR_DTFTFF, sport->membase + UARTSR); + writel(LINFLEXD_UARTSR_DTFTFF, sport->membase + UARTSR); }
static inline void linflex_transmit_buffer(struct uart_port *sport) @@ -255,7 +255,8 @@ static irqreturn_t linflex_rxint(int irq, void *dev_id) sport->icount.parity++; }
- writel(status, sport->membase + UARTSR); + + writel(~(u32)LINFLEXD_UARTSR_DTFTFF, sport->membase + UARTSR); status = readl(sport->membase + UARTSR);
if (brk) { @@ -573,9 +574,7 @@ static void linflex_console_putchar(struct uart_port *port, unsigned char ch) != LINFLEXD_UARTSR_DTFTFF) ;
- writel((readl(port->membase + UARTSR) | - LINFLEXD_UARTSR_DTFTFF), - port->membase + UARTSR); + writel(LINFLEXD_UARTSR_DTFTFF, port->membase + UARTSR); } }
Reception and transmission should not be enabled or disabled while in INITM mode. Although the manual does not explicitly forbid modifying RXEN/TXEN during INITM, this mode is intended for other types of settings.
Fixes: 09864c1cdf5c ("tty: serial: Add linflexuart driver for S32V234") Signed-off-by: Larisa Grigore larisa.grigore@oss.nxp.com --- drivers/tty/serial/fsl_linflexuart.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/drivers/tty/serial/fsl_linflexuart.c b/drivers/tty/serial/fsl_linflexuart.c index a48240b0a5f2..768b3c67a614 100644 --- a/drivers/tty/serial/fsl_linflexuart.c +++ b/drivers/tty/serial/fsl_linflexuart.c @@ -350,8 +350,7 @@ static void linflex_setup_watermark(struct uart_port *sport) /* set UART bit to allow writing other bits */ writel(LINFLEXD_UARTCR_UART, sport->membase + UARTCR);
- cr = (LINFLEXD_UARTCR_RXEN | LINFLEXD_UARTCR_TXEN | - LINFLEXD_UARTCR_WL0 | LINFLEXD_UARTCR_UART); + cr = (LINFLEXD_UARTCR_WL0 | LINFLEXD_UARTCR_UART);
writel(cr, sport->membase + UARTCR);
@@ -359,6 +358,9 @@ static void linflex_setup_watermark(struct uart_port *sport)
writel(cr1, sport->membase + LINCR1);
+ cr |= (LINFLEXD_UARTCR_RXEN | LINFLEXD_UARTCR_TXEN); + writel(cr, sport->membase + UARTCR); + ier = readl(sport->membase + LINIER); ier |= LINFLEXD_LINIER_DRIE; ier |= LINFLEXD_LINIER_DTIE;
In FIFO mode, wait until UARTCR.TDFL_TFC(number Tx FIFO) entries reach 0 before entering INITM mode. Failing to do so may lead to undefined behavior, such as: - corrupted characters being printed. - the device is not able to receive or transmit any character.
In linflex_set_termios, transmission and reception should be disabled before entering INITM mode, as already done in linflex_setup_watermark.
This patch corrects the behavior that was previously addressed by the earlycon workaround, making that workaround no longer necessary. The next patch will remove it.
Fixes: 09864c1cdf5c ("tty: serial: Add linflexuart driver for S32V234") Signed-off-by: Larisa Grigore larisa.grigore@oss.nxp.com --- drivers/tty/serial/fsl_linflexuart.c | 45 ++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 3 deletions(-)
diff --git a/drivers/tty/serial/fsl_linflexuart.c b/drivers/tty/serial/fsl_linflexuart.c index 768b3c67a614..c1d069dc8089 100644 --- a/drivers/tty/serial/fsl_linflexuart.c +++ b/drivers/tty/serial/fsl_linflexuart.c @@ -3,7 +3,7 @@ * Freescale LINFlexD UART serial port driver * * Copyright 2012-2016 Freescale Semiconductor, Inc. - * Copyright 2017-2019 NXP + * Copyright 2017-2019, 2021 NXP */
#include <linux/console.h> @@ -74,6 +74,17 @@
#define LINFLEXD_UARTCR_ROSE BIT(23)
+#define LINFLEXD_UARTCR_RDFLRFC_OFFSET 10 +#define LINFLEXD_UARTCR_RDFLRFC_MASK (0x7 << LINFLEXD_UARTCR_RDFLRFC_OFFSET) +#define LINFLEXD_UARTCR_RDFLRFC(uartcr) (((uartcr) \ + & LINFLEXD_UARTCR_RDFLRFC_MASK) >> \ + LINFLEXD_UARTCR_RDFLRFC_OFFSET) +#define LINFLEXD_UARTCR_TDFLTFC_OFFSET 13 +#define LINFLEXD_UARTCR_TDFLTFC_MASK (0x7 << LINFLEXD_UARTCR_TDFLTFC_OFFSET) +#define LINFLEXD_UARTCR_TDFLTFC(uartcr) (((uartcr) \ + & LINFLEXD_UARTCR_TDFLTFC_MASK) >> \ + LINFLEXD_UARTCR_TDFLTFC_OFFSET) + #define LINFLEXD_UARTCR_RFBM BIT(9) #define LINFLEXD_UARTCR_TFBM BIT(8) #define LINFLEXD_UARTCR_WL1 BIT(7) @@ -140,6 +151,17 @@ static struct { } earlycon_buf; #endif
+static inline void linflex_wait_tx_fifo_empty(struct uart_port *port) +{ + unsigned long cr = readl(port->membase + UARTCR); + + if (!(cr & LINFLEXD_UARTCR_TFBM)) + return; + + while (LINFLEXD_UARTCR_TDFLTFC(readl(port->membase + UARTCR))) + ; +} + static void linflex_stop_tx(struct uart_port *port) { unsigned long ier; @@ -326,6 +348,11 @@ static void linflex_setup_watermark(struct uart_port *sport) cr &= ~(LINFLEXD_UARTCR_RXEN | LINFLEXD_UARTCR_TXEN); writel(cr, sport->membase + UARTCR);
+ /* In FIFO mode, we should make sure the fifo is empty + * before entering INITM. + */ + linflex_wait_tx_fifo_empty(sport); + /* Enter initialization mode by setting INIT bit */
/* set the Linflex in master mode and activate by-pass filter */ @@ -412,8 +439,17 @@ linflex_set_termios(struct uart_port *port, struct ktermios *termios,
uart_port_lock_irqsave(port, &flags);
- cr = readl(port->membase + UARTCR); - old_cr = cr; + old_cr = readl(port->membase + UARTCR) & + ~(LINFLEXD_UARTCR_RXEN | LINFLEXD_UARTCR_TXEN); + cr = old_cr; + + /* In FIFO mode, we should make sure the fifo is empty + * before entering INITM. + */ + linflex_wait_tx_fifo_empty(port); + + /* disable transmit and receive */ + writel(old_cr, port->membase + UARTCR);
/* Enter initialization mode by setting INIT bit */ cr1 = LINFLEXD_LINCR1_INIT | LINFLEXD_LINCR1_MME; @@ -510,6 +546,9 @@ linflex_set_termios(struct uart_port *port, struct ktermios *termios,
writel(cr1, port->membase + LINCR1);
+ cr |= (LINFLEXD_UARTCR_TXEN) | (LINFLEXD_UARTCR_RXEN); + writel(cr, port->membase + UARTCR); + uart_port_unlock_irqrestore(port, flags); }
The workaround is no longer needed, as we now wait for the TX FIFO to be empty before entering INITM mode. This ensures proper behavior without requiring the previous earlycon workaround added in commit 09864c1cdf5c ("tty: serial: Add linflexuart driver for S32V234").
Fixes: 09864c1cdf5c ("tty: serial: Add linflexuart driver for S32V234") Signed-off-by: Larisa Grigore larisa.grigore@oss.nxp.com --- drivers/tty/serial/fsl_linflexuart.c | 87 +--------------------------- 1 file changed, 1 insertion(+), 86 deletions(-)
diff --git a/drivers/tty/serial/fsl_linflexuart.c b/drivers/tty/serial/fsl_linflexuart.c index c1d069dc8089..fb5f325416c0 100644 --- a/drivers/tty/serial/fsl_linflexuart.c +++ b/drivers/tty/serial/fsl_linflexuart.c @@ -141,14 +141,6 @@ MODULE_DEVICE_TABLE(of, linflex_dt_ids);
#ifdef CONFIG_SERIAL_FSL_LINFLEXUART_CONSOLE static struct uart_port *earlycon_port; -static bool linflex_earlycon_same_instance; -static DEFINE_SPINLOCK(init_lock); -static bool during_init; - -static struct { - char *content; - unsigned int len, cap; -} earlycon_buf; #endif
static inline void linflex_wait_tx_fifo_empty(struct uart_port *port) @@ -619,48 +611,6 @@ static void linflex_console_putchar(struct uart_port *port, unsigned char ch) } }
-static void linflex_earlycon_putchar(struct uart_port *port, unsigned char ch) -{ - unsigned long flags; - char *ret; - - if (!linflex_earlycon_same_instance) { - linflex_console_putchar(port, ch); - return; - } - - spin_lock_irqsave(&init_lock, flags); - if (!during_init) - goto outside_init; - - if (earlycon_buf.len >= 1 << CONFIG_LOG_BUF_SHIFT) - goto init_release; - - if (!earlycon_buf.cap) { - earlycon_buf.content = kmalloc(EARLYCON_BUFFER_INITIAL_CAP, - GFP_ATOMIC); - earlycon_buf.cap = earlycon_buf.content ? - EARLYCON_BUFFER_INITIAL_CAP : 0; - } else if (earlycon_buf.len == earlycon_buf.cap) { - ret = krealloc(earlycon_buf.content, earlycon_buf.cap << 1, - GFP_ATOMIC); - if (ret) { - earlycon_buf.content = ret; - earlycon_buf.cap <<= 1; - } - } - - if (earlycon_buf.len < earlycon_buf.cap) - earlycon_buf.content[earlycon_buf.len++] = ch; - - goto init_release; - -outside_init: - linflex_console_putchar(port, ch); -init_release: - spin_unlock_irqrestore(&init_lock, flags); -} - static void linflex_string_write(struct uart_port *sport, const char *s, unsigned int count) { @@ -739,8 +689,6 @@ static int __init linflex_console_setup(struct console *co, char *options) int parity = 'n'; int flow = 'n'; int ret; - int i; - unsigned long flags; /* * check whether an invalid uart number has been specified, and * if so, search for the first available port that does have @@ -758,43 +706,10 @@ static int __init linflex_console_setup(struct console *co, char *options) else linflex_console_get_options(sport, &parity, &bits);
- if (earlycon_port && sport->mapbase == earlycon_port->mapbase) { - linflex_earlycon_same_instance = true; - - spin_lock_irqsave(&init_lock, flags); - during_init = true; - spin_unlock_irqrestore(&init_lock, flags); - - /* Workaround for character loss or output of many invalid - * characters, when INIT mode is entered shortly after a - * character has just been printed. - */ - udelay(PREINIT_DELAY); - } - linflex_setup_watermark(sport);
ret = uart_set_options(sport, co, baud, parity, bits, flow);
- if (!linflex_earlycon_same_instance) - goto done; - - spin_lock_irqsave(&init_lock, flags); - - /* Emptying buffer */ - if (earlycon_buf.len) { - for (i = 0; i < earlycon_buf.len; i++) - linflex_console_putchar(earlycon_port, - earlycon_buf.content[i]); - - kfree(earlycon_buf.content); - earlycon_buf.len = 0; - } - - during_init = false; - spin_unlock_irqrestore(&init_lock, flags); - -done: return ret; }
@@ -814,7 +729,7 @@ static void linflex_earlycon_write(struct console *con, const char *s, { struct earlycon_device *dev = con->data;
- uart_console_write(&dev->port, s, n, linflex_earlycon_putchar); + uart_console_write(&dev->port, s, n, linflex_console_putchar); }
static int __init linflex_early_console_setup(struct earlycon_device *device,
From: Radu Pirea radu-nicolae.pirea@nxp.com
Add optional support for the two clock inputs used by the LINFlexD UART controller: - "lin": LIN_BAUD_CLK - "ipg": LINFLEXD_CLK
The clock inputs are kept optional to maintain compatibility with the S32V234 platform.
Signed-off-by: Radu Pirea radu-nicolae.pirea@nxp.com Co-developed-by: Larisa Grigore larisa.grigore@oss.nxp.com Signed-off-by: Larisa Grigore larisa.grigore@oss.nxp.com --- .../bindings/serial/fsl,s32-linflexuart.yaml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+)
diff --git a/Documentation/devicetree/bindings/serial/fsl,s32-linflexuart.yaml b/Documentation/devicetree/bindings/serial/fsl,s32-linflexuart.yaml index 4171f524a928..885f0b1b3492 100644 --- a/Documentation/devicetree/bindings/serial/fsl,s32-linflexuart.yaml +++ b/Documentation/devicetree/bindings/serial/fsl,s32-linflexuart.yaml @@ -34,6 +34,14 @@ properties: interrupts: maxItems: 1
+ clocks: + maxItems: 2 + + clock-names: + items: + - const: lin + - const: ipg + required: - compatible - reg @@ -48,3 +56,13 @@ examples: reg = <0x40053000 0x1000>; interrupts = <0 59 4>; }; + + - | + serial@401c8000 { + compatible = "nxp,s32g2-linflexuart", + "fsl,s32v234-linflexuart"; + reg = <0x401C8000 0x3000>; + interrupts = <0 82 1>; + clocks = <&clks 14>, <&clks 13>; + clock-names = "lin", "ipg"; + };
On 16/02/2026 16:02, Larisa Grigore wrote:
From: Radu Pirea radu-nicolae.pirea@nxp.com
Add optional support for the two clock inputs used by the LINFlexD UART controller:
- "lin": LIN_BAUD_CLK
- "ipg": LINFLEXD_CLK
The clock inputs are kept optional to maintain compatibility with the S32V234 platform.
Does S32V234 have the clocks? I don't understand the "maintain compatibility" in this context. Either you have or you have not clocks, which should be expressed in schema (: false, see example schema).
Signed-off-by: Radu Pirea radu-nicolae.pirea@nxp.com Co-developed-by: Larisa Grigore larisa.grigore@oss.nxp.com Signed-off-by: Larisa Grigore larisa.grigore@oss.nxp.com
.../bindings/serial/fsl,s32-linflexuart.yaml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+)
diff --git a/Documentation/devicetree/bindings/serial/fsl,s32-linflexuart.yaml b/Documentation/devicetree/bindings/serial/fsl,s32-linflexuart.yaml index 4171f524a928..885f0b1b3492 100644 --- a/Documentation/devicetree/bindings/serial/fsl,s32-linflexuart.yaml +++ b/Documentation/devicetree/bindings/serial/fsl,s32-linflexuart.yaml @@ -34,6 +34,14 @@ properties: interrupts: maxItems: 1
- clocks:
- maxItems: 2
- clock-names:
- items:
- const: lin- const: ipgrequired:
- compatible
- reg
@@ -48,3 +56,13 @@ examples: reg = <0x40053000 0x1000>; interrupts = <0 59 4>; };
- |
- serial@401c8000 {
compatible = "nxp,s32g2-linflexuart","fsl,s32v234-linflexuart";reg = <0x401C8000 0x3000>;interrupts = <0 82 1>;clocks = <&clks 14>, <&clks 13>;clock-names = "lin", "ipg";
Just add the clocks to existing example. No need for new example for each new property.
- };
Best regards, Krzysztof
The team called (Reposolsa Recovery) quickly took action, filing my case and working tirelessly on my behalf. Within just four days, I received the surprising news that my 40,000 CAD had been successfully refunded and deposited back into my bank account. I was overjoyed and relieved to see the money returned, especially after the stressful experience. Thanks to (Reposolsa Recovery) professionalism and dedication, they were able to recover my funds. This experience taught me an important lesson about being cautious with online investments and the importance of seeking expert help when dealing with scams. I am truly grateful to (Reposolsa Recovery) to reach out to the recovery team here are the inform.
EMAIL: reposolsarecovery@email.com WHATSAPP: +12092944699 WEBSITE: https://reposolsa.com Reposolsa Recovery
HOW I RECOVER MY $250,000 FROM CRYPTO SCAM USA.
I’m one of the fortunate individuals who ventured into the cryptocurrency market with hopes of building a nest egg for retirement. Sadly, I invested in a fraudulent platform that ended up scamming me out of approximately $250,000, not including the promised profits. I lost everything, including funds borrowed from friends in my investment quest. Just when I thought all was lost, I discovered the cryptocurrency recovery experts at JAYWEB RECOVERY EXPERT. They successfully helped me recover my funds within 72 hours, and I was even able to track down the scammers. I am incredibly thankful for their assistance and strongly recommend their services to anyone facing a similar situation.
Reach out to them today and turn your situation around!
CONTACT: JAYWEB RECOVERY EXPERT WhatsApp: +1 (305) 452-9075 Email: jay.webb.hack@mail.com Website:https://jaywebbhack.wixsite.com/jayweb-recovery-spec
What you need is the best recovery expert. Wireless Web Recovery can help you get out from under your crypto theft methods. The bitcoin investing industry is full of scammers, and despite people's best efforts to make enormous gains, they regularly end up losing money. No one can defeat these people's cunning schemes because of their intelligence. They typically offer contact agreements and put you in a group with others who have already made investments to tempt you to invest your hard-earned money. I invested my money as advised, then a few days later I discovered that IDMining had taken it. I appreciate Wireless Web Recovery assistance in helping me reclaim my BTC from these cyber crooks. I firmly guarantee that using Wireless Web Recovery service to retrieve your stolen BTC back will be safe. Talk with a representative today on: Email: info@wirelesswebrecovery.com Website: wirelesswebrecovery.com Whatsapp: +447549958672
Hi My name is Mona. I just want to share my experience with the world on how I got my love back and saved my marriage… I was married for 7 years with 4 kids and we lived happily until things started getting ugly and we had fights and arguments almost every time… it got worse at a point that he filed for divorce… I tried my best to make him change his mind & stay with me cause I loved him with all my heart and didn’t want to lose him but everything just didn’t work out… he moved out of the house and still went ahead to file for divorce… I pleaded and tried everything but still nothing worked. The breakthrough came when someone introduced me to this wonderful, great spell caster Dr Zuma, who eventually helped me out… I have never been a fan of things like this but just decided to try reluctantly cause I was desperate and left with no choice… He did special prayers and cast a love spell on him. Within 24hours he called me and was sorry for all the emotional trauma he had cost me, he moved back to the house and we continue to live happily, the kids are happy too and we are expecting our fourth child… I have introduced him to a lot of couples with relationship problems across the world and they have had good news… Just thought I should share my experience cause I strongly believe someone out there needs it… You can contact him on spiritualherbalisthealing@gmail.com visit https://spiritualwork.mozellosite.com
On 2/16/2026 5:10 PM, Krzysztof Kozlowski wrote:
On 16/02/2026 16:02, Larisa Grigore wrote:
From: Radu Pirea radu-nicolae.pirea@nxp.com
Add optional support for the two clock inputs used by the LINFlexD UART controller:
- "lin": LIN_BAUD_CLK
- "ipg": LINFLEXD_CLK
The clock inputs are kept optional to maintain compatibility with the S32V234 platform.
Does S32V234 have the clocks? I don't understand the "maintain compatibility" in this context. Either you have or you have not clocks, which should be expressed in schema (: false, see example schema).
Hello Krzysztof,
Thanks for pointing this out! I will update both the schema and the commit description. S32V234 does not expose these clocks in its device tree—on this platform the LINFlexD clocks are set up and enabled by U‑Boot, so they are not available to the kernel. The changes in this patch are intended specifically for S32G2/G3, where the clocks are provided in the DT and required by the driver.
Signed-off-by: Radu Pirea radu-nicolae.pirea@nxp.com Co-developed-by: Larisa Grigore larisa.grigore@oss.nxp.com Signed-off-by: Larisa Grigore larisa.grigore@oss.nxp.com
.../bindings/serial/fsl,s32-linflexuart.yaml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+)
diff --git a/Documentation/devicetree/bindings/serial/fsl,s32-linflexuart.yaml b/Documentation/devicetree/bindings/serial/fsl,s32-linflexuart.yaml index 4171f524a928..885f0b1b3492 100644 --- a/Documentation/devicetree/bindings/serial/fsl,s32-linflexuart.yaml +++ b/Documentation/devicetree/bindings/serial/fsl,s32-linflexuart.yaml @@ -34,6 +34,14 @@ properties: interrupts: maxItems: 1
- clocks:
- maxItems: 2
- clock-names:
- items:
- const: lin- const: ipg- required:
- compatible
- reg
@@ -48,3 +56,13 @@ examples: reg = <0x40053000 0x1000>; interrupts = <0 59 4>; };
- |
- serial@401c8000 {
compatible = "nxp,s32g2-linflexuart","fsl,s32v234-linflexuart";reg = <0x401C8000 0x3000>;interrupts = <0 82 1>;clocks = <&clks 14>, <&clks 13>;clock-names = "lin", "ipg";Just add the clocks to existing example. No need for new example for each new property.
- };
The existing node refers to S32V234 which does not expose any clock properties in its device tree. Because of this, I couldn’t extend that example with clocks and clock-names. The additional example is there only to illustrate the S32G2 case, where the clocks are required and actually present in the device tree. Should I remove it?
Best regards, Larisa
Best regards, Krzysztof
On 18/02/2026 14:26, Larisa Ileana Grigore wrote:
On 2/16/2026 5:10 PM, Krzysztof Kozlowski wrote:
On 16/02/2026 16:02, Larisa Grigore wrote:
From: Radu Pirea radu-nicolae.pirea@nxp.com
Add optional support for the two clock inputs used by the LINFlexD UART controller:
- "lin": LIN_BAUD_CLK
- "ipg": LINFLEXD_CLK
The clock inputs are kept optional to maintain compatibility with the S32V234 platform.
Does S32V234 have the clocks? I don't understand the "maintain compatibility" in this context. Either you have or you have not clocks, which should be expressed in schema (: false, see example schema).
Hello Krzysztof,
Thanks for pointing this out! I will update both the schema and the commit description. S32V234 does not expose these clocks in its device tree—on this platform the LINFlexD clocks are set up and enabled by U‑Boot, so they are not available to the kernel.
So there are clocks. DTS is being used by bootloader, so how bootloader is going to set up clocks for S32V234 if no one provides them?
This looks like buggy/incomplete approach, although I understand that the original binding had the issue.
The changes in this patch are intended specifically for S32G2/G3, where the clocks are provided in the DT and required by the driver.
Signed-off-by: Radu Pirea radu-nicolae.pirea@nxp.com Co-developed-by: Larisa Grigore larisa.grigore@oss.nxp.com Signed-off-by: Larisa Grigore larisa.grigore@oss.nxp.com
.../bindings/serial/fsl,s32-linflexuart.yaml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+)
diff --git a/Documentation/devicetree/bindings/serial/fsl,s32-linflexuart.yaml b/Documentation/devicetree/bindings/serial/fsl,s32-linflexuart.yaml index 4171f524a928..885f0b1b3492 100644 --- a/Documentation/devicetree/bindings/serial/fsl,s32-linflexuart.yaml +++ b/Documentation/devicetree/bindings/serial/fsl,s32-linflexuart.yaml @@ -34,6 +34,14 @@ properties: interrupts: maxItems: 1
- clocks:
- maxItems: 2
- clock-names:
- items:
- const: lin- const: ipg- required:
- compatible
- reg
@@ -48,3 +56,13 @@ examples: reg = <0x40053000 0x1000>; interrupts = <0 59 4>; };
- |
- serial@401c8000 {
compatible = "nxp,s32g2-linflexuart","fsl,s32v234-linflexuart";reg = <0x401C8000 0x3000>;interrupts = <0 82 1>;clocks = <&clks 14>, <&clks 13>;clock-names = "lin", "ipg";Just add the clocks to existing example. No need for new example for each new property.
- };
The existing node refers to S32V234 which does not expose any clock properties in its device tree. Because of this, I couldn’t extend that
You just said S32V234 has clocks...
example with clocks and clock-names. The additional example is there only to illustrate the S32G2 case, where the clocks are required and actually present in the device tree. Should I remove it?
Best regards, Larisa
Best regards, Krzysztof
Best regards, Krzysztof
On 2/18/2026 3:29 PM, Krzysztof Kozlowski wrote:
On 18/02/2026 14:26, Larisa Ileana Grigore wrote:
On 2/16/2026 5:10 PM, Krzysztof Kozlowski wrote:
On 16/02/2026 16:02, Larisa Grigore wrote:
From: Radu Pirea radu-nicolae.pirea@nxp.com
Add optional support for the two clock inputs used by the LINFlexD UART controller:
- "lin": LIN_BAUD_CLK
- "ipg": LINFLEXD_CLK
The clock inputs are kept optional to maintain compatibility with the S32V234 platform.
Does S32V234 have the clocks? I don't understand the "maintain compatibility" in this context. Either you have or you have not clocks, which should be expressed in schema (: false, see example schema).
Hello Krzysztof,
Thanks for pointing this out! I will update both the schema and the commit description. S32V234 does not expose these clocks in its device tree—on this platform the LINFlexD clocks are set up and enabled by U‑Boot, so they are not available to the kernel.
So there are clocks. DTS is being used by bootloader, so how bootloader is going to set up clocks for S32V234 if no one provides them?
This looks like buggy/incomplete approach, although I understand that the original binding had the issue.
Indeed, I also believe the binding is not fully accurate on S32V234. As I mentioned earlier, the LINFlexD clocks are not managed by Linux on this platform. They, along with several other clocks, are usually initialized by U‑Boot, and Linux does not handle them. That is likely the reason they were omitted from the S32V234 binding. I’m willing to correct this for the S32V234 compatible by making that property optional, so we don’t break compatibility. For S32G, I would prefer to avoid repeating the same oversight we had on S32V234 and make the property required, since the IP, as you correctly pointed out, does have dedicated clock inputs.
How would you approach this?
Thank you, Larisa
The changes in this patch are intended specifically for S32G2/G3, where the clocks are provided in the DT and required by the driver.
Signed-off-by: Radu Pirea radu-nicolae.pirea@nxp.com Co-developed-by: Larisa Grigore larisa.grigore@oss.nxp.com Signed-off-by: Larisa Grigore larisa.grigore@oss.nxp.com
.../bindings/serial/fsl,s32-linflexuart.yaml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+)
diff --git a/Documentation/devicetree/bindings/serial/fsl,s32-linflexuart.yaml b/Documentation/devicetree/bindings/serial/fsl,s32-linflexuart.yaml index 4171f524a928..885f0b1b3492 100644 --- a/Documentation/devicetree/bindings/serial/fsl,s32-linflexuart.yaml +++ b/Documentation/devicetree/bindings/serial/fsl,s32-linflexuart.yaml @@ -34,6 +34,14 @@ properties: interrupts: maxItems: 1
- clocks:
- maxItems: 2
- clock-names:
- items:
- const: lin- const: ipg- required:
- compatible
- reg
@@ -48,3 +56,13 @@ examples: reg = <0x40053000 0x1000>; interrupts = <0 59 4>; };
- |
- serial@401c8000 {
compatible = "nxp,s32g2-linflexuart","fsl,s32v234-linflexuart";reg = <0x401C8000 0x3000>;interrupts = <0 82 1>;clocks = <&clks 14>, <&clks 13>;clock-names = "lin", "ipg";Just add the clocks to existing example. No need for new example for each new property.
- };
The existing node refers to S32V234 which does not expose any clock properties in its device tree. Because of this, I couldn’t extend that
You just said S32V234 has clocks...
example with clocks and clock-names. The additional example is there only to illustrate the S32G2 case, where the clocks are required and actually present in the device tree. Should I remove it?
Best regards, Larisa
Best regards, Krzysztof
Best regards, Krzysztof
On 18/02/2026 14:57, Larisa Ileana Grigore wrote:
On 2/18/2026 3:29 PM, Krzysztof Kozlowski wrote:
On 18/02/2026 14:26, Larisa Ileana Grigore wrote:
On 2/16/2026 5:10 PM, Krzysztof Kozlowski wrote:
On 16/02/2026 16:02, Larisa Grigore wrote:
From: Radu Pirea radu-nicolae.pirea@nxp.com
Add optional support for the two clock inputs used by the LINFlexD UART controller:
- "lin": LIN_BAUD_CLK
- "ipg": LINFLEXD_CLK
The clock inputs are kept optional to maintain compatibility with the S32V234 platform.
Does S32V234 have the clocks? I don't understand the "maintain compatibility" in this context. Either you have or you have not clocks, which should be expressed in schema (: false, see example schema).
Hello Krzysztof,
Thanks for pointing this out! I will update both the schema and the commit description. S32V234 does not expose these clocks in its device tree—on this platform the LINFlexD clocks are set up and enabled by U‑Boot, so they are not available to the kernel.
So there are clocks. DTS is being used by bootloader, so how bootloader is going to set up clocks for S32V234 if no one provides them?
This looks like buggy/incomplete approach, although I understand that the original binding had the issue.
Indeed, I also believe the binding is not fully accurate on S32V234. As I mentioned earlier, the LINFlexD clocks are not managed by Linux on this platform. They, along with several other clocks, are usually initialized by U‑Boot, and Linux does not handle them. That is likely the reason they were omitted from the S32V234 binding. I’m willing to correct this for the S32V234 compatible by making that property optional, so we don’t break compatibility. For S32G, I would prefer to avoid repeating the same oversight we had on S32V234 and make the property required, since the IP, as you correctly pointed out, does have dedicated clock inputs.
How would you approach this?
So the new device should require clocks, which can be left optional for the old one with explanation in the commit msg. Linux is not the only consumer of bindings and DTS.
Best regards, Krzysztof
From: Radu Pirea radu-nicolae.pirea@nxp.com
Add 'dmas' and 'dma-names' properties to describe optional DMA support for RX and TX channels in the LINFlexD UART controller.
This allows the device tree to specify DMA channels used for UART data transfers. If not specified, the driver will fall to interrupt-based operations.
Signed-off-by: Radu Pirea radu-nicolae.pirea@nxp.com Co-developed-by: Larisa Grigore larisa.grigore@oss.nxp.com Signed-off-by: Larisa Grigore larisa.grigore@oss.nxp.com --- .../bindings/serial/fsl,s32-linflexuart.yaml | 13 +++++++++++++ 1 file changed, 13 insertions(+)
diff --git a/Documentation/devicetree/bindings/serial/fsl,s32-linflexuart.yaml b/Documentation/devicetree/bindings/serial/fsl,s32-linflexuart.yaml index 885f0b1b3492..317f9ba41c06 100644 --- a/Documentation/devicetree/bindings/serial/fsl,s32-linflexuart.yaml +++ b/Documentation/devicetree/bindings/serial/fsl,s32-linflexuart.yaml @@ -42,6 +42,16 @@ properties: - const: lin - const: ipg
+ dmas: + items: + - description: DMA controller phandle and request line for RX + - description: DMA controller phandle and request line for TX + + dma-names: + items: + - const: rx + - const: tx + required: - compatible - reg @@ -65,4 +75,7 @@ examples: interrupts = <0 82 1>; clocks = <&clks 14>, <&clks 13>; clock-names = "lin", "ipg"; + dmas = <&edma0 0 4>, + <&edma0 0 3>; + dma-names = "rx", "tx"; };
On 16/02/2026 16:02, Larisa Grigore wrote:
From: Radu Pirea radu-nicolae.pirea@nxp.com
Add 'dmas' and 'dma-names' properties to describe optional DMA support for RX and TX channels in the LINFlexD UART controller.
Same question as in other patch about existing devices.
This allows the device tree to specify DMA channels used for UART data transfers. If not specified, the driver will fall to interrupt-based operations.
Signed-off-by: Radu Pirea radu-nicolae.pirea@nxp.com Co-developed-by: Larisa Grigore larisa.grigore@oss.nxp.com Signed-off-by: Larisa Grigore larisa.grigore@oss.nxp.com
Best regards, Krzysztof
On 2/16/2026 5:10 PM, Krzysztof Kozlowski wrote:
On 16/02/2026 16:02, Larisa Grigore wrote:
From: Radu Pirea radu-nicolae.pirea@nxp.com
Add 'dmas' and 'dma-names' properties to describe optional DMA support for RX and TX channels in the LINFlexD UART controller.
Same question as in other patch about existing devices.
I will update the bindings so that `dmas`/`dma-names` are optional for S32G and not present on S32V234. Would this be acceptable?
Thanks, Larisa
This allows the device tree to specify DMA channels used for UART data transfers. If not specified, the driver will fall to interrupt-based operations.
Signed-off-by: Radu Pirea radu-nicolae.pirea@nxp.com Co-developed-by: Larisa Grigore larisa.grigore@oss.nxp.com Signed-off-by: Larisa Grigore larisa.grigore@oss.nxp.com
Best regards, Krzysztof
On 18/02/2026 15:44, Larisa Ileana Grigore wrote:
On 2/16/2026 5:10 PM, Krzysztof Kozlowski wrote:
On 16/02/2026 16:02, Larisa Grigore wrote:
From: Radu Pirea radu-nicolae.pirea@nxp.com
Add 'dmas' and 'dma-names' properties to describe optional DMA support for RX and TX channels in the LINFlexD UART controller.
Same question as in other patch about existing devices.
I will update the bindings so that `dmas`/`dma-names` are optional for S32G and not present on S32V234. Would this be acceptable?
Yes, with reason in the commit msg.
Best regards, Krzysztof
On 2/16/26 17:02, Larisa Grigore wrote:
[You don't often get email from larisa.grigore@oss.nxp.com. Learn why this is important at https://aka.ms/LearnAboutSenderIdentification ]
From: Radu Pirea radu-nicolae.pirea@nxp.com
Add 'dmas' and 'dma-names' properties to describe optional DMA support for RX and TX channels in the LINFlexD UART controller.
This allows the device tree to specify DMA channels used for UART data transfers. If not specified, the driver will fall to interrupt-based operations.
Signed-off-by: Radu Pirea radu-nicolae.pirea@nxp.com Co-developed-by: Larisa Grigore larisa.grigore@oss.nxp.com Signed-off-by: Larisa Grigore larisa.grigore@oss.nxp.com
If both of you worked on this patch then the last lines must read:
Co-developed-by: Radu Pirea radu-nicolae.pirea@nxp.com
Signed-off-by: Radu Pirea radu-nicolae.pirea@nxp.com
Co-developed-by: Larisa Grigore larisa.grigore@oss.nxp.com
Signed-off-by: Larisa Grigore larisa.grigore@oss.nxp.com
See: https://docs.kernel.org/process/submitting-patches.html
On Mon, Feb 16, 2026 at 05:29:57PM +0200, Daniel Baluta wrote:
On 2/16/26 17:02, Larisa Grigore wrote:
[You don't often get email from larisa.grigore@oss.nxp.com. Learn why this is important at https://aka.ms/LearnAboutSenderIdentification ]
From: Radu Pirea radu-nicolae.pirea@nxp.com
Add 'dmas' and 'dma-names' properties to describe optional DMA support for RX and TX channels in the LINFlexD UART controller.
This allows the device tree to specify DMA channels used for UART data transfers. If not specified, the driver will fall to interrupt-based operations.
Signed-off-by: Radu Pirea radu-nicolae.pirea@nxp.com Co-developed-by: Larisa Grigore larisa.grigore@oss.nxp.com Signed-off-by: Larisa Grigore larisa.grigore@oss.nxp.com
If both of you worked on this patch then the last lines must read:
Co-developed-by: Radu Pirea radu-nicolae.pirea@nxp.com
Signed-off-by: Radu Pirea radu-nicolae.pirea@nxp.com
Co-developed-by: Larisa Grigore larisa.grigore@oss.nxp.com
Signed-off-by: Larisa Grigore larisa.grigore@oss.nxp.com
No, the DCO with this authorship is correct if they both worked. Nothing has to be changed here.
Best regards, Krzysztof
On 2/17/26 10:10, Krzysztof Kozlowski wrote:
On Mon, Feb 16, 2026 at 05:29:57PM +0200, Daniel Baluta wrote:
On 2/16/26 17:02, Larisa Grigore wrote:
[You don't often get email from larisa.grigore@oss.nxp.com. Learn why this is important at https://aka.ms/LearnAboutSenderIdentification ]
From: Radu Pirea radu-nicolae.pirea@nxp.com
Add 'dmas' and 'dma-names' properties to describe optional DMA support for RX and TX channels in the LINFlexD UART controller.
This allows the device tree to specify DMA channels used for UART data transfers. If not specified, the driver will fall to interrupt-based operations.
Signed-off-by: Radu Pirea radu-nicolae.pirea@nxp.com Co-developed-by: Larisa Grigore larisa.grigore@oss.nxp.com Signed-off-by: Larisa Grigore larisa.grigore@oss.nxp.com
If both of you worked on this patch then the last lines must read:
Co-developed-by: Radu Pirea radu-nicolae.pirea@nxp.com
Signed-off-by: Radu Pirea radu-nicolae.pirea@nxp.com
Co-developed-by: Larisa Grigore larisa.grigore@oss.nxp.com
Signed-off-by: Larisa Grigore larisa.grigore@oss.nxp.com
No, the DCO with this authorship is correct if they both worked. Nothing has to be changed here.
Got it now. There is no need to add C-d-by for the people listed as 'main' author
via the 'From' tag.
Updated linflex_set_termios to set the number of stop bits based on termios->c_cflag.
Signed-off-by: Larisa Grigore larisa.grigore@oss.nxp.com --- drivers/tty/serial/fsl_linflexuart.c | 31 +++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-)
diff --git a/drivers/tty/serial/fsl_linflexuart.c b/drivers/tty/serial/fsl_linflexuart.c index 36c8f90d975d..a5a34fd81bcf 100644 --- a/drivers/tty/serial/fsl_linflexuart.c +++ b/drivers/tty/serial/fsl_linflexuart.c @@ -75,6 +75,10 @@
#define LINFLEXD_UARTCR_ROSE BIT(23)
+#define LINFLEXD_UARTCR_SBUR_MASK GENMASK(18, 17) +#define LINFLEXD_UARTCR_SBUR_1SBITS (0x0 << 17) +#define LINFLEXD_UARTCR_SBUR_2SBITS (0x1 << 17) + #define LINFLEXD_UARTCR_RDFLRFC_OFFSET 10 #define LINFLEXD_UARTCR_RDFLRFC_MASK (0x7 << LINFLEXD_UARTCR_RDFLRFC_OFFSET) #define LINFLEXD_UARTCR_RDFLRFC(uartcr) (((uartcr) \ @@ -124,6 +128,10 @@
#define LINFLEX_LDIV_MULTIPLIER (16)
+#define LINFLEXD_GCR_STOP_MASK BIT(1) +#define LINFLEXD_GCR_STOP_1SBITS (0 << 1) +#define LINFLEXD_GCR_STOP_2SBITS BIT(1) + #define DRIVER_NAME "fsl-linflexuart" #define DEV_NAME "ttyLF" #define UART_NR 4 @@ -456,7 +464,7 @@ linflex_set_termios(struct uart_port *port, struct ktermios *termios, const struct ktermios *old) { unsigned long flags; - unsigned long cr, old_cr, cr1; + unsigned long cr, old_cr, cr1, gcr; unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8; unsigned long ibr, fbr, divisr, dividr; unsigned char ldiv_mul; @@ -521,8 +529,25 @@ linflex_set_termios(struct uart_port *port, struct ktermios *termios, cr |= LINFLEXD_UARTCR_WL0; }
- if (termios->c_cflag & CSTOPB) - termios->c_cflag &= ~CSTOPB; + gcr = readl(port->membase + GCR); + + if (termios->c_cflag & CSTOPB) { + /* Use 2 stop bits. */ + cr = (cr & ~LINFLEXD_UARTCR_SBUR_MASK) | + LINFLEXD_UARTCR_SBUR_2SBITS; + /* Set STOP in GCR field for 2 stop bits. */ + gcr = (gcr & ~LINFLEXD_GCR_STOP_MASK) | + LINFLEXD_GCR_STOP_2SBITS; + } else { + /* Use 1 stop bit. */ + cr = (cr & ~LINFLEXD_UARTCR_SBUR_MASK) | + LINFLEXD_UARTCR_SBUR_1SBITS; + /* Set STOP in GCR field for 1 stop bit. */ + gcr = (gcr & ~LINFLEXD_GCR_STOP_MASK) | + LINFLEXD_GCR_STOP_1SBITS; + } + /* Update GCR register. */ + writel(gcr, port->membase + GCR);
/* parity must be enabled when CS7 to match 8-bits format */ if ((termios->c_cflag & CSIZE) == CS7)
From: Radu Pirea radu-nicolae.pirea@nxp.com
This patch adds support for dynamically configuring the baudrate of the LINFlexD UART. It introduces clock handling via clk and clk_ipg, and updates the linflex_set_termios() function to compute and update the baudrate related registers (LINIBRR and LINFBRR) based on the selected baudrate and clock rate. Baudrate is calculated with the following equation: - When UARTCR[ROSE] = 1 (reduced oversampling), baudrate = LIN_CLK ÷ (OSR × LDIV). - When UARTCR[ROSE] = 0, baudrate = LIN_CLK ÷ (16 × LDIV), where LIN_CLK is the frequency of the baud clock. LDIV is an unsigned fixed-point number: - LINIBRR[IBR] stores the mantissa. - LINFBRR[FBR] stores the fraction. This register isn't used in reduced oversampling case.
This feature is supported only if the clock properties are present in the device tree.
Signed-off-by: Radu Pirea radu-nicolae.pirea@nxp.com Co-developed-by: Stefan-Gabriel Mirea stefan-gabriel.mirea@nxp.com Signed-off-by: Stefan-Gabriel Mirea stefan-gabriel.mirea@nxp.com Co-developed-by: Adrian.Nitu adrian.nitu@freescale.com Signed-off-by: Adrian.Nitu adrian.nitu@freescale.com Co-developed-by: Larisa Grigore larisa.grigore@oss.nxp.com Signed-off-by: Larisa Grigore larisa.grigore@oss.nxp.com --- drivers/tty/serial/fsl_linflexuart.c | 124 +++++++++++++++++++++++++-- 1 file changed, 116 insertions(+), 8 deletions(-)
diff --git a/drivers/tty/serial/fsl_linflexuart.c b/drivers/tty/serial/fsl_linflexuart.c index fb5f325416c0..36c8f90d975d 100644 --- a/drivers/tty/serial/fsl_linflexuart.c +++ b/drivers/tty/serial/fsl_linflexuart.c @@ -3,9 +3,10 @@ * Freescale LINFlexD UART serial port driver * * Copyright 2012-2016 Freescale Semiconductor, Inc. - * Copyright 2017-2019, 2021 NXP + * Copyright 2017-2019, 2021-2022 NXP */
+#include <linux/clk.h> #include <linux/console.h> #include <linux/io.h> #include <linux/irq.h> @@ -131,6 +132,22 @@
#define PREINIT_DELAY 2000 /* us */
+enum linflex_clk { + LINFLEX_CLK_LIN, + LINFLEX_CLK_IPG, + LINFLEX_CLK_NUM, +}; + +static const char * const linflex_clks_id[] = { + "lin", + "ipg", +}; + +struct linflex_port { + struct uart_port port; + struct clk_bulk_data clks[LINFLEX_CLK_NUM]; +}; + static const struct of_device_id linflex_dt_ids[] = { { .compatible = "fsl,s32v234-linflexuart", @@ -421,6 +438,19 @@ static void linflex_shutdown(struct uart_port *port) devm_free_irq(port->dev, port->irq, port); }
+static unsigned char +linflex_ldiv_multiplier(struct uart_port *port) +{ + unsigned char mul = LINFLEX_LDIV_MULTIPLIER; + unsigned long cr; + + cr = readl(port->membase + UARTCR); + if (cr & LINFLEXD_UARTCR_ROSE) + mul = LINFLEXD_UARTCR_OSR(cr); + + return mul; +} + static void linflex_set_termios(struct uart_port *port, struct ktermios *termios, const struct ktermios *old) @@ -428,6 +458,9 @@ linflex_set_termios(struct uart_port *port, struct ktermios *termios, unsigned long flags; unsigned long cr, old_cr, cr1; unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8; + unsigned long ibr, fbr, divisr, dividr; + unsigned char ldiv_mul; + unsigned int baud;
uart_port_lock_irqsave(port, &flags);
@@ -532,6 +565,24 @@ linflex_set_termios(struct uart_port *port, struct ktermios *termios, port->ignore_status_mask |= LINFLEXD_UARTSR_BOF; }
+ if (port->uartclk) { + ldiv_mul = linflex_ldiv_multiplier(port); + baud = uart_get_baud_rate(port, termios, old, 0, + port->uartclk / ldiv_mul); + + /* update the per-port timeout */ + uart_update_timeout(port, termios->c_cflag, baud); + + divisr = port->uartclk; + dividr = ((unsigned long)baud * ldiv_mul); + + ibr = divisr / dividr; + fbr = ((divisr % dividr) * 16 / dividr) & 0xF; + + writel(ibr, port->membase + LINIBRR); + writel(fbr, port->membase + LINFBRR); + } + writel(cr, port->membase + UARTCR);
cr1 &= ~(LINFLEXD_LINCR1_INIT); @@ -760,17 +811,52 @@ static struct uart_driver linflex_reg = { .cons = LINFLEX_CONSOLE, };
+static int linflex_init_clk(struct linflex_port *lfport) +{ + int i, ret; + + for (i = 0; i < LINFLEX_CLK_NUM; i++) { + lfport->clks[i].id = linflex_clks_id[i]; + lfport->clks[i].clk = NULL; + } + + ret = devm_clk_bulk_get(lfport->port.dev, LINFLEX_CLK_NUM, + lfport->clks); + if (ret) { + if (ret == -EPROBE_DEFER) + return ret; + + lfport->port.uartclk = 0; + dev_info(lfport->port.dev, + "uart clock is missing, err = %d. Skipping clock setup.\n", + ret); + return 0; + } + + ret = clk_bulk_prepare_enable(LINFLEX_CLK_NUM, lfport->clks); + if (ret) + return dev_err_probe(lfport->port.dev, ret, + "Failed to enable LINFlexD clocks.\n"); + + lfport->port.uartclk = clk_get_rate(lfport->clks[LINFLEX_CLK_LIN].clk); + + return 0; +} + static int linflex_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; + struct linflex_port *lfport; struct uart_port *sport; struct resource *res; int ret;
- sport = devm_kzalloc(&pdev->dev, sizeof(*sport), GFP_KERNEL); - if (!sport) + lfport = devm_kzalloc(&pdev->dev, sizeof(*lfport), GFP_KERNEL); + if (!lfport) return -ENOMEM;
+ sport = &lfport->port; + ret = of_alias_get_id(np, "serial"); if (ret < 0) { dev_err(&pdev->dev, "failed to get alias id, errno %d\n", ret); @@ -800,33 +886,55 @@ static int linflex_probe(struct platform_device *pdev) sport->flags = UPF_BOOT_AUTOCONF; sport->has_sysrq = IS_ENABLED(CONFIG_SERIAL_FSL_LINFLEXUART_CONSOLE);
+ ret = linflex_init_clk(lfport); + if (ret) + return ret; + linflex_ports[sport->line] = sport;
- platform_set_drvdata(pdev, sport); + platform_set_drvdata(pdev, lfport); + + ret = uart_add_one_port(&linflex_reg, sport); + if (ret) + clk_bulk_disable_unprepare(LINFLEX_CLK_NUM, lfport->clks);
- return uart_add_one_port(&linflex_reg, sport); + return ret; }
static void linflex_remove(struct platform_device *pdev) { - struct uart_port *sport = platform_get_drvdata(pdev); + struct linflex_port *lfport = platform_get_drvdata(pdev); + struct uart_port *sport = &lfport->port;
uart_remove_one_port(&linflex_reg, sport); + clk_bulk_disable_unprepare(LINFLEX_CLK_NUM, lfport->clks); }
#ifdef CONFIG_PM_SLEEP static int linflex_suspend(struct device *dev) { - struct uart_port *sport = dev_get_drvdata(dev); + struct linflex_port *lfport = dev_get_drvdata(dev); + struct uart_port *sport = &lfport->port;
uart_suspend_port(&linflex_reg, sport); + clk_bulk_disable_unprepare(LINFLEX_CLK_NUM, lfport->clks);
return 0; }
static int linflex_resume(struct device *dev) { - struct uart_port *sport = dev_get_drvdata(dev); + struct linflex_port *lfport = dev_get_drvdata(dev); + struct uart_port *sport = &lfport->port; + int ret; + + if (lfport->clks[LINFLEX_CLK_LIN].clk) { + ret = clk_bulk_prepare_enable(LINFLEX_CLK_NUM, lfport->clks); + if (ret) { + dev_err(dev, "Failed to enable LINFlexD clocks: %d\n", ret); + return ret; + } + }
uart_resume_port(&linflex_reg, sport);
Add support for using DMA to avoid generating one interrupt per character and losing characters while copy-paste. In UART mode, the DMA capability can be used only if the UART Tx/Rx buffers are configured as FIFOs. If the DMA related properties are missing from the device tree, the driver will fall back to interrupt + Buffer mode. On the RX side, a timer is used to periodically poll for received data.
Signed-off-by: Larisa Grigore larisa.grigore@oss.nxp.com Co-developed-by: Stoica Cosmin-Stefan cosmin.stoica@nxp.com Signed-off-by: Stoica Cosmin-Stefan cosmin.stoica@nxp.com Co-developed-by: Radu Pirea radu-nicolae.pirea@nxp.com Signed-off-by: Radu Pirea radu-nicolae.pirea@nxp.com Co-developed-by: Phu Luu An phu.luuan@nxp.com Signed-off-by: Phu Luu An phu.luuan@nxp.com Co-developed-by: Js Ha js.ha@nxp.com Signed-off-by: Js Ha js.ha@nxp.com Co-developed-by: Ghennadi Procopciuc ghennadi.procopciuc@nxp.com Signed-off-by: Ghennadi Procopciuc ghennadi.procopciuc@nxp.com --- drivers/tty/serial/fsl_linflexuart.c | 642 +++++++++++++++++++++++++-- 1 file changed, 597 insertions(+), 45 deletions(-)
diff --git a/drivers/tty/serial/fsl_linflexuart.c b/drivers/tty/serial/fsl_linflexuart.c index a5a34fd81bcf..dff37c68cff0 100644 --- a/drivers/tty/serial/fsl_linflexuart.c +++ b/drivers/tty/serial/fsl_linflexuart.c @@ -3,19 +3,24 @@ * Freescale LINFlexD UART serial port driver * * Copyright 2012-2016 Freescale Semiconductor, Inc. - * Copyright 2017-2019, 2021-2022 NXP + * Copyright 2017-2019, 2021-2022, 2025 NXP */
#include <linux/clk.h> #include <linux/console.h> +#include <linux/dma-mapping.h> +#include <linux/dmaengine.h> +#include <linux/dmapool.h> #include <linux/io.h> #include <linux/irq.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/of_dma.h> #include <linux/platform_device.h> #include <linux/serial_core.h> #include <linux/slab.h> #include <linux/tty_flip.h> +#include <linux/jiffies.h> #include <linux/delay.h>
/* All registers are 32-bit width */ @@ -42,6 +47,12 @@ #define GCR 0x004C /* Global control register */ #define UARTPTO 0x0050 /* UART preset timeout register */ #define UARTCTO 0x0054 /* UART current timeout register */ +/* The offsets for DMARXE/DMATXE in master mode only */ +#define DMATXE 0x0058 /* DMA Tx enable register */ +#define DMARXE 0x005C /* DMA Rx enable register */ + +#define DMATXE_DRE0 BIT(0) +#define DMARXE_DRE0 BIT(0)
/* * Register field definitions @@ -140,6 +151,9 @@
#define PREINIT_DELAY 2000 /* us */
+#define FSL_UART_RX_DMA_BUFFER_SIZE (PAGE_SIZE) +#define LINFLEXD_UARTCR_FIFO_SIZE (4) + enum linflex_clk { LINFLEX_CLK_LIN, LINFLEX_CLK_IPG, @@ -154,6 +168,24 @@ static const char * const linflex_clks_id[] = { struct linflex_port { struct uart_port port; struct clk_bulk_data clks[LINFLEX_CLK_NUM]; + unsigned int txfifo_size; + unsigned int rxfifo_size; + bool dma_tx_use; + bool dma_rx_use; + struct dma_chan *dma_tx_chan; + struct dma_chan *dma_rx_chan; + struct dma_async_tx_descriptor *dma_tx_desc; + struct dma_async_tx_descriptor *dma_rx_desc; + dma_addr_t dma_tx_buf_bus; + dma_addr_t dma_rx_buf_bus; + dma_cookie_t dma_tx_cookie; + dma_cookie_t dma_rx_cookie; + unsigned char *dma_rx_buf_virt; + unsigned int dma_tx_bytes; + int dma_tx_in_progress; + int dma_rx_in_progress; + unsigned long dma_rx_timeout; + struct timer_list timer; };
static const struct of_device_id linflex_dt_ids[] = { @@ -168,6 +200,76 @@ MODULE_DEVICE_TABLE(of, linflex_dt_ids); static struct uart_port *earlycon_port; #endif
+static void linflex_dma_tx_complete(void *arg); +static void linflex_dma_rx_complete(void *arg); +static void linflex_console_putchar(struct uart_port *port, unsigned char ch); + +static inline struct linflex_port * +to_linflex_port(struct uart_port *uart) +{ + return container_of(uart, struct linflex_port, port); +} + +static void linflex_copy_rx_to_tty(struct linflex_port *lfport, + struct tty_port *tty, int count) +{ + size_t copied; + + lfport->port.icount.rx += count; + + if (!tty) { + dev_err(lfport->port.dev, "No tty port\n"); + return; + } + + dma_sync_single_for_cpu(lfport->port.dev, lfport->dma_rx_buf_bus, + FSL_UART_RX_DMA_BUFFER_SIZE, DMA_FROM_DEVICE); + copied = tty_insert_flip_string(tty, + ((unsigned char *)(lfport->dma_rx_buf_virt)), + count); + + if (copied != count) { + WARN_ON(1); + dev_err(lfport->port.dev, "RxData copy to tty layer failed\n"); + } +} + +static void linflex_enable_dma_rx(struct uart_port *port) +{ + unsigned long dmarxe = readl(port->membase + DMARXE); + + writel(dmarxe | DMARXE_DRE0, port->membase + DMARXE); + while (!(readl(port->membase + DMARXE) & DMARXE_DRE0)) + ; +} + +static void linflex_enable_dma_tx(struct uart_port *port) +{ + unsigned long dmatxe = readl(port->membase + DMATXE); + + writel(dmatxe | DMATXE_DRE0, port->membase + DMATXE); + while (!(readl(port->membase + DMATXE) & DMATXE_DRE0)) + ; +} + +static void linflex_disable_dma_rx(struct uart_port *port) +{ + unsigned long dmarxe = readl(port->membase + DMARXE); + + writel(dmarxe & 0xFFFF0000, port->membase + DMARXE); + while (readl(port->membase + DMARXE) & DMARXE_DRE0) + ; +} + +static void linflex_disable_dma_tx(struct uart_port *port) +{ + unsigned long dmatxe = readl(port->membase + DMATXE); + + writel(dmatxe & 0xFFFF0000, port->membase + DMATXE); + while (readl(port->membase + DMATXE) & DMATXE_DRE0) + ; +} + static inline void linflex_wait_tx_fifo_empty(struct uart_port *port) { unsigned long cr = readl(port->membase + UARTCR); @@ -179,36 +281,113 @@ static inline void linflex_wait_tx_fifo_empty(struct uart_port *port) ; }
+static void _linflex_stop_tx(struct uart_port *port) +{ + struct linflex_port *lfport = to_linflex_port(port); + unsigned long ier; + + if (!lfport->dma_tx_use) { + ier = readl(port->membase + LINIER); + ier &= ~(LINFLEXD_LINIER_DTIE); + writel(ier, port->membase + LINIER); + return; + } + + linflex_disable_dma_tx(port); +} + static void linflex_stop_tx(struct uart_port *port) { + struct linflex_port *lfport = to_linflex_port(port); + struct dma_tx_state state; + unsigned int count; + + _linflex_stop_tx(port); + + if (!lfport->dma_tx_in_progress) + return; + + dmaengine_pause(lfport->dma_tx_chan); + dmaengine_tx_status(lfport->dma_tx_chan, + lfport->dma_tx_cookie, &state); + dmaengine_terminate_all(lfport->dma_tx_chan); + count = lfport->dma_tx_bytes - state.residue; + uart_xmit_advance(port, count); + + lfport->dma_tx_in_progress = 0; +} + +static void _linflex_start_rx(struct uart_port *port) +{ + struct linflex_port *lfport = to_linflex_port(port); unsigned long ier;
- ier = readl(port->membase + LINIER); - ier &= ~(LINFLEXD_LINIER_DTIE); - writel(ier, port->membase + LINIER); + if (!lfport->dma_rx_use) { + ier = readl(port->membase + LINIER); + writel(ier | LINFLEXD_LINIER_DRIE, port->membase + LINIER); + return; + } + + linflex_enable_dma_rx(port); }
-static void linflex_stop_rx(struct uart_port *port) +static void _linflex_stop_rx(struct uart_port *port) { + struct linflex_port *lfport = to_linflex_port(port); unsigned long ier;
- ier = readl(port->membase + LINIER); - writel(ier & ~LINFLEXD_LINIER_DRIE, port->membase + LINIER); + if (!lfport->dma_rx_use) { + ier = readl(port->membase + LINIER); + writel(ier & ~LINFLEXD_LINIER_DRIE, port->membase + LINIER); + return; + } + + linflex_disable_dma_rx(port); +} + +static void linflex_stop_rx(struct uart_port *port) +{ + struct linflex_port *lfport = to_linflex_port(port); + struct dma_tx_state state; + unsigned int count; + + _linflex_stop_rx(port); + + if (!lfport->dma_rx_in_progress) + return; + + dmaengine_pause(lfport->dma_rx_chan); + dmaengine_tx_status(lfport->dma_rx_chan, + lfport->dma_rx_cookie, &state); + dmaengine_terminate_all(lfport->dma_rx_chan); + count = FSL_UART_RX_DMA_BUFFER_SIZE - state.residue; + + lfport->dma_rx_in_progress = 0; + linflex_copy_rx_to_tty(lfport, &port->state->port, count); + tty_flip_buffer_push(&port->state->port); }
static void linflex_put_char(struct uart_port *sport, unsigned char c) { + struct linflex_port *lfport = to_linflex_port(sport); unsigned long status;
writeb(c, sport->membase + BDRL);
/* Waiting for data transmission completed. */ - while (((status = readl(sport->membase + UARTSR)) & - LINFLEXD_UARTSR_DTFTFF) != - LINFLEXD_UARTSR_DTFTFF) - ; + if (!lfport->dma_tx_use) { + while (((status = readl(sport->membase + UARTSR)) & + LINFLEXD_UARTSR_DTFTFF) != + LINFLEXD_UARTSR_DTFTFF) + ; + } else { + while (((status = readl(sport->membase + UARTSR)) & + LINFLEXD_UARTSR_DTFTFF)) + ; + }
- writel(LINFLEXD_UARTSR_DTFTFF, sport->membase + UARTSR); + if (!lfport->dma_tx_use) + writel(LINFLEXD_UARTSR_DTFTFF, sport->membase + UARTSR); }
static inline void linflex_transmit_buffer(struct uart_port *sport) @@ -228,18 +407,198 @@ static inline void linflex_transmit_buffer(struct uart_port *sport) linflex_stop_tx(sport); }
+static int linflex_dma_tx(struct linflex_port *lfport, unsigned int count, + unsigned int tail) +{ + struct uart_port *sport = &lfport->port; + dma_addr_t tx_bus_addr; + + while ((readl(sport->membase + UARTSR) & LINFLEXD_UARTSR_DTFTFF)) + ; + + dma_sync_single_for_device(sport->dev, lfport->dma_tx_buf_bus, + UART_XMIT_SIZE, DMA_TO_DEVICE); + lfport->dma_tx_bytes = count; + tx_bus_addr = lfport->dma_tx_buf_bus + tail; + lfport->dma_tx_desc = + dmaengine_prep_slave_single(lfport->dma_tx_chan, tx_bus_addr, + lfport->dma_tx_bytes, DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + + if (!lfport->dma_tx_desc) { + dev_err(sport->dev, "Not able to get desc for tx\n"); + return -EIO; + } + + lfport->dma_tx_desc->callback = linflex_dma_tx_complete; + lfport->dma_tx_desc->callback_param = sport; + lfport->dma_tx_in_progress = 1; + lfport->dma_tx_cookie = dmaengine_submit(lfport->dma_tx_desc); + dma_async_issue_pending(lfport->dma_tx_chan); + + linflex_enable_dma_tx(&lfport->port); + return 0; +} + +static void linflex_prepare_tx(struct linflex_port *lfport) +{ + struct tty_port *tport = &lfport->port.state->port; + unsigned int count, tail; + + count = kfifo_out_linear(&tport->xmit_fifo, &tail, UART_XMIT_SIZE); + + if (!count || lfport->dma_tx_in_progress) + return; + + linflex_dma_tx(lfport, count, tail); +} + +static void linflex_restart_dma_tx(struct linflex_port *lfport) +{ + struct uart_port *sport = &lfport->port; + struct tty_port *tport = &sport->state->port; + + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) + uart_write_wakeup(sport); + + linflex_prepare_tx(lfport); +} + +static void linflex_dma_tx_complete(void *arg) +{ + struct linflex_port *lfport = arg; + struct uart_port *sport = &lfport->port; + unsigned long flags; + + uart_port_lock_irqsave(sport, &flags); + + /* stopped before? */ + if (!lfport->dma_tx_in_progress) + goto out_tx_callback; + + uart_xmit_advance(sport, lfport->dma_tx_bytes); + lfport->dma_tx_in_progress = 0; + + linflex_restart_dma_tx(lfport); + +out_tx_callback: + uart_port_unlock_irqrestore(sport, flags); +} + +static void linflex_flush_buffer(struct uart_port *port) +{ + struct linflex_port *lfport = to_linflex_port(port); + + if (lfport->dma_tx_use) { + linflex_disable_dma_tx(port); + dmaengine_terminate_async(lfport->dma_tx_chan); + lfport->dma_tx_in_progress = 0; + } +} + +static int linflex_dma_rx(struct linflex_port *lfport) +{ + dma_sync_single_for_device(lfport->port.dev, lfport->dma_rx_buf_bus, + FSL_UART_RX_DMA_BUFFER_SIZE, + DMA_FROM_DEVICE); + lfport->dma_rx_desc = dmaengine_prep_slave_single(lfport->dma_rx_chan, + lfport->dma_rx_buf_bus, + FSL_UART_RX_DMA_BUFFER_SIZE, + DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | + DMA_CTRL_ACK); + + if (!lfport->dma_rx_desc) { + dev_err(lfport->port.dev, "Not able to get desc for rx\n"); + return -EIO; + } + + lfport->dma_rx_desc->callback = linflex_dma_rx_complete; + lfport->dma_rx_desc->callback_param = lfport; + lfport->dma_rx_in_progress = 1; + lfport->dma_rx_cookie = dmaengine_submit(lfport->dma_rx_desc); + dma_async_issue_pending(lfport->dma_rx_chan); + + linflex_enable_dma_rx(&lfport->port); + return 0; +} + +static void linflex_dma_rx_complete(void *arg) +{ + struct linflex_port *lfport = arg; + struct tty_port *port = &lfport->port.state->port; + unsigned long flags; + + timer_delete_sync(&lfport->timer); + + uart_port_lock_irqsave(&lfport->port, &flags); + + /* stopped before? */ + if (!lfport->dma_rx_in_progress) { + uart_port_unlock_irqrestore(&lfport->port, flags); + return; + } + + lfport->dma_rx_in_progress = 0; + linflex_copy_rx_to_tty(lfport, port, FSL_UART_RX_DMA_BUFFER_SIZE); + tty_flip_buffer_push(port); + linflex_dma_rx(lfport); + + uart_port_unlock_irqrestore(&lfport->port, flags); + + mod_timer(&lfport->timer, jiffies + lfport->dma_rx_timeout); +} + +static void linflex_timer_func(struct timer_list *t) +{ + struct linflex_port *lfport = timer_container_of(lfport, t, timer); + unsigned long flags; + + uart_port_lock_irqsave(&lfport->port, &flags); + + /* stopped before? */ + if (!lfport->dma_rx_in_progress) { + uart_port_unlock_irqrestore(&lfport->port, flags); + return; + } + + linflex_stop_rx(&lfport->port); + linflex_dma_rx(lfport); + + uart_port_unlock_irqrestore(&lfport->port, flags); + mod_timer(&lfport->timer, jiffies + lfport->dma_rx_timeout); +} + +static void _linflex_start_tx(struct uart_port *port) +{ + struct linflex_port *lfport = to_linflex_port(port); + unsigned long ier; + + if (lfport->dma_tx_use) { + linflex_enable_dma_tx(&lfport->port); + } else { + ier = readl(port->membase + LINIER); + writel(ier | LINFLEXD_LINIER_DTIE, port->membase + LINIER); + } +} + static void linflex_start_tx(struct uart_port *port) { + struct linflex_port *lfport = to_linflex_port(port); unsigned long ier;
- linflex_transmit_buffer(port); - ier = readl(port->membase + LINIER); - writel(ier | LINFLEXD_LINIER_DTIE, port->membase + LINIER); + if (lfport->dma_tx_use) { + linflex_prepare_tx(lfport); + } else { + linflex_transmit_buffer(port); + ier = readl(port->membase + LINIER); + writel(ier | LINFLEXD_LINIER_DTIE, port->membase + LINIER); + } }
static irqreturn_t linflex_txint(int irq, void *dev_id) { - struct uart_port *sport = dev_id; + struct linflex_port *lfport = dev_id; + struct uart_port *sport = &lfport->port; struct tty_port *tport = &sport->state->port; unsigned long flags;
@@ -263,7 +622,8 @@ static irqreturn_t linflex_txint(int irq, void *dev_id)
static irqreturn_t linflex_rxint(int irq, void *dev_id) { - struct uart_port *sport = dev_id; + struct linflex_port *lfport = dev_id; + struct uart_port *sport = &lfport->port; unsigned int flg; struct tty_port *port = &sport->state->port; unsigned long flags, status; @@ -316,14 +676,14 @@ static irqreturn_t linflex_rxint(int irq, void *dev_id)
static irqreturn_t linflex_int(int irq, void *dev_id) { - struct uart_port *sport = dev_id; + struct linflex_port *lfport = dev_id; unsigned long status;
- status = readl(sport->membase + UARTSR); + status = readl(lfport->port.membase + UARTSR);
- if (status & LINFLEXD_UARTSR_DRFRFE) + if (status & LINFLEXD_UARTSR_DRFRFE && !lfport->dma_rx_use) linflex_rxint(irq, dev_id); - if (status & LINFLEXD_UARTSR_DTFTFF) + if (status & LINFLEXD_UARTSR_DTFTFF && !lfport->dma_rx_use) linflex_txint(irq, dev_id);
return IRQ_HANDLED; @@ -332,11 +692,15 @@ static irqreturn_t linflex_int(int irq, void *dev_id) /* return TIOCSER_TEMT when transmitter is not busy */ static unsigned int linflex_tx_empty(struct uart_port *port) { + struct linflex_port *lfport = to_linflex_port(port); unsigned long status;
status = readl(port->membase + UARTSR) & LINFLEXD_UARTSR_DTFTFF;
- return status ? TIOCSER_TEMT : 0; + if (!lfport->dma_tx_use) + return status ? TIOCSER_TEMT : 0; + else + return status ? 0 : TIOCSER_TEMT; }
static unsigned int linflex_get_mctrl(struct uart_port *port) @@ -354,6 +718,7 @@ static void linflex_break_ctl(struct uart_port *port, int break_state)
static void linflex_setup_watermark(struct uart_port *sport) { + struct linflex_port *lfport = to_linflex_port(sport); unsigned long cr, ier, cr1;
/* Disable transmission/reception */ @@ -396,6 +761,14 @@ static void linflex_setup_watermark(struct uart_port *sport)
cr = (LINFLEXD_UARTCR_WL0 | LINFLEXD_UARTCR_UART);
+ /* FIFO mode enabled for DMA Rx mode. */ + if (lfport->dma_rx_use) + cr |= LINFLEXD_UARTCR_RFBM; + + /* FIFO mode enabled for DMA Tx mode. */ + if (lfport->dma_tx_use) + cr |= LINFLEXD_UARTCR_TFBM; + writel(cr, sport->membase + UARTCR);
cr1 &= ~(LINFLEXD_LINCR1_INIT); @@ -406,44 +779,169 @@ static void linflex_setup_watermark(struct uart_port *sport) writel(cr, sport->membase + UARTCR);
ier = readl(sport->membase + LINIER); - ier |= LINFLEXD_LINIER_DRIE; - ier |= LINFLEXD_LINIER_DTIE; + if (!lfport->dma_rx_use) + ier |= LINFLEXD_LINIER_DRIE; + + if (!lfport->dma_tx_use) + ier |= LINFLEXD_LINIER_DTIE;
writel(ier, sport->membase + LINIER); }
+static int linflex_dma_tx_request(struct uart_port *port) +{ + struct linflex_port *lfport = to_linflex_port(port); + struct tty_port *tport = &port->state->port; + struct dma_slave_config dma_tx_sconfig; + dma_addr_t dma_bus; + int ret; + + dma_bus = dma_map_single(port->dev, tport->xmit_buf, + UART_XMIT_SIZE, DMA_TO_DEVICE); + + if (dma_mapping_error(port->dev, dma_bus)) { + dev_err(port->dev, "dma_map_single tx failed\n"); + return -ENOMEM; + } + + memset(&dma_tx_sconfig, 0, sizeof(dma_tx_sconfig)); + dma_tx_sconfig.dst_addr = port->mapbase + BDRL; + dma_tx_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + dma_tx_sconfig.dst_maxburst = 1; + dma_tx_sconfig.direction = DMA_MEM_TO_DEV; + ret = dmaengine_slave_config(lfport->dma_tx_chan, &dma_tx_sconfig); + + if (ret < 0) { + dev_err(port->dev, "Dma slave config failed, err = %d\n", + ret); + return ret; + } + + lfport->dma_tx_buf_bus = dma_bus; + lfport->dma_tx_in_progress = 0; + + return 0; +} + +static int linflex_dma_rx_request(struct uart_port *port) +{ + struct linflex_port *lfport = to_linflex_port(port); + struct dma_slave_config dma_rx_sconfig; + unsigned char *dma_buf; + dma_addr_t dma_bus; + int ret; + + dma_buf = devm_kmalloc(port->dev, FSL_UART_RX_DMA_BUFFER_SIZE, + GFP_KERNEL); + + if (!dma_buf) { + dev_err(port->dev, "Dma rx alloc failed\n"); + return -ENOMEM; + } + + dma_bus = dma_map_single(port->dev, dma_buf, + FSL_UART_RX_DMA_BUFFER_SIZE, DMA_FROM_DEVICE); + + if (dma_mapping_error(port->dev, dma_bus)) { + dev_err(port->dev, "dma_map_single rx failed\n"); + return -ENOMEM; + } + + memset(&dma_rx_sconfig, 0, sizeof(dma_rx_sconfig)); + dma_rx_sconfig.src_addr = port->mapbase + BDRM; + dma_rx_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + dma_rx_sconfig.src_maxburst = 1; + dma_rx_sconfig.direction = DMA_DEV_TO_MEM; + ret = dmaengine_slave_config(lfport->dma_rx_chan, &dma_rx_sconfig); + + if (ret < 0) { + dev_err(port->dev, "Dma slave config failed, err = %d\n", + ret); + return ret; + } + + lfport->dma_rx_buf_virt = dma_buf; + lfport->dma_rx_buf_bus = dma_bus; + lfport->dma_rx_in_progress = 0; + + return 0; +} + +static void linflex_dma_tx_free(struct uart_port *port) +{ + struct linflex_port *lfport = to_linflex_port(port); + + dma_unmap_single(lfport->port.dev, lfport->dma_tx_buf_bus, UART_XMIT_SIZE, + DMA_TO_DEVICE); + + lfport->dma_tx_buf_bus = 0; +} + +static void linflex_dma_rx_free(struct uart_port *port) +{ + struct linflex_port *lfport = to_linflex_port(port); + + dma_unmap_single(lfport->port.dev, lfport->dma_rx_buf_bus, + FSL_UART_RX_DMA_BUFFER_SIZE, DMA_FROM_DEVICE); + devm_kfree(lfport->port.dev, lfport->dma_rx_buf_virt); + + lfport->dma_rx_buf_bus = 0; + lfport->dma_rx_buf_virt = NULL; +} + static int linflex_startup(struct uart_port *port) { + struct linflex_port *lfport = to_linflex_port(port); int ret = 0; unsigned long flags; + bool dma_rx_use, dma_tx_use; + + dma_rx_use = lfport->dma_rx_chan && !linflex_dma_rx_request(port); + dma_tx_use = lfport->dma_tx_chan && !linflex_dma_tx_request(port);
uart_port_lock_irqsave(port, &flags);
+ lfport->dma_rx_use = dma_rx_use; + lfport->dma_tx_use = dma_tx_use; + lfport->port.fifosize = LINFLEXD_UARTCR_FIFO_SIZE; + linflex_setup_watermark(port);
+ if (lfport->dma_rx_use && !linflex_dma_rx(lfport)) { + timer_setup(&lfport->timer, linflex_timer_func, 0); + mod_timer(&lfport->timer, jiffies + lfport->dma_rx_timeout); + } uart_port_unlock_irqrestore(port, flags);
- ret = devm_request_irq(port->dev, port->irq, linflex_int, 0, - DRIVER_NAME, port); - + if (!lfport->dma_rx_use || !lfport->dma_tx_use) { + ret = devm_request_irq(port->dev, port->irq, linflex_int, 0, + DRIVER_NAME, lfport); + } return ret; }
static void linflex_shutdown(struct uart_port *port) { - unsigned long ier; + struct linflex_port *lfport = to_linflex_port(port); unsigned long flags;
+ timer_delete_sync(&lfport->timer); + uart_port_lock_irqsave(port, &flags);
- /* disable interrupts */ - ier = readl(port->membase + LINIER); - ier &= ~(LINFLEXD_LINIER_DRIE | LINFLEXD_LINIER_DTIE); - writel(ier, port->membase + LINIER); + linflex_stop_tx(port); + linflex_stop_rx(port);
uart_port_unlock_irqrestore(port, flags);
- devm_free_irq(port->dev, port->irq, port); + if (!lfport->dma_rx_use || !lfport->dma_tx_use) + devm_free_irq(port->dev, port->irq, lfport); + + if (lfport->dma_rx_use) + linflex_dma_rx_free(port); + + if (lfport->dma_tx_use) + linflex_dma_tx_free(port); }
static unsigned char @@ -463,6 +961,7 @@ static void linflex_set_termios(struct uart_port *port, struct ktermios *termios, const struct ktermios *old) { + struct linflex_port *lfport = to_linflex_port(port); unsigned long flags; unsigned long cr, old_cr, cr1, gcr; unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8; @@ -472,6 +971,9 @@ linflex_set_termios(struct uart_port *port, struct ktermios *termios,
uart_port_lock_irqsave(port, &flags);
+ _linflex_stop_rx(port); + _linflex_stop_tx(port); + old_cr = readl(port->membase + UARTCR) & ~(LINFLEXD_UARTCR_RXEN | LINFLEXD_UARTCR_TXEN); cr = old_cr; @@ -608,6 +1110,8 @@ linflex_set_termios(struct uart_port *port, struct ktermios *termios, writel(fbr, port->membase + LINFBRR); }
+ lfport->dma_rx_timeout = msecs_to_jiffies(DIV_ROUND_UP(10000000, baud)); + writel(cr, port->membase + UARTCR);
cr1 &= ~(LINFLEXD_LINCR1_INIT); @@ -617,6 +1121,9 @@ linflex_set_termios(struct uart_port *port, struct ktermios *termios, cr |= (LINFLEXD_UARTCR_TXEN) | (LINFLEXD_UARTCR_RXEN); writel(cr, port->membase + UARTCR);
+ _linflex_start_rx(port); + _linflex_start_tx(port); + uart_port_unlock_irqrestore(port, flags); }
@@ -657,6 +1164,7 @@ static const struct uart_ops linflex_pops = { .request_port = linflex_request_port, .release_port = linflex_release_port, .config_port = linflex_config_port, + .flush_buffer = linflex_flush_buffer, };
static struct uart_port *linflex_ports[UART_NR]; @@ -690,18 +1198,16 @@ static void linflex_console_putchar(struct uart_port *port, unsigned char ch) static void linflex_string_write(struct uart_port *sport, const char *s, unsigned int count) { - unsigned long cr, ier = 0; - - ier = readl(sport->membase + LINIER); - linflex_stop_tx(sport); + unsigned long cr;
+ _linflex_stop_tx(sport); cr = readl(sport->membase + UARTCR); cr |= (LINFLEXD_UARTCR_TXEN); writel(cr, sport->membase + UARTCR);
uart_console_write(sport, s, count, linflex_console_putchar);
- writel(ier, sport->membase + LINIER); + _linflex_start_tx(sport); }
static void @@ -881,30 +1387,59 @@ static int linflex_probe(struct platform_device *pdev) return -ENOMEM;
sport = &lfport->port; + sport->dev = &pdev->dev; + + lfport->dma_tx_chan = dma_request_chan(sport->dev, "tx"); + if (IS_ERR(lfport->dma_tx_chan)) { + ret = PTR_ERR(lfport->dma_tx_chan); + if (ret == -EPROBE_DEFER) + return ret; + + dev_info(sport->dev, + "DMA tx channel request failed, operating without tx DMA %ld\n", + PTR_ERR(lfport->dma_tx_chan)); + lfport->dma_tx_chan = NULL; + } + + lfport->dma_rx_chan = dma_request_chan(sport->dev, "rx"); + if (IS_ERR(lfport->dma_rx_chan)) { + ret = PTR_ERR(lfport->dma_rx_chan); + if (ret == -EPROBE_DEFER) { + dma_release_channel(lfport->dma_tx_chan); + return ret; + } + + dev_info(sport->dev, + "DMA rx channel request failed, operating without rx DMA %ld\n", + PTR_ERR(lfport->dma_rx_chan)); + lfport->dma_rx_chan = NULL; + }
ret = of_alias_get_id(np, "serial"); if (ret < 0) { dev_err(&pdev->dev, "failed to get alias id, errno %d\n", ret); - return ret; + goto linflex_probe_free_dma; } if (ret >= UART_NR) { dev_err(&pdev->dev, "driver limited to %d serial ports\n", UART_NR); - return -ENOMEM; + ret = -ENOMEM; + goto linflex_probe_free_dma; }
sport->line = ret;
sport->membase = devm_platform_get_and_ioremap_resource(pdev, 0, &res); - if (IS_ERR(sport->membase)) - return PTR_ERR(sport->membase); + if (IS_ERR(sport->membase)) { + ret = PTR_ERR(sport->membase); + goto linflex_probe_free_dma; + } sport->mapbase = res->start;
ret = platform_get_irq(pdev, 0); if (ret < 0) return ret;
- sport->dev = &pdev->dev; sport->iotype = UPIO_MEM; sport->irq = ret; sport->ops = &linflex_pops; @@ -913,15 +1448,25 @@ static int linflex_probe(struct platform_device *pdev)
ret = linflex_init_clk(lfport); if (ret) - return ret; + goto linflex_probe_free_dma;
linflex_ports[sport->line] = sport;
platform_set_drvdata(pdev, lfport);
ret = uart_add_one_port(&linflex_reg, sport); - if (ret) + if (ret) { clk_bulk_disable_unprepare(LINFLEX_CLK_NUM, lfport->clks); + goto linflex_probe_free_dma; + } + + return 0; + +linflex_probe_free_dma: + if (lfport->dma_tx_chan) + dma_release_channel(lfport->dma_tx_chan); + if (lfport->dma_rx_chan) + dma_release_channel(lfport->dma_rx_chan);
return ret; } @@ -933,6 +1478,13 @@ static void linflex_remove(struct platform_device *pdev)
uart_remove_one_port(&linflex_reg, sport); clk_bulk_disable_unprepare(LINFLEX_CLK_NUM, lfport->clks); + + if (lfport->dma_tx_chan) + dma_release_channel(lfport->dma_tx_chan); + + if (lfport->dma_rx_chan) + dma_release_channel(lfport->dma_rx_chan); + }
#ifdef CONFIG_PM_SLEEP
On 16/02/2026 16:02, Larisa Grigore wrote:
Add support for using DMA to avoid generating one interrupt per character and losing characters while copy-paste. In UART mode, the DMA capability can be used only if the UART Tx/Rx buffers are configured as FIFOs. If the DMA related properties are missing from the device tree, the driver will fall back to interrupt + Buffer mode. On the RX side, a timer is used to periodically poll for received data.
Signed-off-by: Larisa Grigore larisa.grigore@oss.nxp.com Co-developed-by: Stoica Cosmin-Stefan cosmin.stoica@nxp.com Signed-off-by: Stoica Cosmin-Stefan cosmin.stoica@nxp.com Co-developed-by: Radu Pirea radu-nicolae.pirea@nxp.com Signed-off-by: Radu Pirea radu-nicolae.pirea@nxp.com Co-developed-by: Phu Luu An phu.luuan@nxp.com Signed-off-by: Phu Luu An phu.luuan@nxp.com Co-developed-by: Js Ha js.ha@nxp.com Signed-off-by: Js Ha js.ha@nxp.com Co-developed-by: Ghennadi Procopciuc ghennadi.procopciuc@nxp.com Signed-off-by: Ghennadi Procopciuc ghennadi.procopciuc@nxp.com
Incorrect DCO chain. Please read submitting patches document.
Best regards, Krzysztof
Hi Larisa,
kernel test robot noticed the following build warnings:
[auto build test WARNING on tty/tty-testing] [also build test WARNING on tty/tty-next tty/tty-linus usb/usb-testing usb/usb-next usb/usb-linus robh/for-next linus/master v6.19 next-20260216] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Larisa-Grigore/serial-linflex... base: https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty.git tty-testing patch link: https://lore.kernel.org/r/20260216150205.212318-13-larisa.grigore%40oss.nxp.... patch subject: [PATCH 12/13] serial: linflexuart: Add DMA support config: parisc-randconfig-001-20260217 (https://download.01.org/0day-ci/archive/20260217/202602170428.SOCWu0Wb-lkp@i...) compiler: hppa-linux-gcc (GCC) 15.2.0 reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260217/202602170428.SOCWu0Wb-lkp@i...)
If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags | Reported-by: kernel test robot lkp@intel.com | Closes: https://lore.kernel.org/oe-kbuild-all/202602170428.SOCWu0Wb-lkp@intel.com/
All warnings (new ones prefixed by >>):
drivers/tty/serial/fsl_linflexuart.c:205:13: warning: 'linflex_console_putchar' declared 'static' but never defined [-Wunused-function]
205 | static void linflex_console_putchar(struct uart_port *port, unsigned char ch); | ^~~~~~~~~~~~~~~~~~~~~~~
vim +205 drivers/tty/serial/fsl_linflexuart.c
202 203 static void linflex_dma_tx_complete(void *arg); 204 static void linflex_dma_rx_complete(void *arg);
205 static void linflex_console_putchar(struct uart_port *port, unsigned char ch);
206
Hi Larisa,
kernel test robot noticed the following build warnings:
[auto build test WARNING on tty/tty-testing] [also build test WARNING on tty/tty-next tty/tty-linus usb/usb-testing usb/usb-next usb/usb-linus robh/for-next linus/master v6.19 next-20260216] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Larisa-Grigore/serial-linflex... base: https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty.git tty-testing patch link: https://lore.kernel.org/r/20260216150205.212318-13-larisa.grigore%40oss.nxp.... patch subject: [PATCH 12/13] serial: linflexuart: Add DMA support config: i386-buildonly-randconfig-002-20260217 (https://download.01.org/0day-ci/archive/20260217/202602171112.rMhRspEp-lkp@i...) compiler: clang version 20.1.8 (https://github.com/llvm/llvm-project 87f0227cb60147a26a1eeb4fb06e3b505e9c7261) reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260217/202602171112.rMhRspEp-lkp@i...)
If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags | Reported-by: kernel test robot lkp@intel.com | Closes: https://lore.kernel.org/oe-kbuild-all/202602171112.rMhRspEp-lkp@intel.com/
All warnings (new ones prefixed by >>):
drivers/tty/serial/fsl_linflexuart.c:1095:6: warning: variable 'baud' is used uninitialized whenever 'if' condition is false [-Wsometimes-uninitialized]
1095 | if (port->uartclk) { | ^~~~~~~~~~~~~ drivers/tty/serial/fsl_linflexuart.c:1113:67: note: uninitialized use occurs here 1113 | lfport->dma_rx_timeout = msecs_to_jiffies(DIV_ROUND_UP(10000000, baud)); | ^~~~ include/linux/math.h:49:22: note: expanded from macro 'DIV_ROUND_UP' 49 | #define DIV_ROUND_UP __KERNEL_DIV_ROUND_UP | ^ include/uapi/linux/const.h:51:46: note: expanded from macro '__KERNEL_DIV_ROUND_UP' 51 | #define __KERNEL_DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) | ^ drivers/tty/serial/fsl_linflexuart.c:1095:2: note: remove the 'if' if its condition is always true 1095 | if (port->uartclk) { | ^~~~~~~~~~~~~~~~~~ drivers/tty/serial/fsl_linflexuart.c:970:19: note: initialize the variable 'baud' to silence this warning 970 | unsigned int baud; | ^ | = 0 drivers/tty/serial/fsl_linflexuart.c:205:13: warning: unused function 'linflex_console_putchar' [-Wunused-function] 205 | static void linflex_console_putchar(struct uart_port *port, unsigned char ch); | ^~~~~~~~~~~~~~~~~~~~~~~ 2 warnings generated.
vim +1095 drivers/tty/serial/fsl_linflexuart.c
1d3f5f07fafc712 Radu Pirea 2026-02-16 959 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 960 static void 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 961 linflex_set_termios(struct uart_port *port, struct ktermios *termios, bec5b814d46c2a7 Ilpo Järvinen 2022-08-16 962 const struct ktermios *old) 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 963 { 0b34325c5f79f1f Larisa Grigore 2026-02-16 964 struct linflex_port *lfport = to_linflex_port(port); 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 965 unsigned long flags; 1312e6586227421 Larisa Grigore 2026-02-16 966 unsigned long cr, old_cr, cr1, gcr; 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 967 unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8; 1d3f5f07fafc712 Radu Pirea 2026-02-16 968 unsigned long ibr, fbr, divisr, dividr; 1d3f5f07fafc712 Radu Pirea 2026-02-16 969 unsigned char ldiv_mul; 1d3f5f07fafc712 Radu Pirea 2026-02-16 970 unsigned int baud; 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 971 a75137a58feb092 Radu Pirea 2026-02-16 972 uart_port_lock_irqsave(port, &flags); a75137a58feb092 Radu Pirea 2026-02-16 973 0b34325c5f79f1f Larisa Grigore 2026-02-16 974 _linflex_stop_rx(port); 0b34325c5f79f1f Larisa Grigore 2026-02-16 975 _linflex_stop_tx(port); 0b34325c5f79f1f Larisa Grigore 2026-02-16 976 fb1da4d7f0bec28 Larisa Grigore 2026-02-16 977 old_cr = readl(port->membase + UARTCR) & fb1da4d7f0bec28 Larisa Grigore 2026-02-16 978 ~(LINFLEXD_UARTCR_RXEN | LINFLEXD_UARTCR_TXEN); fb1da4d7f0bec28 Larisa Grigore 2026-02-16 979 cr = old_cr; fb1da4d7f0bec28 Larisa Grigore 2026-02-16 980 fb1da4d7f0bec28 Larisa Grigore 2026-02-16 981 /* In FIFO mode, we should make sure the fifo is empty fb1da4d7f0bec28 Larisa Grigore 2026-02-16 982 * before entering INITM. fb1da4d7f0bec28 Larisa Grigore 2026-02-16 983 */ fb1da4d7f0bec28 Larisa Grigore 2026-02-16 984 linflex_wait_tx_fifo_empty(port); fb1da4d7f0bec28 Larisa Grigore 2026-02-16 985 fb1da4d7f0bec28 Larisa Grigore 2026-02-16 986 /* disable transmit and receive */ fb1da4d7f0bec28 Larisa Grigore 2026-02-16 987 writel(old_cr, port->membase + UARTCR); 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 988 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 989 /* Enter initialization mode by setting INIT bit */ 5e8e1ccacae0470 Larisa Grigore 2026-02-16 990 cr1 = LINFLEXD_LINCR1_INIT | LINFLEXD_LINCR1_MME; 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 991 writel(cr1, port->membase + LINCR1); 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 992 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 993 /* wait for init mode entry */ 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 994 while ((readl(port->membase + LINSR) 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 995 & LINFLEXD_LINSR_LINS_MASK) 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 996 != LINFLEXD_LINSR_LINS_INITMODE) 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 997 ; 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 998 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 999 /* 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1000 * only support CS8 and CS7, and for CS7 must enable PE. 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1001 * supported mode: 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1002 * - (7,e/o,1) 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1003 * - (8,n,1) 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1004 * - (8,e/o,1) 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1005 */ 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1006 /* enter the UART into configuration mode */ 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1007 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1008 while ((termios->c_cflag & CSIZE) != CS8 && 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1009 (termios->c_cflag & CSIZE) != CS7) { 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1010 termios->c_cflag &= ~CSIZE; 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1011 termios->c_cflag |= old_csize; 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1012 old_csize = CS8; 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1013 } 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1014 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1015 if ((termios->c_cflag & CSIZE) == CS7) { 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1016 /* Word length: WL1WL0:00 */ 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1017 cr = old_cr & ~LINFLEXD_UARTCR_WL1 & ~LINFLEXD_UARTCR_WL0; 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1018 } 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1019 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1020 if ((termios->c_cflag & CSIZE) == CS8) { 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1021 /* Word length: WL1WL0:01 */ 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1022 cr = (old_cr | LINFLEXD_UARTCR_WL0) & ~LINFLEXD_UARTCR_WL1; 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1023 } 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1024 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1025 if (termios->c_cflag & CMSPAR) { 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1026 if ((termios->c_cflag & CSIZE) != CS8) { 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1027 termios->c_cflag &= ~CSIZE; 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1028 termios->c_cflag |= CS8; 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1029 } 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1030 /* has a space/sticky bit */ 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1031 cr |= LINFLEXD_UARTCR_WL0; 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1032 } 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1033 1312e6586227421 Larisa Grigore 2026-02-16 1034 gcr = readl(port->membase + GCR); 1312e6586227421 Larisa Grigore 2026-02-16 1035 1312e6586227421 Larisa Grigore 2026-02-16 1036 if (termios->c_cflag & CSTOPB) { 1312e6586227421 Larisa Grigore 2026-02-16 1037 /* Use 2 stop bits. */ 1312e6586227421 Larisa Grigore 2026-02-16 1038 cr = (cr & ~LINFLEXD_UARTCR_SBUR_MASK) | 1312e6586227421 Larisa Grigore 2026-02-16 1039 LINFLEXD_UARTCR_SBUR_2SBITS; 1312e6586227421 Larisa Grigore 2026-02-16 1040 /* Set STOP in GCR field for 2 stop bits. */ 1312e6586227421 Larisa Grigore 2026-02-16 1041 gcr = (gcr & ~LINFLEXD_GCR_STOP_MASK) | 1312e6586227421 Larisa Grigore 2026-02-16 1042 LINFLEXD_GCR_STOP_2SBITS; 1312e6586227421 Larisa Grigore 2026-02-16 1043 } else { 1312e6586227421 Larisa Grigore 2026-02-16 1044 /* Use 1 stop bit. */ 1312e6586227421 Larisa Grigore 2026-02-16 1045 cr = (cr & ~LINFLEXD_UARTCR_SBUR_MASK) | 1312e6586227421 Larisa Grigore 2026-02-16 1046 LINFLEXD_UARTCR_SBUR_1SBITS; 1312e6586227421 Larisa Grigore 2026-02-16 1047 /* Set STOP in GCR field for 1 stop bit. */ 1312e6586227421 Larisa Grigore 2026-02-16 1048 gcr = (gcr & ~LINFLEXD_GCR_STOP_MASK) | 1312e6586227421 Larisa Grigore 2026-02-16 1049 LINFLEXD_GCR_STOP_1SBITS; 1312e6586227421 Larisa Grigore 2026-02-16 1050 } 1312e6586227421 Larisa Grigore 2026-02-16 1051 /* Update GCR register. */ 1312e6586227421 Larisa Grigore 2026-02-16 1052 writel(gcr, port->membase + GCR); 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1053 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1054 /* parity must be enabled when CS7 to match 8-bits format */ 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1055 if ((termios->c_cflag & CSIZE) == CS7) 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1056 termios->c_cflag |= PARENB; 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1057 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1058 if ((termios->c_cflag & PARENB)) { 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1059 cr |= LINFLEXD_UARTCR_PCE; 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1060 if (termios->c_cflag & PARODD) 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1061 cr = (cr | LINFLEXD_UARTCR_PC0) & 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1062 (~LINFLEXD_UARTCR_PC1); 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1063 else 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1064 cr = cr & (~LINFLEXD_UARTCR_PC1 & 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1065 ~LINFLEXD_UARTCR_PC0); 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1066 } else { 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1067 cr &= ~LINFLEXD_UARTCR_PCE; 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1068 } 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1069 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1070 port->read_status_mask = 0; 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1071 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1072 if (termios->c_iflag & INPCK) 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1073 port->read_status_mask |= (LINFLEXD_UARTSR_FEF | 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1074 LINFLEXD_UARTSR_PE0 | 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1075 LINFLEXD_UARTSR_PE1 | 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1076 LINFLEXD_UARTSR_PE2 | 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1077 LINFLEXD_UARTSR_PE3); 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1078 if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK)) 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1079 port->read_status_mask |= LINFLEXD_UARTSR_FEF; 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1080 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1081 /* characters to ignore */ 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1082 port->ignore_status_mask = 0; 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1083 if (termios->c_iflag & IGNPAR) 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1084 port->ignore_status_mask |= LINFLEXD_UARTSR_PE; 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1085 if (termios->c_iflag & IGNBRK) { 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1086 port->ignore_status_mask |= LINFLEXD_UARTSR_PE; 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1087 /* 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1088 * if we're ignoring parity and break indicators, 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1089 * ignore overruns too (for real raw support). 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1090 */ 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1091 if (termios->c_iflag & IGNPAR) 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1092 port->ignore_status_mask |= LINFLEXD_UARTSR_BOF; 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1093 } 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1094 1d3f5f07fafc712 Radu Pirea 2026-02-16 @1095 if (port->uartclk) { 1d3f5f07fafc712 Radu Pirea 2026-02-16 1096 ldiv_mul = linflex_ldiv_multiplier(port); 1d3f5f07fafc712 Radu Pirea 2026-02-16 1097 baud = uart_get_baud_rate(port, termios, old, 0, 1d3f5f07fafc712 Radu Pirea 2026-02-16 1098 port->uartclk / ldiv_mul); 1d3f5f07fafc712 Radu Pirea 2026-02-16 1099 1d3f5f07fafc712 Radu Pirea 2026-02-16 1100 /* update the per-port timeout */ 1d3f5f07fafc712 Radu Pirea 2026-02-16 1101 uart_update_timeout(port, termios->c_cflag, baud); 1d3f5f07fafc712 Radu Pirea 2026-02-16 1102 1d3f5f07fafc712 Radu Pirea 2026-02-16 1103 divisr = port->uartclk; 1d3f5f07fafc712 Radu Pirea 2026-02-16 1104 dividr = ((unsigned long)baud * ldiv_mul); 1d3f5f07fafc712 Radu Pirea 2026-02-16 1105 1d3f5f07fafc712 Radu Pirea 2026-02-16 1106 ibr = divisr / dividr; 1d3f5f07fafc712 Radu Pirea 2026-02-16 1107 fbr = ((divisr % dividr) * 16 / dividr) & 0xF; 1d3f5f07fafc712 Radu Pirea 2026-02-16 1108 1d3f5f07fafc712 Radu Pirea 2026-02-16 1109 writel(ibr, port->membase + LINIBRR); 1d3f5f07fafc712 Radu Pirea 2026-02-16 1110 writel(fbr, port->membase + LINFBRR); 1d3f5f07fafc712 Radu Pirea 2026-02-16 1111 } 1d3f5f07fafc712 Radu Pirea 2026-02-16 1112 0b34325c5f79f1f Larisa Grigore 2026-02-16 1113 lfport->dma_rx_timeout = msecs_to_jiffies(DIV_ROUND_UP(10000000, baud)); 0b34325c5f79f1f Larisa Grigore 2026-02-16 1114 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1115 writel(cr, port->membase + UARTCR); 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1116 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1117 cr1 &= ~(LINFLEXD_LINCR1_INIT); 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1118 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1119 writel(cr1, port->membase + LINCR1); 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1120 fb1da4d7f0bec28 Larisa Grigore 2026-02-16 1121 cr |= (LINFLEXD_UARTCR_TXEN) | (LINFLEXD_UARTCR_RXEN); fb1da4d7f0bec28 Larisa Grigore 2026-02-16 1122 writel(cr, port->membase + UARTCR); fb1da4d7f0bec28 Larisa Grigore 2026-02-16 1123 0b34325c5f79f1f Larisa Grigore 2026-02-16 1124 _linflex_start_rx(port); 0b34325c5f79f1f Larisa Grigore 2026-02-16 1125 _linflex_start_tx(port); 0b34325c5f79f1f Larisa Grigore 2026-02-16 1126 7c6725ffd581335 Thomas Gleixner 2023-09-14 1127 uart_port_unlock_irqrestore(port, flags); 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1128 } 09864c1cdf5c537 Stefan-gabriel Mirea 2019-08-09 1129
Hi Larisa,
kernel test robot noticed the following build warnings:
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Larisa-Grigore/serial-linflex... base: https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty.git tty-testing patch link: https://lore.kernel.org/r/20260216150205.212318-13-larisa.grigore%40oss.nxp.... patch subject: [PATCH 12/13] serial: linflexuart: Add DMA support config: i386-randconfig-141-20260217 (https://download.01.org/0day-ci/archive/20260217/202602171109.6YSFXcJ3-lkp@i...) compiler: gcc-14 (Debian 14.2.0-19) 14.2.0 smatch version: v0.5.0-8994-gd50c5a4c
If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags | Reported-by: kernel test robot lkp@intel.com | Reported-by: Dan Carpenter dan.carpenter@linaro.org | Closes: https://lore.kernel.org/r/202602171109.6YSFXcJ3-lkp@intel.com/
smatch warnings: drivers/tty/serial/fsl_linflexuart.c:1441 linflex_probe() warn: missing unwind goto?
vim +1441 drivers/tty/serial/fsl_linflexuart.c
09864c1cdf5c53 Stefan-gabriel Mirea 2019-08-09 1377 static int linflex_probe(struct platform_device *pdev) 09864c1cdf5c53 Stefan-gabriel Mirea 2019-08-09 1378 { 09864c1cdf5c53 Stefan-gabriel Mirea 2019-08-09 1379 struct device_node *np = pdev->dev.of_node; 1d3f5f07fafc71 Radu Pirea 2026-02-16 1380 struct linflex_port *lfport; 09864c1cdf5c53 Stefan-gabriel Mirea 2019-08-09 1381 struct uart_port *sport; 09864c1cdf5c53 Stefan-gabriel Mirea 2019-08-09 1382 struct resource *res; 09864c1cdf5c53 Stefan-gabriel Mirea 2019-08-09 1383 int ret; 09864c1cdf5c53 Stefan-gabriel Mirea 2019-08-09 1384 1d3f5f07fafc71 Radu Pirea 2026-02-16 1385 lfport = devm_kzalloc(&pdev->dev, sizeof(*lfport), GFP_KERNEL); 1d3f5f07fafc71 Radu Pirea 2026-02-16 1386 if (!lfport) 09864c1cdf5c53 Stefan-gabriel Mirea 2019-08-09 1387 return -ENOMEM; 09864c1cdf5c53 Stefan-gabriel Mirea 2019-08-09 1388 1d3f5f07fafc71 Radu Pirea 2026-02-16 1389 sport = &lfport->port; 0b34325c5f79f1 Larisa Grigore 2026-02-16 1390 sport->dev = &pdev->dev; 0b34325c5f79f1 Larisa Grigore 2026-02-16 1391 0b34325c5f79f1 Larisa Grigore 2026-02-16 1392 lfport->dma_tx_chan = dma_request_chan(sport->dev, "tx"); 0b34325c5f79f1 Larisa Grigore 2026-02-16 1393 if (IS_ERR(lfport->dma_tx_chan)) { 0b34325c5f79f1 Larisa Grigore 2026-02-16 1394 ret = PTR_ERR(lfport->dma_tx_chan); 0b34325c5f79f1 Larisa Grigore 2026-02-16 1395 if (ret == -EPROBE_DEFER) 0b34325c5f79f1 Larisa Grigore 2026-02-16 1396 return ret; 0b34325c5f79f1 Larisa Grigore 2026-02-16 1397 0b34325c5f79f1 Larisa Grigore 2026-02-16 1398 dev_info(sport->dev, 0b34325c5f79f1 Larisa Grigore 2026-02-16 1399 "DMA tx channel request failed, operating without tx DMA %ld\n", 0b34325c5f79f1 Larisa Grigore 2026-02-16 1400 PTR_ERR(lfport->dma_tx_chan)); 0b34325c5f79f1 Larisa Grigore 2026-02-16 1401 lfport->dma_tx_chan = NULL; 0b34325c5f79f1 Larisa Grigore 2026-02-16 1402 } 0b34325c5f79f1 Larisa Grigore 2026-02-16 1403 0b34325c5f79f1 Larisa Grigore 2026-02-16 1404 lfport->dma_rx_chan = dma_request_chan(sport->dev, "rx"); 0b34325c5f79f1 Larisa Grigore 2026-02-16 1405 if (IS_ERR(lfport->dma_rx_chan)) { 0b34325c5f79f1 Larisa Grigore 2026-02-16 1406 ret = PTR_ERR(lfport->dma_rx_chan); 0b34325c5f79f1 Larisa Grigore 2026-02-16 1407 if (ret == -EPROBE_DEFER) { 0b34325c5f79f1 Larisa Grigore 2026-02-16 1408 dma_release_channel(lfport->dma_tx_chan); 0b34325c5f79f1 Larisa Grigore 2026-02-16 1409 return ret; 0b34325c5f79f1 Larisa Grigore 2026-02-16 1410 } 0b34325c5f79f1 Larisa Grigore 2026-02-16 1411 0b34325c5f79f1 Larisa Grigore 2026-02-16 1412 dev_info(sport->dev, 0b34325c5f79f1 Larisa Grigore 2026-02-16 1413 "DMA rx channel request failed, operating without rx DMA %ld\n", 0b34325c5f79f1 Larisa Grigore 2026-02-16 1414 PTR_ERR(lfport->dma_rx_chan)); 0b34325c5f79f1 Larisa Grigore 2026-02-16 1415 lfport->dma_rx_chan = NULL; 0b34325c5f79f1 Larisa Grigore 2026-02-16 1416 } 1d3f5f07fafc71 Radu Pirea 2026-02-16 1417 09864c1cdf5c53 Stefan-gabriel Mirea 2019-08-09 1418 ret = of_alias_get_id(np, "serial"); 09864c1cdf5c53 Stefan-gabriel Mirea 2019-08-09 1419 if (ret < 0) { 09864c1cdf5c53 Stefan-gabriel Mirea 2019-08-09 1420 dev_err(&pdev->dev, "failed to get alias id, errno %d\n", ret); 0b34325c5f79f1 Larisa Grigore 2026-02-16 1421 goto linflex_probe_free_dma; 09864c1cdf5c53 Stefan-gabriel Mirea 2019-08-09 1422 } 09864c1cdf5c53 Stefan-gabriel Mirea 2019-08-09 1423 if (ret >= UART_NR) { 09864c1cdf5c53 Stefan-gabriel Mirea 2019-08-09 1424 dev_err(&pdev->dev, "driver limited to %d serial ports\n", 09864c1cdf5c53 Stefan-gabriel Mirea 2019-08-09 1425 UART_NR); 0b34325c5f79f1 Larisa Grigore 2026-02-16 1426 ret = -ENOMEM; 0b34325c5f79f1 Larisa Grigore 2026-02-16 1427 goto linflex_probe_free_dma; 09864c1cdf5c53 Stefan-gabriel Mirea 2019-08-09 1428 } 09864c1cdf5c53 Stefan-gabriel Mirea 2019-08-09 1429 09864c1cdf5c53 Stefan-gabriel Mirea 2019-08-09 1430 sport->line = ret; 09864c1cdf5c53 Stefan-gabriel Mirea 2019-08-09 1431 8c6d7e5fd50b45 Yangtao Li 2023-07-12 1432 sport->membase = devm_platform_get_and_ioremap_resource(pdev, 0, &res); 0b34325c5f79f1 Larisa Grigore 2026-02-16 1433 if (IS_ERR(sport->membase)) { 0b34325c5f79f1 Larisa Grigore 2026-02-16 1434 ret = PTR_ERR(sport->membase); 0b34325c5f79f1 Larisa Grigore 2026-02-16 1435 goto linflex_probe_free_dma; 0b34325c5f79f1 Larisa Grigore 2026-02-16 1436 } 8c6d7e5fd50b45 Yangtao Li 2023-07-12 1437 sport->mapbase = res->start; 09864c1cdf5c53 Stefan-gabriel Mirea 2019-08-09 1438 4e8da86fc1f767 Zhang Shurong 2023-08-26 1439 ret = platform_get_irq(pdev, 0); 4e8da86fc1f767 Zhang Shurong 2023-08-26 1440 if (ret < 0) 4e8da86fc1f767 Zhang Shurong 2023-08-26 @1441 return ret;
No clean up?
4e8da86fc1f767 Zhang Shurong 2023-08-26 1442 09864c1cdf5c53 Stefan-gabriel Mirea 2019-08-09 1443 sport->iotype = UPIO_MEM; 4e8da86fc1f767 Zhang Shurong 2023-08-26 1444 sport->irq = ret; 09864c1cdf5c53 Stefan-gabriel Mirea 2019-08-09 1445 sport->ops = &linflex_pops; 09864c1cdf5c53 Stefan-gabriel Mirea 2019-08-09 1446 sport->flags = UPF_BOOT_AUTOCONF; 4151bbed79f98b Dmitry Safonov 2019-12-13 1447 sport->has_sysrq = IS_ENABLED(CONFIG_SERIAL_FSL_LINFLEXUART_CONSOLE); 09864c1cdf5c53 Stefan-gabriel Mirea 2019-08-09 1448 1d3f5f07fafc71 Radu Pirea 2026-02-16 1449 ret = linflex_init_clk(lfport); 1d3f5f07fafc71 Radu Pirea 2026-02-16 1450 if (ret) 0b34325c5f79f1 Larisa Grigore 2026-02-16 1451 goto linflex_probe_free_dma; 1d3f5f07fafc71 Radu Pirea 2026-02-16 1452 09864c1cdf5c53 Stefan-gabriel Mirea 2019-08-09 1453 linflex_ports[sport->line] = sport; 09864c1cdf5c53 Stefan-gabriel Mirea 2019-08-09 1454 1d3f5f07fafc71 Radu Pirea 2026-02-16 1455 platform_set_drvdata(pdev, lfport); 09864c1cdf5c53 Stefan-gabriel Mirea 2019-08-09 1456 1d3f5f07fafc71 Radu Pirea 2026-02-16 1457 ret = uart_add_one_port(&linflex_reg, sport); 0b34325c5f79f1 Larisa Grigore 2026-02-16 1458 if (ret) { 1d3f5f07fafc71 Radu Pirea 2026-02-16 1459 clk_bulk_disable_unprepare(LINFLEX_CLK_NUM, lfport->clks); 0b34325c5f79f1 Larisa Grigore 2026-02-16 1460 goto linflex_probe_free_dma; 0b34325c5f79f1 Larisa Grigore 2026-02-16 1461 } 0b34325c5f79f1 Larisa Grigore 2026-02-16 1462 0b34325c5f79f1 Larisa Grigore 2026-02-16 1463 return 0; 0b34325c5f79f1 Larisa Grigore 2026-02-16 1464 0b34325c5f79f1 Larisa Grigore 2026-02-16 1465 linflex_probe_free_dma: 0b34325c5f79f1 Larisa Grigore 2026-02-16 1466 if (lfport->dma_tx_chan) 0b34325c5f79f1 Larisa Grigore 2026-02-16 1467 dma_release_channel(lfport->dma_tx_chan); 0b34325c5f79f1 Larisa Grigore 2026-02-16 1468 if (lfport->dma_rx_chan) 0b34325c5f79f1 Larisa Grigore 2026-02-16 1469 dma_release_channel(lfport->dma_rx_chan); 1d3f5f07fafc71 Radu Pirea 2026-02-16 1470 1d3f5f07fafc71 Radu Pirea 2026-02-16 1471 return ret; 09864c1cdf5c53 Stefan-gabriel Mirea 2019-08-09 1472 }
From: Radu Pirea radu-nicolae.pirea@nxp.com
Replace DMA single transactions with DMA cyclic transactions. Characters may be lost between two single DMA transactions if the CPU is running at lower frequencies.
Signed-off-by: Radu Pirea radu-nicolae.pirea@nxp.com Co-developed-by: Larisa Grigore larisa.grigore@oss.nxp.com Signed-off-by: Larisa Grigore larisa.grigore@oss.nxp.com --- drivers/tty/serial/fsl_linflexuart.c | 119 +++++++++++++++------------ 1 file changed, 68 insertions(+), 51 deletions(-)
diff --git a/drivers/tty/serial/fsl_linflexuart.c b/drivers/tty/serial/fsl_linflexuart.c index dff37c68cff0..4598c7ff669e 100644 --- a/drivers/tty/serial/fsl_linflexuart.c +++ b/drivers/tty/serial/fsl_linflexuart.c @@ -6,6 +6,7 @@ * Copyright 2017-2019, 2021-2022, 2025 NXP */
+#include <linux/circ_buf.h> #include <linux/clk.h> #include <linux/console.h> #include <linux/dma-mapping.h> @@ -180,7 +181,7 @@ struct linflex_port { dma_addr_t dma_rx_buf_bus; dma_cookie_t dma_tx_cookie; dma_cookie_t dma_rx_cookie; - unsigned char *dma_rx_buf_virt; + struct circ_buf dma_rx_ring_buf; unsigned int dma_tx_bytes; int dma_tx_in_progress; int dma_rx_in_progress; @@ -210,28 +211,63 @@ to_linflex_port(struct uart_port *uart) return container_of(uart, struct linflex_port, port); }
-static void linflex_copy_rx_to_tty(struct linflex_port *lfport, - struct tty_port *tty, int count) +static void linflex_copy_rx_to_tty(struct linflex_port *lfport) { - size_t copied; - - lfport->port.icount.rx += count; + struct circ_buf *ring_buf = &lfport->dma_rx_ring_buf; + struct tty_port *port = &lfport->port.state->port; + size_t count, received = 0, copied = 0; + struct dma_tx_state state; + enum dma_status dmastat; + int new_head;
- if (!tty) { + if (!port) { dev_err(lfport->port.dev, "No tty port\n"); return; }
+ dmastat = dmaengine_tx_status(lfport->dma_rx_chan, lfport->dma_rx_cookie, &state); + if (dmastat == DMA_ERROR) { + dev_err(lfport->port.dev, "Rx DMA transfer failed!\n"); + return; + } + + new_head = FSL_UART_RX_DMA_BUFFER_SIZE - state.residue; + if (ring_buf->head == new_head) + return; + + ring_buf->head = new_head; dma_sync_single_for_cpu(lfport->port.dev, lfport->dma_rx_buf_bus, FSL_UART_RX_DMA_BUFFER_SIZE, DMA_FROM_DEVICE); - copied = tty_insert_flip_string(tty, - ((unsigned char *)(lfport->dma_rx_buf_virt)), - count);
- if (copied != count) { - WARN_ON(1); - dev_err(lfport->port.dev, "RxData copy to tty layer failed\n"); + if (ring_buf->head > FSL_UART_RX_DMA_BUFFER_SIZE) + dev_err_once(lfport->port.dev, + "Circular buffer head bigger than the buffer size\n"); + + if (ring_buf->head < ring_buf->tail) { + count = FSL_UART_RX_DMA_BUFFER_SIZE - ring_buf->tail; + received += count; + copied += tty_insert_flip_string(port, ring_buf->buf + ring_buf->tail, count); + ring_buf->tail = 0; + lfport->port.icount.rx += count; } + + if (ring_buf->head > ring_buf->tail) { + count = ring_buf->head - ring_buf->tail; + received += count; + copied += tty_insert_flip_string(port, ring_buf->buf + ring_buf->tail, count); + if (ring_buf->head >= FSL_UART_RX_DMA_BUFFER_SIZE) + ring_buf->head = 0; + ring_buf->tail = ring_buf->head; + lfport->port.icount.rx += count; + } + + if (copied != received) + dev_err_once(lfport->port.dev, "RxData copy to tty layer failed\n"); + + dma_sync_single_for_device(lfport->port.dev, lfport->dma_rx_buf_bus, + FSL_UART_RX_DMA_BUFFER_SIZE, + DMA_FROM_DEVICE); + tty_flip_buffer_push(port); }
static void linflex_enable_dma_rx(struct uart_port *port) @@ -348,8 +384,6 @@ static void _linflex_stop_rx(struct uart_port *port) static void linflex_stop_rx(struct uart_port *port) { struct linflex_port *lfport = to_linflex_port(port); - struct dma_tx_state state; - unsigned int count;
_linflex_stop_rx(port);
@@ -357,14 +391,12 @@ static void linflex_stop_rx(struct uart_port *port) return;
dmaengine_pause(lfport->dma_rx_chan); - dmaengine_tx_status(lfport->dma_rx_chan, - lfport->dma_rx_cookie, &state); + linflex_copy_rx_to_tty(lfport); + lfport->dma_rx_ring_buf.head = 0; + lfport->dma_rx_ring_buf.tail = 0; dmaengine_terminate_all(lfport->dma_rx_chan); - count = FSL_UART_RX_DMA_BUFFER_SIZE - state.residue;
lfport->dma_rx_in_progress = 0; - linflex_copy_rx_to_tty(lfport, &port->state->port, count); - tty_flip_buffer_push(&port->state->port); }
static void linflex_put_char(struct uart_port *sport, unsigned char c) @@ -501,11 +533,12 @@ static int linflex_dma_rx(struct linflex_port *lfport) dma_sync_single_for_device(lfport->port.dev, lfport->dma_rx_buf_bus, FSL_UART_RX_DMA_BUFFER_SIZE, DMA_FROM_DEVICE); - lfport->dma_rx_desc = dmaengine_prep_slave_single(lfport->dma_rx_chan, - lfport->dma_rx_buf_bus, - FSL_UART_RX_DMA_BUFFER_SIZE, - DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | - DMA_CTRL_ACK); + lfport->dma_rx_desc = + dmaengine_prep_dma_cyclic(lfport->dma_rx_chan, + lfport->dma_rx_buf_bus, + FSL_UART_RX_DMA_BUFFER_SIZE, + FSL_UART_RX_DMA_BUFFER_SIZE / 2, + DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT);
if (!lfport->dma_rx_desc) { dev_err(lfport->port.dev, "Not able to get desc for rx\n"); @@ -525,11 +558,8 @@ static int linflex_dma_rx(struct linflex_port *lfport) static void linflex_dma_rx_complete(void *arg) { struct linflex_port *lfport = arg; - struct tty_port *port = &lfport->port.state->port; unsigned long flags;
- timer_delete_sync(&lfport->timer); - uart_port_lock_irqsave(&lfport->port, &flags);
/* stopped before? */ @@ -538,34 +568,17 @@ static void linflex_dma_rx_complete(void *arg) return; }
- lfport->dma_rx_in_progress = 0; - linflex_copy_rx_to_tty(lfport, port, FSL_UART_RX_DMA_BUFFER_SIZE); - tty_flip_buffer_push(port); - linflex_dma_rx(lfport); + linflex_copy_rx_to_tty(lfport);
uart_port_unlock_irqrestore(&lfport->port, flags); - mod_timer(&lfport->timer, jiffies + lfport->dma_rx_timeout); }
static void linflex_timer_func(struct timer_list *t) { struct linflex_port *lfport = timer_container_of(lfport, t, timer); - unsigned long flags; - - uart_port_lock_irqsave(&lfport->port, &flags);
- /* stopped before? */ - if (!lfport->dma_rx_in_progress) { - uart_port_unlock_irqrestore(&lfport->port, flags); - return; - } - - linflex_stop_rx(&lfport->port); - linflex_dma_rx(lfport); - - uart_port_unlock_irqrestore(&lfport->port, flags); - mod_timer(&lfport->timer, jiffies + lfport->dma_rx_timeout); + linflex_dma_rx_complete(lfport); }
static void _linflex_start_tx(struct uart_port *port) @@ -827,8 +840,8 @@ static int linflex_dma_rx_request(struct uart_port *port) { struct linflex_port *lfport = to_linflex_port(port); struct dma_slave_config dma_rx_sconfig; - unsigned char *dma_buf; dma_addr_t dma_bus; + char *dma_buf; int ret;
dma_buf = devm_kmalloc(port->dev, FSL_UART_RX_DMA_BUFFER_SIZE, @@ -860,7 +873,9 @@ static int linflex_dma_rx_request(struct uart_port *port) return ret; }
- lfport->dma_rx_buf_virt = dma_buf; + lfport->dma_rx_ring_buf.buf = dma_buf; + lfport->dma_rx_ring_buf.head = 0; + lfport->dma_rx_ring_buf.tail = 0; lfport->dma_rx_buf_bus = dma_bus; lfport->dma_rx_in_progress = 0;
@@ -883,10 +898,12 @@ static void linflex_dma_rx_free(struct uart_port *port)
dma_unmap_single(lfport->port.dev, lfport->dma_rx_buf_bus, FSL_UART_RX_DMA_BUFFER_SIZE, DMA_FROM_DEVICE); - devm_kfree(lfport->port.dev, lfport->dma_rx_buf_virt); + devm_kfree(lfport->port.dev, lfport->dma_rx_ring_buf.buf);
lfport->dma_rx_buf_bus = 0; - lfport->dma_rx_buf_virt = NULL; + lfport->dma_rx_ring_buf.buf = NULL; + lfport->dma_rx_ring_buf.head = 0; + lfport->dma_rx_ring_buf.tail = 0; }
static int linflex_startup(struct uart_port *port)
Hi Larisa,
On Mon, Feb 16, 2026 at 04:01:52PM +0100, Larisa Grigore wrote:
This patchset enhances the LINFlexD UART driver and its device tree bindings to support DMA transfers, configurable clock inputs, dynamic baudrate changes, and termios features. It also includes a series of fixes and improvements to ensure reliable operation across various modes and configurations.
The changes added can be summarized as follows:
- Fixes with respect to FIFO handling, locking, interrupt related registers and
INITM mode transition.
Tested this series with the default devicetree configuration by booting the board to a login prompt about 200 times. Without the series applied, I was seeing a bug roughly every 30-50 boots where the kernel would would hang in linflex_console_putchar() waiting for DTFTFF. In my tests with the series applied, I didn't see any regressions and the bug no longer appeared. Thanks for the fix!
Tested-by: Jared Kangas jkangas@redhat.com # S32G3, interrupt-driven
- Removal of the earlycon workaround, as proper FIFO handling and INITM
transitions now ensure stable behavior. 3. Support for configurable stop bits and dynamic baudrate changes based on clock inputs and termios settings. 4. Optional DMA support for RX and TX paths, preventing character loss during high-throughput operations like copy-paste. Cyclic DMA is used for RX to avoid gaps between transactions.
Larisa Grigore (8): serial: linflexuart: Clean SLEEP bit in LINCR1 after suspend serial: linflexuart: Check FIFO full before writing serial: linflexuart: Correctly clear UARTSR in buffer mode serial: linflexuart: Update RXEN/TXEN outside INITM mode serial: linflexuart: Ensure FIFO is empty when entering INITM serial: linflexuart: Revert earlycon workaround serial: linflexuart: Add support for configurable stop bits serial: linflexuart: Add DMA support
Radu Pirea (5): serial: linflexuart: Fix locking in set_termios dt-bindings: serial: fsl-linflexuart: add clock input properties dt-bindings: serial: fsl-linflexuart: add dma properties serial: linflexuart: Add support for changing baudrate serial: linflexuart: Avoid stopping DMA during receive operations
.../bindings/serial/fsl,s32-linflexuart.yaml | 31 + drivers/tty/serial/fsl_linflexuart.c | 972 +++++++++++++++--- 2 files changed, 846 insertions(+), 157 deletions(-)
-- 2.47.0
linaro-mm-sig@lists.linaro.org