From: Claudiu Beznea claudiu.beznea.uj@bp.renesas.com
Hi,
Commit 653143ed73ec ("serial: sh-sci: Check if TX data was written to device in .tx_empty()") doesn't apply cleanly on top of v5.10.y stable tree. This series adjust it. Along with it, propose for backporting other sh-sci fixes.
Please provide your feedback.
Thank you, Claudiu Beznea
Claudiu Beznea (4): serial: sh-sci: Check if TX data was written to device in .tx_empty() serial: sh-sci: Move runtime PM enable to sci_probe_single() serial: sh-sci: Clean sci_ports[0] after at earlycon exit serial: sh-sci: Increment the runtime usage counter for the earlycon device
drivers/tty/serial/sh-sci.c | 97 ++++++++++++++++++++++++++++++------- 1 file changed, 79 insertions(+), 18 deletions(-)
From: Claudiu Beznea claudiu.beznea.uj@bp.renesas.com
commit 7cc0e0a43a91052477c2921f924a37d9c3891f0c upstream.
On the Renesas RZ/G3S, when doing suspend to RAM, the uart_suspend_port() is called. The uart_suspend_port() calls 3 times the struct uart_port::ops::tx_empty() before shutting down the port.
According to the documentation, the struct uart_port::ops::tx_empty() API tests whether the transmitter FIFO and shifter for the port is empty.
The Renesas RZ/G3S SCIFA IP reports the number of data units stored in the transmit FIFO through the FDR (FIFO Data Count Register). The data units in the FIFOs are written in the shift register and transmitted from there. The TEND bit in the Serial Status Register reports if the data was transmitted from the shift register.
In the previous code, in the tx_empty() API implemented by the sh-sci driver, it is considered that the TX is empty if the hardware reports the TEND bit set and the number of data units in the FIFO is zero.
According to the HW manual, the TEND bit has the following meaning:
0: Transmission is in the waiting state or in progress. 1: Transmission is completed.
It has been noticed that when opening the serial device w/o using it and then switch to a power saving mode, the tx_empty() call in the uart_port_suspend() function fails, leading to the "Unable to drain transmitter" message being printed on the console. This is because the TEND=0 if nothing has been transmitted and the FIFOs are empty. As the TEND=0 has double meaning (waiting state, in progress) we can't determined the scenario described above.
Add a software workaround for this. This sets a variable if any data has been sent on the serial console (when using PIO) or if the DMA callback has been called (meaning something has been transmitted). In the tx_empty() API the status of the DMA transaction is also checked and if it is completed or in progress the code falls back in checking the hardware registers instead of relying on the software variable.
Fixes: 73a19e4c0301 ("serial: sh-sci: Add DMA support.") Cc: stable@vger.kernel.org Signed-off-by: Claudiu Beznea claudiu.beznea.uj@bp.renesas.com Link: https://lore.kernel.org/r/20241125115856.513642-1-claudiu.beznea.uj@bp.renes... Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org [claudiu.beznea: fixed conflict by: - keeping serial_port_out() instead of sci_port_out() in sci_transmit_chars() - keeping !uart_circ_empty(xmit) condition in sci_dma_tx_complete(), after s->tx_occurred = true; assignement] Signed-off-by: Claudiu Beznea claudiu.beznea.uj@bp.renesas.com --- drivers/tty/serial/sh-sci.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+)
diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 19738d8a05e1..bdb156d4ac4d 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -157,6 +157,7 @@ struct sci_port {
bool has_rtscts; bool autorts; + bool tx_occurred; };
#define SCI_NPORTS CONFIG_SERIAL_SH_SCI_NR_UARTS @@ -806,6 +807,7 @@ static void sci_transmit_chars(struct uart_port *port) { struct circ_buf *xmit = &port->state->xmit; unsigned int stopped = uart_tx_stopped(port); + struct sci_port *s = to_sci_port(port); unsigned short status; unsigned short ctrl; int count; @@ -837,6 +839,7 @@ static void sci_transmit_chars(struct uart_port *port) }
serial_port_out(port, SCxTDR, c); + s->tx_occurred = true;
port->icount.tx++; } while (--count > 0); @@ -1204,6 +1207,8 @@ static void sci_dma_tx_complete(void *arg) if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(port);
+ s->tx_occurred = true; + if (!uart_circ_empty(xmit)) { s->cookie_tx = 0; schedule_work(&s->work_tx); @@ -1686,6 +1691,19 @@ static void sci_flush_buffer(struct uart_port *port) s->cookie_tx = -EINVAL; } } + +static void sci_dma_check_tx_occurred(struct sci_port *s) +{ + struct dma_tx_state state; + enum dma_status status; + + if (!s->chan_tx) + return; + + status = dmaengine_tx_status(s->chan_tx, s->cookie_tx, &state); + if (status == DMA_COMPLETE || status == DMA_IN_PROGRESS) + s->tx_occurred = true; +} #else /* !CONFIG_SERIAL_SH_SCI_DMA */ static inline void sci_request_dma(struct uart_port *port) { @@ -1695,6 +1713,10 @@ static inline void sci_free_dma(struct uart_port *port) { }
+static void sci_dma_check_tx_occurred(struct sci_port *s) +{ +} + #define sci_flush_buffer NULL #endif /* !CONFIG_SERIAL_SH_SCI_DMA */
@@ -2007,6 +2029,12 @@ static unsigned int sci_tx_empty(struct uart_port *port) { unsigned short status = serial_port_in(port, SCxSR); unsigned short in_tx_fifo = sci_txfill(port); + struct sci_port *s = to_sci_port(port); + + sci_dma_check_tx_occurred(s); + + if (!s->tx_occurred) + return TIOCSER_TEMT;
return (status & SCxSR_TEND(port)) && !in_tx_fifo ? TIOCSER_TEMT : 0; } @@ -2177,6 +2205,7 @@ static int sci_startup(struct uart_port *port)
dev_dbg(port->dev, "%s(%d)\n", __func__, port->line);
+ s->tx_occurred = false; sci_request_dma(port);
ret = sci_request_irq(s);
[ Sasha's backport helper bot ]
Hi,
✅ All tests passed successfully. No issues detected. No action required from the submitter.
The upstream commit SHA1 provided is correct: 7cc0e0a43a91052477c2921f924a37d9c3891f0c
WARNING: Author mismatch between patch and upstream commit: Backport author: Claudiuclaudiu.beznea@tuxon.dev Commit author: Claudiu Bezneaclaudiu.beznea.uj@bp.renesas.com
Status in newer kernel trees: 6.15.y | Present (exact SHA1) 6.14.y | Present (exact SHA1) 6.12.y | Present (different SHA1: 7415bc5198ef) 6.6.y | Not found 6.1.y | Not found 5.15.y | Not found
Note: The patch differs from the upstream commit: --- 1: 7cc0e0a43a910 ! 1: 41467f9d0eef9 serial: sh-sci: Check if TX data was written to device in .tx_empty() @@ Metadata ## Commit message ## serial: sh-sci: Check if TX data was written to device in .tx_empty()
+ commit 7cc0e0a43a91052477c2921f924a37d9c3891f0c upstream. + On the Renesas RZ/G3S, when doing suspend to RAM, the uart_suspend_port() is called. The uart_suspend_port() calls 3 times the struct uart_port::ops::tx_empty() before shutting down the port. @@ Commit message Signed-off-by: Claudiu Beznea claudiu.beznea.uj@bp.renesas.com Link: https://lore.kernel.org/r/20241125115856.513642-1-claudiu.beznea.uj@bp.renes... Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org + [claudiu.beznea: fixed conflict by: + - keeping serial_port_out() instead of sci_port_out() in + sci_transmit_chars() + - keeping !uart_circ_empty(xmit) condition in sci_dma_tx_complete(), + after s->tx_occurred = true; assignement] + Signed-off-by: Claudiu Beznea claudiu.beznea.uj@bp.renesas.com
## drivers/tty/serial/sh-sci.c ## @@ drivers/tty/serial/sh-sci.c: struct sci_port { @@ drivers/tty/serial/sh-sci.c: struct sci_port { #define SCI_NPORTS CONFIG_SERIAL_SH_SCI_NR_UARTS @@ drivers/tty/serial/sh-sci.c: static void sci_transmit_chars(struct uart_port *port) { - struct tty_port *tport = &port->state->port; + struct circ_buf *xmit = &port->state->xmit; unsigned int stopped = uart_tx_stopped(port); + struct sci_port *s = to_sci_port(port); unsigned short status; @@ drivers/tty/serial/sh-sci.c: static void sci_transmit_chars(struct uart_port *po @@ drivers/tty/serial/sh-sci.c: static void sci_transmit_chars(struct uart_port *port) }
- sci_serial_out(port, SCxTDR, c); + serial_port_out(port, SCxTDR, c); + s->tx_occurred = true;
port->icount.tx++; } while (--count > 0); @@ drivers/tty/serial/sh-sci.c: static void sci_dma_tx_complete(void *arg) - if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(port);
+ s->tx_occurred = true; + - if (!kfifo_is_empty(&tport->xmit_fifo)) { + if (!uart_circ_empty(xmit)) { s->cookie_tx = 0; schedule_work(&s->work_tx); @@ drivers/tty/serial/sh-sci.c: static void sci_flush_buffer(struct uart_port *port) @@ drivers/tty/serial/sh-sci.c: static inline void sci_free_dma(struct uart_port *p
@@ drivers/tty/serial/sh-sci.c: static unsigned int sci_tx_empty(struct uart_port *port) { - unsigned short status = sci_serial_in(port, SCxSR); + unsigned short status = serial_port_in(port, SCxSR); unsigned short in_tx_fifo = sci_txfill(port); + struct sci_port *s = to_sci_port(port); + ---
Results of testing on various branches:
| Branch | Patch Apply | Build Test | |---------------------------|-------------|------------| | stable/linux-5.10.y | Success | Success |
On Tue, Jun 03, 2025 at 12:36:58PM +0300, Claudiu wrote:
From: Claudiu Beznea claudiu.beznea.uj@bp.renesas.com
commit 7cc0e0a43a91052477c2921f924a37d9c3891f0c upstream.
On the Renesas RZ/G3S, when doing suspend to RAM, the uart_suspend_port() is called. The uart_suspend_port() calls 3 times the struct uart_port::ops::tx_empty() before shutting down the port.
According to the documentation, the struct uart_port::ops::tx_empty() API tests whether the transmitter FIFO and shifter for the port is empty.
The Renesas RZ/G3S SCIFA IP reports the number of data units stored in the transmit FIFO through the FDR (FIFO Data Count Register). The data units in the FIFOs are written in the shift register and transmitted from there. The TEND bit in the Serial Status Register reports if the data was transmitted from the shift register.
In the previous code, in the tx_empty() API implemented by the sh-sci driver, it is considered that the TX is empty if the hardware reports the TEND bit set and the number of data units in the FIFO is zero.
According to the HW manual, the TEND bit has the following meaning:
0: Transmission is in the waiting state or in progress. 1: Transmission is completed.
It has been noticed that when opening the serial device w/o using it and then switch to a power saving mode, the tx_empty() call in the uart_port_suspend() function fails, leading to the "Unable to drain transmitter" message being printed on the console. This is because the TEND=0 if nothing has been transmitted and the FIFOs are empty. As the TEND=0 has double meaning (waiting state, in progress) we can't determined the scenario described above.
Add a software workaround for this. This sets a variable if any data has been sent on the serial console (when using PIO) or if the DMA callback has been called (meaning something has been transmitted). In the tx_empty() API the status of the DMA transaction is also checked and if it is completed or in progress the code falls back in checking the hardware registers instead of relying on the software variable.
Fixes: 73a19e4c0301 ("serial: sh-sci: Add DMA support.") Cc: stable@vger.kernel.org Signed-off-by: Claudiu Beznea claudiu.beznea.uj@bp.renesas.com Link: https://lore.kernel.org/r/20241125115856.513642-1-claudiu.beznea.uj@bp.renes... Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org [claudiu.beznea: fixed conflict by:
- keeping serial_port_out() instead of sci_port_out() in sci_transmit_chars()
- keeping !uart_circ_empty(xmit) condition in sci_dma_tx_complete(), after s->tx_occurred = true; assignement]
Signed-off-by: Claudiu Beznea claudiu.beznea.uj@bp.renesas.com
drivers/tty/serial/sh-sci.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+)
For obvious reasons, we can't take patches ONLY for older stable kernels, and not newer ones. Otherwise you would have kernel releases without any fixes and get a regression when moving to a newer tree, which you yourself don't want to have happen :)
So can you resend these patches for all of the needed branches, including this one, as I'll drop this from my review queue now.
thanks,
greg k-h
From: Claudiu Beznea claudiu.beznea.uj@bp.renesas.com
commit 239f11209e5f282e16f5241b99256e25dd0614b6 upstream.
Relocate the runtime PM enable operation to sci_probe_single(). This change prepares the codebase for upcoming fixes.
While at it, replace the existing logic with a direct call to devm_pm_runtime_enable() and remove sci_cleanup_single(). The devm_pm_runtime_enable() function automatically handles disabling runtime PM during driver removal.
Reviewed-by: Geert Uytterhoeven geert+renesas@glider.be Signed-off-by: Claudiu Beznea claudiu.beznea.uj@bp.renesas.com Link: https://lore.kernel.org/r/20250116182249.3828577-3-claudiu.beznea.uj@bp.rene... Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org Signed-off-by: Claudiu Beznea claudiu.beznea.uj@bp.renesas.com --- drivers/tty/serial/sh-sci.c | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-)
diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index bdb156d4ac4d..478fa745ad99 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -3021,10 +3021,6 @@ static int sci_init_single(struct platform_device *dev, ret = sci_init_clocks(sci_port, &dev->dev); if (ret < 0) return ret; - - port->dev = &dev->dev; - - pm_runtime_enable(&dev->dev); }
port->type = p->type; @@ -3054,11 +3050,6 @@ static int sci_init_single(struct platform_device *dev, return 0; }
-static void sci_cleanup_single(struct sci_port *port) -{ - pm_runtime_disable(port->port.dev); -} - #if defined(CONFIG_SERIAL_SH_SCI_CONSOLE) || \ defined(CONFIG_SERIAL_SH_SCI_EARLYCON) static void serial_console_putchar(struct uart_port *port, int ch) @@ -3216,8 +3207,6 @@ static int sci_remove(struct platform_device *dev) sci_ports_in_use &= ~BIT(port->port.line); uart_remove_one_port(&sci_uart_driver, &port->port);
- sci_cleanup_single(port); - if (port->port.fifosize > 1) device_remove_file(&dev->dev, &dev_attr_rx_fifo_trigger); if (type == PORT_SCIFA || type == PORT_SCIFB || type == PORT_HSCIF) @@ -3348,6 +3337,11 @@ static int sci_probe_single(struct platform_device *dev, if (ret) return ret;
+ sciport->port.dev = &dev->dev; + ret = devm_pm_runtime_enable(&dev->dev); + if (ret) + return ret; + sciport->gpios = mctrl_gpio_init(&sciport->port, 0); if (IS_ERR(sciport->gpios)) return PTR_ERR(sciport->gpios); @@ -3361,13 +3355,7 @@ static int sci_probe_single(struct platform_device *dev, sciport->port.flags |= UPF_HARD_FLOW; }
- ret = uart_add_one_port(&sci_uart_driver, &sciport->port); - if (ret) { - sci_cleanup_single(sciport); - return ret; - } - - return 0; + return uart_add_one_port(&sci_uart_driver, &sciport->port); }
static int sci_probe(struct platform_device *dev)
[ Sasha's backport helper bot ]
Hi,
✅ All tests passed successfully. No issues detected. No action required from the submitter.
The upstream commit SHA1 provided is correct: 239f11209e5f282e16f5241b99256e25dd0614b6
WARNING: Author mismatch between patch and upstream commit: Backport author: Claudiuclaudiu.beznea@tuxon.dev Commit author: Claudiu Bezneaclaudiu.beznea.uj@bp.renesas.com
Status in newer kernel trees: 6.15.y | Present (exact SHA1) 6.14.y | Present (exact SHA1) 6.12.y | Not found 6.6.y | Not found 6.1.y | Not found 5.15.y | Not found
Note: The patch differs from the upstream commit: --- 1: 239f11209e5f2 ! 1: ef2e932fb64e7 serial: sh-sci: Move runtime PM enable to sci_probe_single() @@ Metadata ## Commit message ## serial: sh-sci: Move runtime PM enable to sci_probe_single()
+ commit 239f11209e5f282e16f5241b99256e25dd0614b6 upstream. + Relocate the runtime PM enable operation to sci_probe_single(). This change prepares the codebase for upcoming fixes.
@@ Commit message Signed-off-by: Claudiu Beznea claudiu.beznea.uj@bp.renesas.com Link: https://lore.kernel.org/r/20250116182249.3828577-3-claudiu.beznea.uj@bp.rene... Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org + Signed-off-by: Claudiu Beznea claudiu.beznea.uj@bp.renesas.com
## drivers/tty/serial/sh-sci.c ## @@ drivers/tty/serial/sh-sci.c: static int sci_init_single(struct platform_device *dev, @@ drivers/tty/serial/sh-sci.c: static int sci_init_single(struct platform_device * - #if defined(CONFIG_SERIAL_SH_SCI_CONSOLE) || \ defined(CONFIG_SERIAL_SH_SCI_EARLYCON) - static void serial_console_putchar(struct uart_port *port, unsigned char ch) -@@ drivers/tty/serial/sh-sci.c: static void sci_remove(struct platform_device *dev) + static void serial_console_putchar(struct uart_port *port, int ch) +@@ drivers/tty/serial/sh-sci.c: static int sci_remove(struct platform_device *dev) sci_ports_in_use &= ~BIT(port->port.line); uart_remove_one_port(&sci_uart_driver, &port->port);
---
Results of testing on various branches:
| Branch | Patch Apply | Build Test | |---------------------------|-------------|------------| | stable/linux-5.15.y | Success | Success |
From: Claudiu Beznea claudiu.beznea.uj@bp.renesas.com
commit 5f1017069933489add0c08659673443c9905659e upstream.
The early_console_setup() function initializes sci_ports[0].port with an object of type struct uart_port obtained from the struct earlycon_device passed as an argument to early_console_setup().
Later, during serial port probing, the serial port used as earlycon (e.g., port A) might be remapped to a different position in the sci_ports[] array, and a different serial port (e.g., port B) might be assigned to slot 0. For example:
sci_ports[0] = port B sci_ports[X] = port A
In this scenario, the new port mapped at index zero (port B) retains the data associated with the earlycon configuration. Consequently, after the Linux boot process, any access to the serial port now mapped to sci_ports[0] (port B) will block the original earlycon port (port A).
To address this, introduce an early_console_exit() function to clean up sci_ports[0] when earlycon is exited.
To prevent the cleanup of sci_ports[0] while the serial device is still being used by earlycon, introduce the struct sci_port::probing flag and account for it in early_console_exit().
Fixes: 0b0cced19ab1 ("serial: sh-sci: Add CONFIG_SERIAL_EARLYCON support") Cc: stable@vger.kernel.org Signed-off-by: Claudiu Beznea claudiu.beznea.uj@bp.renesas.com Link: https://lore.kernel.org/r/20250116182249.3828577-5-claudiu.beznea.uj@bp.rene... Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org Signed-off-by: Claudiu Beznea claudiu.beznea.uj@bp.renesas.com --- drivers/tty/serial/sh-sci.c | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-)
diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 478fa745ad99..000a920727b3 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -166,6 +166,7 @@ static struct sci_port sci_ports[SCI_NPORTS]; static unsigned long sci_ports_in_use; static struct uart_driver sci_uart_driver; static bool sci_uart_earlycon; +static bool sci_uart_earlycon_dev_probing;
static inline struct sci_port * to_sci_port(struct uart_port *uart) @@ -3308,7 +3309,8 @@ static struct plat_sci_port *sci_parse_dt(struct platform_device *pdev, static int sci_probe_single(struct platform_device *dev, unsigned int index, struct plat_sci_port *p, - struct sci_port *sciport) + struct sci_port *sciport, + struct resource *sci_res) { int ret;
@@ -3355,6 +3357,14 @@ static int sci_probe_single(struct platform_device *dev, sciport->port.flags |= UPF_HARD_FLOW; }
+ if (sci_uart_earlycon && sci_ports[0].port.mapbase == sci_res->start) { + /* + * Skip cleanup the sci_port[0] in early_console_exit(), this + * port is the same as the earlycon one. + */ + sci_uart_earlycon_dev_probing = true; + } + return uart_add_one_port(&sci_uart_driver, &sciport->port); }
@@ -3413,7 +3423,7 @@ static int sci_probe(struct platform_device *dev)
platform_set_drvdata(dev, sp);
- ret = sci_probe_single(dev, dev_id, p, sp); + ret = sci_probe_single(dev, dev_id, p, sp, res); if (ret) return ret;
@@ -3496,6 +3506,22 @@ sh_early_platform_init_buffer("earlyprintk", &sci_driver, #ifdef CONFIG_SERIAL_SH_SCI_EARLYCON static struct plat_sci_port port_cfg;
+static int early_console_exit(struct console *co) +{ + struct sci_port *sci_port = &sci_ports[0]; + + /* + * Clean the slot used by earlycon. A new SCI device might + * map to this slot. + */ + if (!sci_uart_earlycon_dev_probing) { + memset(sci_port, 0, sizeof(*sci_port)); + sci_uart_earlycon = false; + } + + return 0; +} + static int __init early_console_setup(struct earlycon_device *device, int type) { @@ -3515,6 +3541,8 @@ static int __init early_console_setup(struct earlycon_device *device, SCSCR_RE | SCSCR_TE | port_cfg.scscr);
device->con->write = serial_console_write; + device->con->exit = early_console_exit; + return 0; } static int __init sci_early_console_setup(struct earlycon_device *device,
[ Sasha's backport helper bot ]
Hi,
✅ All tests passed successfully. No issues detected. No action required from the submitter.
The upstream commit SHA1 provided is correct: 5f1017069933489add0c08659673443c9905659e
WARNING: Author mismatch between patch and upstream commit: Backport author: Claudiuclaudiu.beznea@tuxon.dev Commit author: Claudiu Bezneaclaudiu.beznea.uj@bp.renesas.com
Status in newer kernel trees: 6.15.y | Present (exact SHA1) 6.14.y | Present (exact SHA1) 6.12.y | Not found 6.6.y | Not found 6.1.y | Not found 5.15.y | Not found
Note: The patch differs from the upstream commit: --- 1: 5f10170699334 ! 1: b68647923713a serial: sh-sci: Clean sci_ports[0] after at earlycon exit @@ Metadata ## Commit message ## serial: sh-sci: Clean sci_ports[0] after at earlycon exit
+ commit 5f1017069933489add0c08659673443c9905659e upstream. + The early_console_setup() function initializes sci_ports[0].port with an object of type struct uart_port obtained from the struct earlycon_device passed as an argument to early_console_setup(). @@ Commit message Signed-off-by: Claudiu Beznea claudiu.beznea.uj@bp.renesas.com Link: https://lore.kernel.org/r/20250116182249.3828577-5-claudiu.beznea.uj@bp.rene... Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org + Signed-off-by: Claudiu Beznea claudiu.beznea.uj@bp.renesas.com
## drivers/tty/serial/sh-sci.c ## @@ drivers/tty/serial/sh-sci.c: static struct sci_port sci_ports[SCI_NPORTS]; ---
Results of testing on various branches:
| Branch | Patch Apply | Build Test | |---------------------------|-------------|------------| | stable/linux-5.15.y | Success | Success |
From: Claudiu Beznea claudiu.beznea.uj@bp.renesas.com
commit 651dee03696e1dfde6d9a7e8664bbdcd9a10ea7f upstream.
In the sh-sci driver, serial ports are mapped to the sci_ports[] array, with earlycon mapped at index zero.
The uart_add_one_port() function eventually calls __device_attach(), which, in turn, calls pm_request_idle(). The identified code path is as follows:
uart_add_one_port() -> serial_ctrl_register_port() -> serial_core_register_port() -> serial_core_port_device_add() -> serial_base_port_add() -> device_add() -> bus_probe_device() -> device_initial_probe() -> __device_attach() -> // ... if (dev->p->dead) { // ... } else if (dev->driver) { // ... } else { // ... pm_request_idle(dev); // ... }
The earlycon device clocks are enabled by the bootloader. However, the pm_request_idle() call in __device_attach() disables the SCI port clocks while earlycon is still active.
The earlycon write function, serial_console_write(), calls sci_poll_put_char() via serial_console_putchar(). If the SCI port clocks are disabled, writing to earlycon may sometimes cause the SR.TDFE bit to remain unset indefinitely, causing the while loop in sci_poll_put_char() to never exit. On single-core SoCs, this can result in the system being blocked during boot when this issue occurs.
To resolve this, increment the runtime PM usage counter for the earlycon SCI device before registering the UART port.
Fixes: 0b0cced19ab1 ("serial: sh-sci: Add CONFIG_SERIAL_EARLYCON support") Cc: stable@vger.kernel.org Signed-off-by: Claudiu Beznea claudiu.beznea.uj@bp.renesas.com Link: https://lore.kernel.org/r/20250116182249.3828577-6-claudiu.beznea.uj@bp.rene... Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org Signed-off-by: Claudiu Beznea claudiu.beznea.uj@bp.renesas.com --- drivers/tty/serial/sh-sci.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+)
diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 000a920727b3..3b30cf09bb9b 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -3358,6 +3358,22 @@ static int sci_probe_single(struct platform_device *dev, }
if (sci_uart_earlycon && sci_ports[0].port.mapbase == sci_res->start) { + /* + * In case: + * - this is the earlycon port (mapped on index 0 in sci_ports[]) and + * - it now maps to an alias other than zero and + * - the earlycon is still alive (e.g., "earlycon keep_bootcon" is + * available in bootargs) + * + * we need to avoid disabling clocks and PM domains through the runtime + * PM APIs called in __device_attach(). For this, increment the runtime + * PM reference counter (the clocks and PM domains were already enabled + * by the bootloader). Otherwise the earlycon may access the HW when it + * has no clocks enabled leading to failures (infinite loop in + * sci_poll_put_char()). + */ + pm_runtime_get_noresume(&dev->dev); + /* * Skip cleanup the sci_port[0] in early_console_exit(), this * port is the same as the earlycon one.
[ Sasha's backport helper bot ]
Hi,
✅ All tests passed successfully. No issues detected. No action required from the submitter.
The upstream commit SHA1 provided is correct: 651dee03696e1dfde6d9a7e8664bbdcd9a10ea7f
WARNING: Author mismatch between patch and upstream commit: Backport author: Claudiuclaudiu.beznea@tuxon.dev Commit author: Claudiu Bezneaclaudiu.beznea.uj@bp.renesas.com
Status in newer kernel trees: 6.15.y | Present (exact SHA1) 6.14.y | Present (exact SHA1) 6.12.y | Not found 6.6.y | Not found 6.1.y | Not found 5.15.y | Not found
Note: The patch differs from the upstream commit: --- 1: 651dee03696e1 ! 1: 523044130a840 serial: sh-sci: Increment the runtime usage counter for the earlycon device @@ Metadata ## Commit message ## serial: sh-sci: Increment the runtime usage counter for the earlycon device
+ commit 651dee03696e1dfde6d9a7e8664bbdcd9a10ea7f upstream. + In the sh-sci driver, serial ports are mapped to the sci_ports[] array, with earlycon mapped at index zero.
@@ Commit message Signed-off-by: Claudiu Beznea claudiu.beznea.uj@bp.renesas.com Link: https://lore.kernel.org/r/20250116182249.3828577-6-claudiu.beznea.uj@bp.rene... Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org + Signed-off-by: Claudiu Beznea claudiu.beznea.uj@bp.renesas.com
## drivers/tty/serial/sh-sci.c ## @@ drivers/tty/serial/sh-sci.c: static int sci_probe_single(struct platform_device *dev, ---
Results of testing on various branches:
| Branch | Patch Apply | Build Test | |---------------------------|-------------|------------| | stable/linux-5.15.y | Success | Success |
linux-stable-mirror@lists.linaro.org