* Re: [PATCH tty v4 2/6] serial: Replace driver usage of UPF_CONS_FLOW
From: John Ogness @ 2026-05-07 9:50 UTC (permalink / raw)
To: Greg Kroah-Hartman, Jiri Slaby, Krzysztof Kozlowski, Alim Akhtar
Cc: Andy Shevchenko, linux-kernel, David S. Miller,
Ilpo Järvinen, Andy Shevchenko, Thomas Fourier, Kees Cook,
linux-serial, linux-arm-kernel, linux-samsung-soc, sparclinux
In-Reply-To: <20260506121606.5805-3-john.ogness@linutronix.de>
Hi,
(This email is particularly directed at the Samsung folks.)
Responding to Sashiko:
https://sashiko.dev/#/patchset/20260506121606.5805-1-john.ogness%40linutronix.de
On 2026-05-06, Sashiko wrote:
>> diff --git a/drivers/tty/serial/samsung_tty.c b/drivers/tty/serial/samsung_tty.c
>> index e27806bf2cf3e..2f94fc798cffb 100644
>> --- a/drivers/tty/serial/samsung_tty.c
>> +++ b/drivers/tty/serial/samsung_tty.c
>> @@ -319,7 +319,7 @@ static void s3c24xx_serial_stop_tx(struct uart_port *port)
>> ourport->tx_enabled = 0;
>> ourport->tx_in_progress = 0;
>>
>> - if (port->flags & UPF_CONS_FLOW)
>> + if (uart_cons_flow_enabled(port))
>> s3c24xx_serial_rx_enable(port);
>
> This isn't a new bug introduced by this patch, but does this code
> result in a recursive spinlock acquisition and deadlock when console
> flow control is enabled?
>
> The serial core framework calls port->ops->stop_tx() with the
> port->lock spinlock already held. When uart_cons_flow_enabled(port)
> evaluates to true, s3c24xx_serial_stop_tx() invokes
> s3c24xx_serial_rx_enable().
>
> Looking at s3c24xx_serial_rx_enable(), it unconditionally attempts to
> acquire the exact same lock again:
>
> static void s3c24xx_serial_rx_enable(struct uart_port *port)
> {
> struct s3c24xx_uart_port *ourport = to_ourport(port);
> unsigned long flags;
> ...
> uart_port_lock_irqsave(port, &flags);
> ...
> }
>
> Because spinlocks are not recursive in the kernel, it appears this
> sequence would cause an immediate deadlock on the same CPU.
Although out of scope for my series, the reported issue is real. Perhaps
something for Krzysztof or Alim to look into.
>> @@ -493,7 +493,7 @@ static void s3c24xx_serial_start_tx(struct uart_port *port)
>> struct tty_port *tport = &port->state->port;
>>
>> if (!ourport->tx_enabled) {
>> - if (port->flags & UPF_CONS_FLOW)
>> + if (uart_cons_flow_enabled(port))
>> s3c24xx_serial_rx_disable(port);
>
> Similarly here, s3c24xx_serial_start_tx() is also called with
> port->lock held, and s3c24xx_serial_rx_disable() will attempt to
> acquire port->lock again:
>
> static void s3c24xx_serial_rx_disable(struct uart_port *port)
> {
> struct s3c24xx_uart_port *ourport = to_ourport(port);
> unsigned long flags;
> ...
> uart_port_lock_irqsave(port, &flags);
> ...
> }
>
> Could this pre-existing locking issue in the samsung_tty driver be
> addressed so that the rx enable/disable helpers do not try to take the
> port lock when it is already held by the caller?
Also legitimate. But out of scope for my series.
John Ogness
^ permalink raw reply
* Re: [PATCH v7 6/6] ARM: zte: defconfig: Add a zx29 defconfig file
From: Linus Walleij @ 2026-05-07 12:24 UTC (permalink / raw)
To: Stefan Dösinger
Cc: Jonathan Corbet, Shuah Khan, Russell King, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Arnd Bergmann,
Krzysztof Kozlowski, Alexandre Belloni, Drew Fustini,
Greg Kroah-Hartman, Jiri Slaby, linux-doc, linux-kernel,
linux-arm-kernel, devicetree, linux-serial
In-Reply-To: <5379905.31r3eYUQgx@strix>
Hi Stefan,
On Wed, May 6, 2026 at 7:39 PM Stefan Dösinger
<stefandoesinger@gmail.com> wrote:
> I'll send a v8 with some of Sashiko's (very impressive)
> findings but keep the defconfig.
Maybe not send all patches to soc@kernel.org right now because they
end up in the patch tracker.
For a new platform that may be OK though...
Nominall it should be three pull requests:
1. Platform
2. DTS files
3. Defconfig
But in this case maybe it is better if we cherry-pick them to the
SoC tree.
Yours,
Linus Walleij
^ permalink raw reply
* Re: [PATCH] serial: sh-sci: Remove plat_sci_port.flags
From: Lad, Prabhakar @ 2026-05-07 15:55 UTC (permalink / raw)
To: Geert Uytterhoeven
Cc: John Ogness, Greg Kroah-Hartman, Jiri Slaby, Biju Das,
Lad Prabhakar, Wolfram Sang, linux-serial, linux-renesas-soc,
linux-sh
In-Reply-To: <20260506124643.128021-1-geert+renesas@glider.be>
On Wed, May 6, 2026 at 1:52 PM Geert Uytterhoeven
<geert+renesas@glider.be> wrote:
>
> The last setter of p->flags was removed in commit 37744feebc086908
> ("sh: remove sh5 support") in v5.8.
>
> Link: https://lore.kernel.org/CAMuHMdXs94k3-7YD-yO7p2=+u8waYGAz8mpP5LDbMf3szt4V-w@mail.gmail.com
> Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
> ---
> drivers/tty/serial/sh-sci.c | 2 +-
> include/linux/serial_sci.h | 1 -
> 2 files changed, 1 insertion(+), 2 deletions(-)
>
Reviewed-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
Cheers,
Prabhakar
> diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c
> index 6c819b6b24258d36..a35230d57540384c 100644
> --- a/drivers/tty/serial/sh-sci.c
> +++ b/drivers/tty/serial/sh-sci.c
> @@ -3369,7 +3369,7 @@ static int sci_init_single(struct platform_device *dev,
> }
>
> port->type = SCI_PUBLIC_PORT_ID(p->type);
> - port->flags = UPF_FIXED_PORT | UPF_BOOT_AUTOCONF | p->flags;
> + port->flags = UPF_FIXED_PORT | UPF_BOOT_AUTOCONF;
> port->fifosize = sci_port->params->fifosize;
>
> if (p->type == PORT_SCI && !dev->dev.of_node) {
> diff --git a/include/linux/serial_sci.h b/include/linux/serial_sci.h
> index 0f2f50b8d28e2743..36c795d61f7e8457 100644
> --- a/include/linux/serial_sci.h
> +++ b/include/linux/serial_sci.h
> @@ -51,7 +51,6 @@ struct plat_sci_port_ops {
> */
> struct plat_sci_port {
> unsigned int type; /* SCI / SCIF / IRDA / HSCIF */
> - upf_t flags; /* UPF_* flags */
>
> unsigned int sampling_rate;
> unsigned int scscr; /* SCSCR initialization */
> --
> 2.43.0
>
>
^ permalink raw reply
* Re: [PATCH v7 6/6] ARM: zte: defconfig: Add a zx29 defconfig file
From: Stefan Dösinger @ 2026-05-07 22:08 UTC (permalink / raw)
To: Linus Walleij
Cc: Jonathan Corbet, Shuah Khan, Russell King, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Arnd Bergmann,
Krzysztof Kozlowski, Alexandre Belloni, Drew Fustini,
Greg Kroah-Hartman, Jiri Slaby, linux-doc, linux-kernel,
linux-arm-kernel, devicetree, linux-serial
In-Reply-To: <CAD++jLk02QnkXYwJ0b6x=qw9stR4nPrjD3sYPOvWAQz8t9OsUA@mail.gmail.com>
[-- Attachment #1: Type: text/plain, Size: 1974 bytes --]
Hi Linus,
Am Donnerstag, 7. Mai 2026, 15:24:38 Ostafrikanische Zeit schrieb Linus
Walleij:
> Hi Stefan,
>
> On Wed, May 6, 2026 at 7:39 PM Stefan Dösinger
>
> <stefandoesinger@gmail.com> wrote:
> > I'll send a v8 with some of Sashiko's (very impressive)
> > findings but keep the defconfig.
>
> Maybe not send all patches to soc@kernel.org right now because they
> end up in the patch tracker.
I meant send them to linux-arm-kernel@, not soc@ just yet.
I propose to hold off on adding the new SoC upstream until the clk and pinctrl
drivers had at least an initial review. They are more complicated than this
current patchset and they will be necessary to do anything useful with this
SoC. I expect to send a first version of the clock driver over the weekend.
The important thing with the submission to this mailing list was to get
feedback, so I avoid building a long set of patches on a shaky foundation.
> For a new platform that may be OK though...
>
> Nominall it should be three pull requests:
> 1. Platform
> 2. DTS files
> 3. Defconfig
So I read https://docs.kernel.org/process/maintainer-soc.html a few times. If
I understand it correctly at this point "pull request" still means emails sent
with p4, correct? Or does someone create a git repository on git.kernel.org
for me that I can use to send actual pull requests?
As I understand it, my 6 patches then go to the 4 corners of the kernel:
Patch 1 (dt binding) to devicetree@vger.kernel.org
Patches 2 (platform), 5 (DTS) and 6 (defconfig) to soc@kernel.org, but not in
one series but 3 independent ones
Patches 3 and 4 (UART) to linux-serial@vger.kernel.org. I think this can and
should be a series of both patches belonging together
It might make sense to send the DT binding on its way so it is in place when
the SoC maintainers look at the patch that adds the new platform.
Do I understand the mechanics correctly?
Thanks,
Stefan
[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 870 bytes --]
^ permalink raw reply
* Re: [PATCH 2/3] tty: remove unnecessary SERIAL_NONSTANDARD config option
From: Randy Dunlap @ 2026-05-08 0:14 UTC (permalink / raw)
To: Ethan Nelson-Moore, linux-serial
Cc: Greg Kroah-Hartman, Jiri Slaby, Ilpo Järvinen,
Andy Shevchenko
In-Reply-To: <2cefa6ee9b6bf13d367e15dd74093a0ef9ad81db.1777943090.git.enelsonmoore@gmail.com>
On 5/4/26 6:20 PM, Ethan Nelson-Moore wrote:
> The SERIAL_NONSTANDARD config option currently only guards the
> selection of two Moxa multiport serial card drivers and the HDLC line
> discipline, so it does not significantly simplify configuration. Make
> the configuration process more straightforward by dropping this option
> and dependencies on it.
>
> Signed-off-by: Ethan Nelson-Moore <enelsonmoore@gmail.com>
> ---
> arch/arm/configs/footbridge_defconfig | 1 -
> arch/arm/configs/lpc18xx_defconfig | 1 -
> arch/arm/configs/mps2_defconfig | 1 -
> arch/arm/configs/neponset_defconfig | 1 -
> arch/arm/configs/stm32_defconfig | 1 -
> arch/csky/configs/defconfig | 1 -
> arch/loongarch/configs/loongson32_defconfig | 1 -
> arch/loongarch/configs/loongson64_defconfig | 1 -
> arch/mips/configs/bigsur_defconfig | 1 -
> arch/mips/configs/lemote2f_defconfig | 1 -
> arch/mips/configs/loongson2k_defconfig | 1 -
> arch/mips/configs/loongson3_defconfig | 1 -
> arch/powerpc/configs/cell_defconfig | 1 -
> arch/powerpc/configs/microwatt_defconfig | 1 -
> arch/powerpc/configs/ppc6xx_defconfig | 1 -
> arch/sh/configs/polaris_defconfig | 1 -
> arch/x86/configs/i386_defconfig | 1 -
> arch/x86/configs/x86_64_defconfig | 1 -
> drivers/tty/Kconfig | 18 ------------------
> drivers/tty/serial/Kconfig | 4 ++--
> tools/testing/selftests/bpf/config.x86_64 | 1 -
> tools/testing/selftests/hid/config.common | 1 -
> 22 files changed, 2 insertions(+), 40 deletions(-)
>
> diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig
> index 63b378e44a59..a9ade8bc88b2 100644
> --- a/drivers/tty/Kconfig
> +++ b/drivers/tty/Kconfig
> @@ -191,26 +191,8 @@ config LDISC_AUTOLOAD
>
> source "drivers/tty/serial/Kconfig"
>
> -config SERIAL_NONSTANDARD
> - bool "Non-standard serial port support"
> - depends on HAS_IOMEM
> - help
> - Say Y here if you have any non-standard serial boards -- boards
> - which aren't supported using the standard "dumb" serial driver.
> - This includes intelligent serial boards such as
> - Digiboards, etc. These are usually used for systems that need many
> - serial ports because they serve many terminals or dial-in
> - connections.
> -
> - Note that the answer to this question won't directly affect the
> - kernel: saying N will just cause the configurator to skip all
> - the questions about non-standard serial boards.
> -
> - Most people can say N here.
> -
> config N_HDLC
> tristate "HDLC line discipline support"
> - depends on SERIAL_NONSTANDARD
Here, N_HDLC used to depend on HAS_IOMEM (due to SERIAL_NONSTANDARD).
It no longer depends on HAS_IOMEM so it can be built for ARCH=um with
no build errors.
Just FYI/FWIW. Maybe add that to the commit message.
--
~Randy
^ permalink raw reply
* Re: [PATCH 3/3] docs: driver-api/tty/index.rst: copy-editing; improve organization
From: Randy Dunlap @ 2026-05-08 0:16 UTC (permalink / raw)
To: Ethan Nelson-Moore, linux-serial
Cc: Greg Kroah-Hartman, Jiri Slaby, Ilpo Järvinen,
Andy Shevchenko
In-Reply-To: <31fe93b480b4ca157086af3bebb21326d0fbe983.1777943090.git.enelsonmoore@gmail.com>
On 5/4/26 6:20 PM, Ethan Nelson-Moore wrote:
> The TTY driver API intro document contains a number of grammar and
> capitalization issues. Fix them.
>
> Now that the MOXA Smartio driver documentation is in the serial/
> directory, the "Other Documentation" section only contains TTY line
> disciplines. Rename it to reflect that.
>
> Signed-off-by: Ethan Nelson-Moore <enelsonmoore@gmail.com>
> ---
> Documentation/driver-api/tty/index.rst | 46 +++++++++++++-------------
> 1 file changed, 23 insertions(+), 23 deletions(-)
>
> diff --git a/Documentation/driver-api/tty/index.rst b/Documentation/driver-api/tty/index.rst
> index 6a08aebbc47c..eb5d602450f8 100644
> --- a/Documentation/driver-api/tty/index.rst
> +++ b/Documentation/driver-api/tty/index.rst
This LGTM.
Tested-by: Randy Dunlap <rdunlap@infradead.org>
Acked-by: Randy Dunlap <rdunlap@infradead.org>
--
~Randy
^ permalink raw reply
* [REQUEST] Backport 8250_dw BUSY deassert series to 6.12.y stable
From: Ionut Nechita (Wind River) @ 2026-05-08 15:16 UTC (permalink / raw)
To: ilpo.jarvinen, gregkh
Cc: stable, linux-serial, andriy.shevchenko, ionut.nechita,
chris.friesen
From: Ionut Nechita <ionut.nechita@windriver.com>
Hi Ilpo, Greg,
We're running kernel 6.12 (LTS) on production systems with DesignWare
8250 UARTs and are hitting the BUSY assertion issue that your recent
series addresses in 6.18:
Ilpo Järvinen (7):
serial: 8250: Protect LCR write in shutdown
serial: 8250_dw: Avoid unnecessary LCR writes
serial: 8250: Add serial8250_handle_irq_locked()
serial: 8250_dw: Rework dw8250_handle_irq() locking and IIR handling
serial: 8250_dw: Rework IIR_NO_INT handling to stop interrupt storm
serial: 8250: Add late synchronize_irq() to shutdown to handle DW UART BUSY
serial: 8250_dw: Ensure BUSY is deasserted
Patch 7/7 has Cc: stable, but it depends on patches 1-6 for the new
infrastructure (serial8250_handle_irq_locked(), the reworked locking in
dw8250_handle_irq(), etc.).
Could the full series be nominated for 6.12.y stable backport? Or if
that's too invasive for stable, could you advise on the minimal subset
that would allow patch 7 to apply cleanly on 6.12?
We've attempted a standalone backport of patch 7 (squashing the
necessary helpers from 8250_port.c), but the IRQ handler refactoring
in patches 3-5 changes the locking model significantly, and we'd
rather carry the maintainer-blessed version than risk introducing
subtle races.
Our environment:
- Kernel: 6.12.57-rt/6.12.87-rt (PREEMPT_RT)
- Hardware: Intel platforms with DW APB UART (snps,dw-apb-uart)
- Symptom: LCR writes silently ignored under Rx load, causing
baud rate / framing mismatches after set_termios
We are available to test any backport candidates on our hardware.
Thanks,
Ionut
---
^ permalink raw reply
* Re: [REQUEST] Backport 8250_dw BUSY deassert series to 6.12.y stable
From: Greg KH @ 2026-05-08 15:21 UTC (permalink / raw)
To: Ionut Nechita (Wind River)
Cc: ilpo.jarvinen, stable, linux-serial, andriy.shevchenko,
chris.friesen
In-Reply-To: <20260508151614.498810-1-ionut.nechita@windriver.com>
On Fri, May 08, 2026 at 06:16:14PM +0300, Ionut Nechita (Wind River) wrote:
> From: Ionut Nechita <ionut.nechita@windriver.com>
>
> Hi Ilpo, Greg,
>
> We're running kernel 6.12 (LTS) on production systems with DesignWare
> 8250 UARTs and are hitting the BUSY assertion issue that your recent
> series addresses in 6.18:
>
> Ilpo Järvinen (7):
> serial: 8250: Protect LCR write in shutdown
> serial: 8250_dw: Avoid unnecessary LCR writes
> serial: 8250: Add serial8250_handle_irq_locked()
> serial: 8250_dw: Rework dw8250_handle_irq() locking and IIR handling
> serial: 8250_dw: Rework IIR_NO_INT handling to stop interrupt storm
> serial: 8250: Add late synchronize_irq() to shutdown to handle DW UART BUSY
> serial: 8250_dw: Ensure BUSY is deasserted
>
> Patch 7/7 has Cc: stable, but it depends on patches 1-6 for the new
> infrastructure (serial8250_handle_irq_locked(), the reworked locking in
> dw8250_handle_irq(), etc.).
>
> Could the full series be nominated for 6.12.y stable backport? Or if
> that's too invasive for stable, could you advise on the minimal subset
> that would allow patch 7 to apply cleanly on 6.12?
Why not just move to 6.18.y instead if you have this issue?
thanks,
greg k-h
^ permalink raw reply
* Re: [REQUEST] Backport 8250_dw BUSY deassert series to 6.12.y stable
From: Ionut Nechita (Wind River) @ 2026-05-08 15:46 UTC (permalink / raw)
To: gregkh
Cc: ilpo.jarvinen, stable, linux-serial, andriy.shevchenko,
ionut.nechita, chris.friesen
In-Reply-To: <20260508151614.498810-1-ionut.nechita@windriver.com>
From: Ionut Nechita <ionut.nechita@windriver.com>
Hi Greg,
Fair question. We're shipping 6.12 as part of a certified platform
(StarlingX/Yocto) that is already deployed in production at customer
sites. A major kernel version bump requires a full re-qualification
cycle across the entire platform stack (RT latency validation,
out-of-tree driver compat, storage/networking regression testing),
which is a multi-month effort we cannot justify for a single
subsystem fix.
We are planning the move to a newer LTS for our next major release,
but for the current production branch 6.12 is what we're committed to.
Since patch 7/7 already carries Cc: stable, would it be possible to
pull in just the minimal dependencies needed to make it apply on
6.12.y? We're happy to do the backport work ourselves and submit it
for review if that helps — we just wanted to check with Ilpo first
on what the correct minimal subset would be before sending something
that might be wrong.
Thanks,
Ionut
---
^ permalink raw reply
* Re: [REQUEST] Backport 8250_dw BUSY deassert series to 6.12.y stable
From: Ilpo Järvinen @ 2026-05-08 17:13 UTC (permalink / raw)
To: Ionut Nechita (Wind River)
Cc: Greg Kroah-Hartman, stable, linux-serial, Andy Shevchenko,
chris.friesen
In-Reply-To: <20260508154629.530915-1-ionut.nechita@windriver.com>
[-- Attachment #1: Type: text/plain, Size: 2333 bytes --]
On Fri, 8 May 2026, Ionut Nechita (Wind River) wrote:
> From: Ionut Nechita <ionut.nechita@windriver.com>
>
> Hi Greg,
>
> Fair question. We're shipping 6.12 as part of a certified platform
> (StarlingX/Yocto) that is already deployed in production at customer
> sites. A major kernel version bump requires a full re-qualification
> cycle across the entire platform stack (RT latency validation,
> out-of-tree driver compat, storage/networking regression testing),
> which is a multi-month effort we cannot justify for a single
> subsystem fix.
>
> We are planning the move to a newer LTS for our next major release,
> but for the current production branch 6.12 is what we're committed to.
>
> Since patch 7/7 already carries Cc: stable, would it be possible to
> pull in just the minimal dependencies needed to make it apply on
> 6.12.y? We're happy to do the backport work ourselves and submit it
> for review if that helps — we just wanted to check with Ilpo first
> on what the correct minimal subset would be before sending something
> that might be wrong.
Ah, this didn't get backported that far likely due to some syntax related
changes causing conflicts with the series.
To backport the entire series my suggestion is to take parts of the
commits:
dbd26a886e94 ("serial: 8250: use serial_port_in/out() helpers") (8250_dw.c only or just all if it goes cleanly)
b339809edda1 ("serial: 8250: use guard()s") (8250_port.c shutdown hunks)
And this completely (just syntax changes):
bd8cad85561b ("serial: 8250_dw: Comment possible corner cases in serial_out() implementation")
Those above are effectively just syntax changes but result in conflicts if
not applied prior to the BUSY fix series.
This leaves only minor conflicts in:
a7b9ce39fbe4 ("serial: 8250_dw: Ensure BUSY is deasserted")
with type change in
fc9ceb501e38 ("serial: 8250: sanitize uart_port::serial_{in,out}() types")
(trivial resolution as the change was in the code removed by the BUSY
change)
73a4ed8f9efa ("serial: 8250_dw: Rework IIR_NO_INT handling to stop interrupt storm")
with probe function changes (handle_irq assignment changes and unrelated
dw8250_setup_dma_filter() change) in
c213375e3283 ("serial: 8250_dw: Call dw8250_quirks() conditionally")
Hope this helps.
--
i.
^ permalink raw reply
* Re: [PATCH v1 1/2] serial: qcom-geni: trace: Add tracepoint support for Qualcomm GENI serial
From: Steven Rostedt @ 2026-05-08 17:25 UTC (permalink / raw)
To: Praveen Talari
Cc: Masami Hiramatsu, Mathieu Desnoyers, Greg Kroah-Hartman,
Jiri Slaby, Konrad Dybcio, linux-kernel, linux-trace-kernel,
linux-arm-msm, linux-serial, Mukesh Kumar Savaliya,
Aniket Randive, chandana.chiluveru, jyothi.seerapu
In-Reply-To: <20260506-add-tracepoints-for-qcom-geni-serial-v1-1-544b22612e08@oss.qualcomm.com>
On Wed, 06 May 2026 22:54:44 +0530
Praveen Talari <praveen.talari@oss.qualcomm.com> wrote:
> +TRACE_EVENT(geni_serial_tx_data,
> + TP_PROTO(struct device *dev, const u8 *buf, unsigned int len),
> + TP_ARGS(dev, buf, len),
> +
> + TP_STRUCT__entry(__string(name, dev_name(dev))
> + __field(unsigned int, len)
> + __dynamic_array(u8, data, len)
> + ),
> +
> + TP_fast_assign(__assign_str(name);
> + __entry->len = len;
> + memcpy(__get_dynamic_array(data), buf, len);
> + ),
> +
> + TP_printk("%s: tx_len=%u data=%s",
> + __get_str(name), __entry->len,
> + __print_hex(__get_dynamic_array(data), __entry->len))
> +);
> +
> +TRACE_EVENT(geni_serial_rx_data,
> + TP_PROTO(struct device *dev, const u8 *buf, unsigned int len),
> + TP_ARGS(dev, buf, len),
> +
> + TP_STRUCT__entry(__string(name, dev_name(dev))
> + __field(unsigned int, len)
> + __dynamic_array(u8, data, len)
> + ),
> +
> + TP_fast_assign(__assign_str(name);
> + __entry->len = len;
> + memcpy(__get_dynamic_array(data), buf, len);
> + ),
> +
> + TP_printk("%s: rx_len=%u data=%s",
Do you really need to say "tx_len" and "rx_len", could it just be "len" and
have the name of the tracepoint show what it is?
Each TRACE_EVENT() is really just a:
DECLARE_EVENT_CLASS() followed by a DEFINE_EVENT()
underneath.
And each TRACE_EVENT() costs around 5K in size, where most of that is in
the DECLARE_EVENT_CLASS() portion. Thus, you can save some memory by using
DECLARE_EVENT_CLASS() and then define the above two events with
DEFINE_EVENT().
-- Steve
> + __get_str(name), __entry->len,
> + __print_hex(__get_dynamic_array(data), __entry->len))
> +);
> +
^ permalink raw reply
* [PATCH] tty: serial: 8250: protect against NULL uart->port.dev in register
From: Stepan Ionichev @ 2026-05-08 18:12 UTC (permalink / raw)
To: gregkh; +Cc: jirislaby, linux-serial, linux-kernel, Stepan Ionichev
serial8250_register_8250_port() conditionally copies uart->port.dev
from up->port.dev only when up->port.dev is non-NULL:
if (up->port.dev) {
uart->port.dev = up->port.dev;
...
}
So if both the existing uart slot and up have a NULL ->dev,
uart->port.dev remains NULL. The very next ACPI companion check
then dereferences it unconditionally:
if (!has_acpi_companion(uart->port.dev)) {
has_acpi_companion() reads dev->fwnode without a NULL guard
(include/linux/acpi.h), so this NULL-derefs the kernel for the
remaining no-dev case rather than just skipping the
mctrl_gpio_init() initialisation as intended.
smatch flags the inconsistency:
drivers/tty/serial/8250/8250_core.c:767
serial8250_register_8250_port() error: 'uart->port.dev' could be
null (see line 719)
Guard the call with a NULL check so register continues to work
for callers that legitimately have no parent device (legacy
non-OF/non-ACPI registrations).
No functional change for callers that pass a non-NULL ->dev.
Signed-off-by: Stepan Ionichev <sozdayvek@gmail.com>
---
drivers/tty/serial/8250/8250_core.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
index a428e8893..e136cec0c 100644
--- a/drivers/tty/serial/8250/8250_core.c
+++ b/drivers/tty/serial/8250/8250_core.c
@@ -764,7 +764,7 @@ int serial8250_register_8250_port(const struct uart_8250_port *up)
* Only call mctrl_gpio_init(), if the device has no ACPI
* companion device
*/
- if (!has_acpi_companion(uart->port.dev)) {
+ if (uart->port.dev && !has_acpi_companion(uart->port.dev)) {
struct mctrl_gpios *gpios = mctrl_gpio_init(&uart->port, 0);
if (IS_ERR(gpios)) {
ret = PTR_ERR(gpios);
--
2.43.0
^ permalink raw reply related
* Re: [PATCH v1 1/2] serial: qcom-geni: trace: Add tracepoint support for Qualcomm GENI serial
From: Praveen Talari @ 2026-05-09 1:19 UTC (permalink / raw)
To: Steven Rostedt
Cc: Masami Hiramatsu, Mathieu Desnoyers, Greg Kroah-Hartman,
Jiri Slaby, Konrad Dybcio, linux-kernel, linux-trace-kernel,
linux-arm-msm, linux-serial, Mukesh Kumar Savaliya,
Aniket Randive, chandana.chiluveru, jyothi.seerapu
In-Reply-To: <20260508132543.4f100ae0@gandalf.local.home>
On 08-05-2026 22:55, Steven Rostedt wrote:
> On Wed, 06 May 2026 22:54:44 +0530
> Praveen Talari <praveen.talari@oss.qualcomm.com> wrote:
>
>> +TRACE_EVENT(geni_serial_tx_data,
>> + TP_PROTO(struct device *dev, const u8 *buf, unsigned int len),
>> + TP_ARGS(dev, buf, len),
>> +
>> + TP_STRUCT__entry(__string(name, dev_name(dev))
>> + __field(unsigned int, len)
>> + __dynamic_array(u8, data, len)
>> + ),
>> +
>> + TP_fast_assign(__assign_str(name);
>> + __entry->len = len;
>> + memcpy(__get_dynamic_array(data), buf, len);
>> + ),
>> +
>> + TP_printk("%s: tx_len=%u data=%s",
>> + __get_str(name), __entry->len,
>> + __print_hex(__get_dynamic_array(data), __entry->len))
>> +);
>> +
>> +TRACE_EVENT(geni_serial_rx_data,
>> + TP_PROTO(struct device *dev, const u8 *buf, unsigned int len),
>> + TP_ARGS(dev, buf, len),
>> +
>> + TP_STRUCT__entry(__string(name, dev_name(dev))
>> + __field(unsigned int, len)
>> + __dynamic_array(u8, data, len)
>> + ),
>> +
>> + TP_fast_assign(__assign_str(name);
>> + __entry->len = len;
>> + memcpy(__get_dynamic_array(data), buf, len);
>> + ),
>> +
>> + TP_printk("%s: rx_len=%u data=%s",
> Do you really need to say "tx_len" and "rx_len", could it just be "len" and
> have the name of the tracepoint show what it is?
Sure. I will review and update next patch.
>
> Each TRACE_EVENT() is really just a:
>
> DECLARE_EVENT_CLASS() followed by a DEFINE_EVENT()
>
> underneath.
>
> And each TRACE_EVENT() costs around 5K in size, where most of that is in
> the DECLARE_EVENT_CLASS() portion. Thus, you can save some memory by using
> DECLARE_EVENT_CLASS() and then define the above two events with
> DEFINE_EVENT().
Thank you for suggestion and will update in next patch.
Thanks,
Praveen Talari
>
> -- Steve
>
>
>> + __get_str(name), __entry->len,
>> + __print_hex(__get_dynamic_array(data), __entry->len))
>> +);
>> +
^ permalink raw reply
* [PATCH] tty: n_tty: read termios under lock in poll
From: Cen Zhang @ 2026-05-10 2:59 UTC (permalink / raw)
To: gregkh, jirislaby; +Cc: linux-kernel, linux-serial, baijiaju1990, Cen Zhang
n_tty_poll() uses input_available_p() to decide whether buffered input
makes the tty readable. That helper reads termios state through
L_EXTPROC(), VMIN, and VTIME, but the poll path does not hold the read
side of tty->termios_rwsem.
tty_set_termios() updates tty->termios under the write side of the same
semaphore, including c_lflag and c_cc[]. n_tty_read() already takes the
read side before reading the same termios fields and before calling
input_available_p(). Protect the poll-side readiness checks the same way
so poll observes a coherent termios state when deciding whether to report
readable input.
Do not hold termios_rwsem across tty_buffer_flush_work(), matching the
read path which drops the semaphore before flushing pending receive work
and then checks input availability again after reacquiring it.
Signed-off-by: Cen Zhang <zzzccc427@gmail.com>
---
drivers/tty/n_tty.c | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c
index e6a0f5b40d0a..c8e1882782db 100644
--- a/drivers/tty/n_tty.c
+++ b/drivers/tty/n_tty.c
@@ -2437,13 +2437,17 @@ static __poll_t n_tty_poll(struct tty_struct *tty, struct file *file,
poll_wait(file, &tty->read_wait, wait);
poll_wait(file, &tty->write_wait, wait);
- if (input_available_p(tty, 1))
- mask |= EPOLLIN | EPOLLRDNORM;
- else {
- tty_buffer_flush_work(tty->port);
+ scoped_guard(rwsem_read, &tty->termios_rwsem) {
if (input_available_p(tty, 1))
mask |= EPOLLIN | EPOLLRDNORM;
}
+ if (!(mask & (EPOLLIN | EPOLLRDNORM))) {
+ tty_buffer_flush_work(tty->port);
+ scoped_guard(rwsem_read, &tty->termios_rwsem) {
+ if (input_available_p(tty, 1))
+ mask |= EPOLLIN | EPOLLRDNORM;
+ }
+ }
if (tty->ctrl.packet && tty->link->ctrl.pktstatus)
mask |= EPOLLPRI | EPOLLIN | EPOLLRDNORM;
if (test_bit(TTY_OTHER_CLOSED, &tty->flags))
--
2.43.0
^ permalink raw reply related
* [PATCH] vt: keyboard: serialize KDGETLED with keyboard_tasklet
From: Cen Zhang @ 2026-05-10 3:13 UTC (permalink / raw)
To: gregkh, jirislaby; +Cc: linux-kernel, linux-serial, baijiaju1990, Cen Zhang
KDGETLED reaches getledstate() and samples ledstate without serializing
against keyboard_tasklet.
That is problematic because kbd_bh() temporarily stores ~leds into
ledstate during a VT switch to force LED propagation before committing
the final value. An unlocked KDGETLED can therefore observe that
internal sentinel instead of the last committed LED state.
Other ledstate readers in this file already quiesce keyboard_tasklet
before sampling the value. Do the same in getledstate() so the ioctl
only returns a stable snapshot.
Signed-off-by: Cen Zhang <zzzccc427@gmail.com>
---
drivers/tty/vt/keyboard.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c
index 13bc048f45e86..671ad4a5cb64c 100644
--- a/drivers/tty/vt/keyboard.c
+++ b/drivers/tty/vt/keyboard.c
@@ -1127,7 +1127,13 @@ static void kbd_init_leds(void)
*/
static unsigned char getledstate(void)
{
- return ledstate & 0xff;
+ unsigned char leds;
+
+ tasklet_disable(&keyboard_tasklet);
+ leds = ledstate & 0xff;
+ tasklet_enable(&keyboard_tasklet);
+
+ return leds;
}
void setledstate(struct kbd_struct *kb, unsigned int led)
--
2.43.0
^ permalink raw reply related
* [PATCH] ARM: footbridge: convert to sparse IRQs
From: Ethan Nelson-Moore @ 2026-05-10 5:20 UTC (permalink / raw)
To: linux-arm-kernel, linux-input, linux-serial
Cc: Ethan Nelson-Moore, Russell King, Arnd Bergmann,
Greg Kroah-Hartman, Dmitry Torokhov, Jiri Slaby,
Russell King (Oracle), Linus Walleij, Kees Cook,
Nathan Chancellor, Sebastian Andrzej Siewior, Steven Rostedt,
Thomas Weissschuh, Peter Zijlstra
To improve future maintainability, change the interrupt handling for
mach-footbridge to use sparse IRQs.
Since the number of possible interrupts is already fixed and relatively
small, just make it use all legacy interrupts preallocated using the
.nr_irqs field in the machine descriptor, rather than actually
allocating domains on the fly.
Many files had to be adjusted to include <mach/irqs.h>
explicitly because it is no longer implicitly included with sparse
IRQs.
Description adapted from commit c78a41fc04f0 ("ARM: s3c24xx: convert
to sparse-irq").
Signed-off-by: Ethan Nelson-Moore <enelsonmoore@gmail.com>
---
This commit depends on my previous submission "ARM: <asm/floppy.h>: fix
build with sparse IRQs".
arch/arm/Kconfig | 2 +-
arch/arm/include/asm/irq.h | 4 +++-
| 2 +-
| 2 +-
| 2 +-
| 2 ++
| 4 +---
| 2 +-
| 2 +-
| 2 +-
| 2 ++
| 2 +-
drivers/char/nwbutton.c | 2 +-
drivers/input/serio/i8042-io.h | 6 +++---
drivers/tty/serial/21285.c | 2 +-
15 files changed, 21 insertions(+), 17 deletions(-)
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 09b2767fee0f..1155c78bb6aa 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -153,7 +153,7 @@ config ARM
select PCI_SYSCALL if PCI
select PERF_USE_VMALLOC
select RTC_LIB
- select SPARSE_IRQ if !ARCH_FOOTBRIDGE
+ select SPARSE_IRQ
select SYS_SUPPORTS_APM_EMULATION
select THREAD_INFO_IN_TASK
select TIMER_OF if OF
diff --git a/arch/arm/include/asm/irq.h b/arch/arm/include/asm/irq.h
index 26c1d2ced4ce..08589b88c3b9 100644
--- a/arch/arm/include/asm/irq.h
+++ b/arch/arm/include/asm/irq.h
@@ -10,7 +10,9 @@
#define NR_IRQS NR_IRQS_LEGACY
#endif
-#ifndef irq_canonicalize
+#ifdef CONFIG_ARCH_FOOTBRIDGE
+#define irq_canonicalize(i) (((i) == 2) ? 9 : i)
+#else
#define irq_canonicalize(i) (i)
#endif
--git a/arch/arm/mach-footbridge/dc21285-timer.c b/arch/arm/mach-footbridge/dc21285-timer.c
index 2908c9ef3c9b..7d7ad1c1ef3f 100644
--- a/arch/arm/mach-footbridge/dc21285-timer.c
+++ b/arch/arm/mach-footbridge/dc21285-timer.c
@@ -12,7 +12,7 @@
#include <linux/irq.h>
#include <linux/sched_clock.h>
-#include <asm/irq.h>
+#include <mach/irqs.h>
#include <asm/hardware/dec21285.h>
#include <asm/mach/time.h>
--git a/arch/arm/mach-footbridge/dc21285.c b/arch/arm/mach-footbridge/dc21285.c
index e1b336624883..ffdecfadc9e2 100644
--- a/arch/arm/mach-footbridge/dc21285.c
+++ b/arch/arm/mach-footbridge/dc21285.c
@@ -17,7 +17,7 @@
#include <linux/io.h>
#include <linux/spinlock.h>
-#include <asm/irq.h>
+#include <mach/irqs.h>
#include <asm/mach/pci.h>
#include <asm/hardware/dec21285.h>
--git a/arch/arm/mach-footbridge/ebsa285-pci.c b/arch/arm/mach-footbridge/ebsa285-pci.c
index c3f280d08fa7..d2168660dd01 100644
--- a/arch/arm/mach-footbridge/ebsa285-pci.c
+++ b/arch/arm/mach-footbridge/ebsa285-pci.c
@@ -10,7 +10,7 @@
#include <linux/pci.h>
#include <linux/init.h>
-#include <asm/irq.h>
+#include <mach/irqs.h>
#include <asm/mach/pci.h>
#include <asm/mach-types.h>
--git a/arch/arm/mach-footbridge/ebsa285.c b/arch/arm/mach-footbridge/ebsa285.c
index 1cb7d674bc81..a820f7467468 100644
--- a/arch/arm/mach-footbridge/ebsa285.c
+++ b/arch/arm/mach-footbridge/ebsa285.c
@@ -10,6 +10,7 @@
#include <linux/slab.h>
#include <linux/leds.h>
+#include <mach/irqs.h>
#include <asm/hardware/dec21285.h>
#include <asm/mach-types.h>
@@ -117,6 +118,7 @@ MACHINE_START(EBSA285, "EBSA285")
.video_end = 0x000bffff,
.map_io = footbridge_map_io,
.init_early = footbridge_sched_clock,
+ .nr_irqs = FOOTBRIDGE_NR_IRQS,
.init_irq = footbridge_init_irq,
.init_time = footbridge_timer_init,
.restart = footbridge_restart,
--git a/arch/arm/mach-footbridge/include/mach/irqs.h b/arch/arm/mach-footbridge/include/mach/irqs.h
index a5f41846ab9c..10f1fbc24012 100644
--- a/arch/arm/mach-footbridge/include/mach/irqs.h
+++ b/arch/arm/mach-footbridge/include/mach/irqs.h
@@ -11,7 +11,7 @@
*/
#include <asm/mach-types.h>
-#define NR_IRQS 36
+#define FOOTBRIDGE_NR_IRQS 36
#define NR_DC21285_IRQS 16
#define _ISA_IRQ(x) (0 + (x))
@@ -93,5 +93,3 @@
#define I8042_KBD_IRQ IRQ_ISA_KEYBOARD
#define I8042_AUX_IRQ (machine_is_netwinder() ? IRQ_NETWINDER_PS2MOUSE : IRQ_ISA_PS2MOUSE)
#define IRQ_FLOPPYDISK IRQ_ISA_FLOPPY
-
-#define irq_canonicalize(_i) (((_i) == IRQ_ISA_CASCADE) ? IRQ_ISA_2 : _i)
--git a/arch/arm/mach-footbridge/isa-irq.c b/arch/arm/mach-footbridge/isa-irq.c
index 842ddb4121ef..e4e71bdf1dc7 100644
--- a/arch/arm/mach-footbridge/isa-irq.c
+++ b/arch/arm/mach-footbridge/isa-irq.c
@@ -21,8 +21,8 @@
#include <asm/mach/irq.h>
#include <mach/hardware.h>
+#include <mach/irqs.h>
#include <asm/hardware/dec21285.h>
-#include <asm/irq.h>
#include <asm/mach-types.h>
#include "common.h"
--git a/arch/arm/mach-footbridge/isa-timer.c b/arch/arm/mach-footbridge/isa-timer.c
index 723e3eae995d..07dee61b0b03 100644
--- a/arch/arm/mach-footbridge/isa-timer.c
+++ b/arch/arm/mach-footbridge/isa-timer.c
@@ -13,7 +13,7 @@
#include <linux/spinlock.h>
#include <linux/timex.h>
-#include <asm/irq.h>
+#include <mach/irqs.h>
#include <asm/mach/time.h>
#include "common.h"
--git a/arch/arm/mach-footbridge/isa.c b/arch/arm/mach-footbridge/isa.c
index 84caccddce44..1e7b0f5fb111 100644
--- a/arch/arm/mach-footbridge/isa.c
+++ b/arch/arm/mach-footbridge/isa.c
@@ -7,7 +7,7 @@
#include <linux/init.h>
#include <linux/serial_8250.h>
-#include <asm/irq.h>
+#include <mach/irqs.h>
#include <asm/hardware/dec21285.h>
#include "common.h"
--git a/arch/arm/mach-footbridge/netwinder-hw.c b/arch/arm/mach-footbridge/netwinder-hw.c
index c024eefd4978..ab17ba916d47 100644
--- a/arch/arm/mach-footbridge/netwinder-hw.c
+++ b/arch/arm/mach-footbridge/netwinder-hw.c
@@ -16,6 +16,7 @@
#include <linux/slab.h>
#include <linux/leds.h>
+#include <mach/irqs.h>
#include <asm/hardware/dec21285.h>
#include <asm/mach-types.h>
#include <asm/setup.h>
@@ -766,6 +767,7 @@ MACHINE_START(NETWINDER, "Rebel-NetWinder")
.reserve_lp2 = 1,
.fixup = fixup_netwinder,
.map_io = footbridge_map_io,
+ .nr_irqs = FOOTBRIDGE_NR_IRQS,
.init_irq = footbridge_init_irq,
.init_time = isa_timer_init,
.restart = netwinder_restart,
--git a/arch/arm/mach-footbridge/netwinder-pci.c b/arch/arm/mach-footbridge/netwinder-pci.c
index e8304392074b..bfd5c0606c71 100644
--- a/arch/arm/mach-footbridge/netwinder-pci.c
+++ b/arch/arm/mach-footbridge/netwinder-pci.c
@@ -10,7 +10,7 @@
#include <linux/pci.h>
#include <linux/init.h>
-#include <asm/irq.h>
+#include <mach/irqs.h>
#include <asm/mach/pci.h>
#include <asm/mach-types.h>
diff --git a/drivers/char/nwbutton.c b/drivers/char/nwbutton.c
index 92cee5717237..99819b184aac 100644
--- a/drivers/char/nwbutton.c
+++ b/drivers/char/nwbutton.c
@@ -18,7 +18,7 @@
#include <linux/init.h>
#include <linux/uaccess.h>
-#include <asm/irq.h>
+#include <mach/irqs.h>
#include <asm/mach-types.h>
#define __NWBUTTON_C /* Tell the header file who we are */
diff --git a/drivers/input/serio/i8042-io.h b/drivers/input/serio/i8042-io.h
index a8f4b2d70e59..cea72bd888af 100644
--- a/drivers/input/serio/i8042-io.h
+++ b/drivers/input/serio/i8042-io.h
@@ -15,9 +15,9 @@
* IRQs.
*/
-#if defined(__arm__)
-/* defined in include/asm-arm/arch-xxx/irqs.h */
-#include <asm/irq.h>
+#ifdef CONFIG_ARCH_FOOTBRIDGE
+/* defined in arch/arm/mach-footbridge/include/mach/irqs.h */
+#include <mach/irqs.h>
#elif defined(CONFIG_PPC)
extern int of_i8042_kbd_irq;
extern int of_i8042_aux_irq;
diff --git a/drivers/tty/serial/21285.c b/drivers/tty/serial/21285.c
index 4de0c975ebdc..f0c63875912e 100644
--- a/drivers/tty/serial/21285.c
+++ b/drivers/tty/serial/21285.c
@@ -15,11 +15,11 @@
#include <linux/serial.h>
#include <linux/io.h>
-#include <asm/irq.h>
#include <asm/mach-types.h>
#include <asm/system_info.h>
#include <asm/hardware/dec21285.h>
#include <mach/hardware.h>
+#include <mach/irqs.h>
#define BAUD_BASE (mem_fclk_21285/64)
--
2.43.0
^ permalink raw reply related
* [PATCH 6.12.y 0/8] serial: 8250_dw: backport BUSY deassert series
From: Ionut Nechita (Wind River) @ 2026-05-10 13:40 UTC (permalink / raw)
To: ilpo.jarvinen, gregkh; +Cc: andriy.shevchenko, linux-serial, Ionut Nechita
From: Ionut Nechita <ionut.nechita@windriver.com>
Hi Greg, Ilpo,
This series backports the 8250_dw BUSY deassert fix to 6.12.y, per
Ilpo's guidance from the request thread:
https://lore.kernel.org/linux-serial/deb9499d-3245-7e38-9034-e533d4b5f512@linux.intel.com/
Background: we ship 6.12 LTS as part of a certified production
platform (StarlingX/Yocto) and are hitting the BUSY assertion issue
on Intel DesignWare 8250 UARTs - LCR writes get silently ignored
under Rx load, causing baud-rate / framing mismatches after
set_termios. A major LTS bump is a multi-month re-qualification we
can't justify for a single subsystem fix.
The original mainline series is 7 patches by Ilpo (plus its
dependencies). For 6.12.y, the resulting series here is 8 patches:
Prerequisites (per Ilpo):
1/8 serial: 8250: use serial_port_in/out() helpers [dbd26a886e94]
2/8 serial: 8250_dw: Comment possible corner cases ... [bd8cad85561b]
BUSY deassert series:
3/8 serial: 8250: Protect LCR write in shutdown [59a33d83bbe6]
4/8 serial: 8250_dw: Avoid unnecessary LCR writes [8002d6d6d0d8]
5/8 serial: 8250: Add serial8250_handle_irq_locked() [8324a54f604d]
6/8 serial: 8250_dw: Rework dw8250_handle_irq() ... [883c5a2bc934]
7/8 serial: 8250_dw: Rework IIR_NO_INT handling ... [73a4ed8f9efa]
8/8 serial: 8250_dw: Ensure BUSY is deasserted [a7b9ce39fbe4]
Notes:
- Patch 6/7 of the original mainline series,
commit e0a368ae7953 ("serial: 8250: Add late synchronize_irq() to shutdown to handle DW UART BUSY"),
is *already* in 6.12.y as
commit 0bae1c670aa8 ("serial: 8250: Add late synchronize_irq() to shutdown to handle DW UART BUSY")
(Ilpo, 2026-02-03), so it is not re-sent here. Functionally this
means patches 3-8 above land on top of the existing
late-synchronize_irq() fix; the conflict in patch 3 (LCR write
placement around the late synchronize_irq) was resolved
accordingly.
- Ilpo's other suggested prerequisites are *not* included as prereqs:
* commit b339809edda1 ("serial: 8250: use guard()s")
* commit fc9ceb501e38 ("serial: 8250: sanitize uart_port::serial_{in,out}() types")
* commit c213375e3283 ("serial: 8250_dw: Call dw8250_quirks() conditionally")
Reasoning:
* commit b339809edda1 ("serial: 8250: use guard()s") is a
large refactor and only the shutdown hunk was needed;
instead, patches 3, 5, 6, 7 here are adapted to use the
existing explicit uart_port_lock_irqsave/unlock_irqrestore
form rather than the cleanup-based guard() introduced by
that commit;
* commit fc9ceb501e38 ("serial: 8250: sanitize
uart_port::serial_{in,out}() types") only causes a trivial
conflict in patch 8 in code that the BUSY change removes
anyway (per Ilpo's note);
* commit c213375e3283 ("serial: 8250_dw: Call dw8250_quirks()
conditionally") only causes a conflict in patch 7 around
the dw8250_setup_dma_filter() helper and the conditional
p->handle_irq assignment, neither of which exist in 6.12.y
and neither of which is needed for the BUSY fix.
- Namespace export syntax: in 6.12.y both EXPORT_SYMBOL_NS_GPL()
and MODULE_IMPORT_NS() apply __stringify(ns) to the namespace
argument, so it must be a bare identifier. Mainline (where the
upstream patches were written) accepts a string literal. Patches
5 and 8 here use the bare-identifier form (SERIAL_8250) instead
of the upstream string form ("SERIAL_8250"); without this fix
the .vmlinux.export.c link step fails with "expected ':' or ')'
before 'SERIAL_8250'". This is noted in the [Ionut: ...] block
of the affected patches.
Each of patches 3, 5, 6, 7 and 8 carries an explicit
"[Ionut: adapt to 6.12.y - ...]" note describing exactly what
was changed relative to the upstream commit.
Build:
- Each patch builds individually on 6.12.87 to a complete vmlinux
(bisect-safe), with CONFIG_SERIAL_8250=y, CONFIG_SERIAL_8250_DW=m
on x86_64 defconfig.
Testing plan:
- We will test on Intel platforms with DW APB UART
(snps,dw-apb-uart) running 6.12.57-rt / 6.12.87-rt (PREEMPT_RT)
to confirm the original symptom (LCR writes silently ignored
under Rx load -> baud / framing mismatch after set_termios) is
gone. Will report Tested-by once cycles complete.
Based on: linux-6.12.y at v6.12.87 (8bf2f55ef536).
Andy Shevchenko (1):
serial: 8250_dw: Comment possible corner cases in serial_out()
implementation
Ilpo Järvinen (6):
serial: 8250: Protect LCR write in shutdown
serial: 8250_dw: Avoid unnecessary LCR writes
serial: 8250: Add serial8250_handle_irq_locked()
serial: 8250_dw: Rework dw8250_handle_irq() locking and IIR handling
serial: 8250_dw: Rework IIR_NO_INT handling to stop interrupt storm
serial: 8250_dw: Ensure BUSY is deasserted
Jiri Slaby (SUSE) (1):
serial: 8250: use serial_port_in/out() helpers
drivers/tty/serial/8250/8250.h | 25 +++
drivers/tty/serial/8250/8250_dw.c | 298 +++++++++++++++++++++++-----
drivers/tty/serial/8250/8250_fsl.c | 8 +-
drivers/tty/serial/8250/8250_omap.c | 2 +-
drivers/tty/serial/8250/8250_port.c | 66 +++---
include/linux/serial_8250.h | 1 +
6 files changed, 319 insertions(+), 81 deletions(-)
--
2.54.0
^ permalink raw reply
* [PATCH 6.12.y 2/8] serial: 8250_dw: Comment possible corner cases in serial_out() implementation
From: Ionut Nechita (Wind River) @ 2026-05-10 13:40 UTC (permalink / raw)
To: ilpo.jarvinen, gregkh; +Cc: andriy.shevchenko, linux-serial, Ionut Nechita
In-Reply-To: <20260510134011.618215-1-ionut.nechita@windriver.com>
From: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
commit bd8cad85561b8de36f099404c332bf3e3dbe90f5 upstream.
8250 DesignWare driver uses a few custom implementations of the serial_out().
These implementations are carefully made to avoid infinite loops. But this is
not obvious from looking at the code. Comment the possible corner cases in
the respective functions.
Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Link: https://lore.kernel.org/r/20250317094021.1201512-1-andriy.shevchenko@linux.intel.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Ionut Nechita <ionut.nechita@windriver.com>
---
drivers/tty/serial/8250/8250_dw.c | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c
index 3225011fd772..88c55336d50f 100644
--- a/drivers/tty/serial/8250/8250_dw.c
+++ b/drivers/tty/serial/8250/8250_dw.c
@@ -107,11 +107,23 @@ static inline int dw8250_modify_msr(struct uart_port *p, int offset, int value)
return value;
}
+/*
+ * This function is being called as part of the uart_port::serial_out()
+ * routine. Hence, it must not call serial_port_out() or serial_out()
+ * against the modified registers here, i.e. LCR.
+ */
static void dw8250_force_idle(struct uart_port *p)
{
struct uart_8250_port *up = up_to_u8250p(p);
unsigned int lsr;
+ /*
+ * The following call currently performs serial_out()
+ * against the FCR register. Because it differs to LCR
+ * there will be no infinite loop, but if it ever gets
+ * modified, we might need a new custom version of it
+ * that avoids infinite recursion.
+ */
serial8250_clear_and_reinit_fifos(up);
/*
@@ -128,6 +140,11 @@ static void dw8250_force_idle(struct uart_port *p)
serial_port_in(p, UART_RX);
}
+/*
+ * This function is being called as part of the uart_port::serial_out()
+ * routine. Hence, it must not call serial_port_out() or serial_out()
+ * against the modified registers here, i.e. LCR.
+ */
static void dw8250_check_lcr(struct uart_port *p, int offset, int value)
{
struct dw8250_data *d = to_dw8250_data(p->private_data);
--
2.54.0
^ permalink raw reply related
* [PATCH 6.12.y 1/8] serial: 8250: use serial_port_in/out() helpers
From: Ionut Nechita (Wind River) @ 2026-05-10 13:40 UTC (permalink / raw)
To: ilpo.jarvinen, gregkh
Cc: andriy.shevchenko, linux-serial, Jiri Slaby (SUSE), Ionut Nechita
In-Reply-To: <20260510134011.618215-1-ionut.nechita@windriver.com>
From: "Jiri Slaby (SUSE)" <jirislaby@kernel.org>
commit dbd26a886e94deb7fda9050f9195ccb41f9a5d93 upstream.
There are serial_port_in/out() helpers to be used instead of direct
p->serial_in/out(). Use them in various 8250 drivers.
Signed-off-by: Jiri Slaby (SUSE) <jirislaby@kernel.org>
Cc: "Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com>
Cc: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
--
[v2]
* Use serial_port_in/out() and not serial_in/out() [Andy]
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> # 8250_dw
Link: https://lore.kernel.org/r/20250317070046.24386-28-jirislaby@kernel.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Ionut Nechita <ionut.nechita@windriver.com>
---
drivers/tty/serial/8250/8250_dw.c | 16 ++++++++--------
drivers/tty/serial/8250/8250_fsl.c | 8 ++++----
drivers/tty/serial/8250/8250_omap.c | 2 +-
3 files changed, 13 insertions(+), 13 deletions(-)
diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c
index f17dc3de020c..3225011fd772 100644
--- a/drivers/tty/serial/8250/8250_dw.c
+++ b/drivers/tty/serial/8250/8250_dw.c
@@ -120,12 +120,12 @@ static void dw8250_force_idle(struct uart_port *p)
* enabled.
*/
if (up->fcr & UART_FCR_ENABLE_FIFO) {
- lsr = p->serial_in(p, UART_LSR);
+ lsr = serial_port_in(p, UART_LSR);
if (!(lsr & UART_LSR_DR))
return;
}
- (void)p->serial_in(p, UART_RX);
+ serial_port_in(p, UART_RX);
}
static void dw8250_check_lcr(struct uart_port *p, int offset, int value)
@@ -139,7 +139,7 @@ static void dw8250_check_lcr(struct uart_port *p, int offset, int value)
/* Make sure LCR write wasn't ignored */
while (tries--) {
- unsigned int lcr = p->serial_in(p, offset);
+ unsigned int lcr = serial_port_in(p, offset);
if ((value & ~UART_LCR_SPAR) == (lcr & ~UART_LCR_SPAR))
return;
@@ -260,7 +260,7 @@ static int dw8250_handle_irq(struct uart_port *p)
{
struct uart_8250_port *up = up_to_u8250p(p);
struct dw8250_data *d = to_dw8250_data(p->private_data);
- unsigned int iir = p->serial_in(p, UART_IIR);
+ unsigned int iir = serial_port_in(p, UART_IIR);
bool rx_timeout = (iir & 0x3f) == UART_IIR_RX_TIMEOUT;
unsigned int quirks = d->pdata->quirks;
unsigned int status;
@@ -281,7 +281,7 @@ static int dw8250_handle_irq(struct uart_port *p)
status = serial_lsr_in(up);
if (!(status & (UART_LSR_DR | UART_LSR_BI)))
- (void) p->serial_in(p, UART_RX);
+ serial_port_in(p, UART_RX);
uart_port_unlock_irqrestore(p, flags);
}
@@ -303,7 +303,7 @@ static int dw8250_handle_irq(struct uart_port *p)
if ((iir & UART_IIR_BUSY) == UART_IIR_BUSY) {
/* Clear the USR */
- (void)p->serial_in(p, d->pdata->usr_reg);
+ serial_port_in(p, d->pdata->usr_reg);
return 1;
}
@@ -390,7 +390,7 @@ static void dw8250_set_termios(struct uart_port *p, struct ktermios *termios,
static void dw8250_set_ldisc(struct uart_port *p, struct ktermios *termios)
{
struct uart_8250_port *up = up_to_u8250p(p);
- unsigned int mcr = p->serial_in(p, UART_MCR);
+ unsigned int mcr = serial_port_in(p, UART_MCR);
if (up->capabilities & UART_CAP_IRDA) {
if (termios->c_line == N_IRDA)
@@ -398,7 +398,7 @@ static void dw8250_set_ldisc(struct uart_port *p, struct ktermios *termios)
else
mcr &= ~DW_UART_MCR_SIRE;
- p->serial_out(p, UART_MCR, mcr);
+ serial_port_out(p, UART_MCR, mcr);
}
serial8250_do_set_ldisc(p, termios);
}
diff --git a/drivers/tty/serial/8250/8250_fsl.c b/drivers/tty/serial/8250/8250_fsl.c
index b4ed442082a8..59d3d2801c2e 100644
--- a/drivers/tty/serial/8250/8250_fsl.c
+++ b/drivers/tty/serial/8250/8250_fsl.c
@@ -32,7 +32,7 @@ int fsl8250_handle_irq(struct uart_port *port)
uart_port_lock_irqsave(&up->port, &flags);
- iir = port->serial_in(port, UART_IIR);
+ iir = serial_port_in(port, UART_IIR);
if (iir & UART_IIR_NO_INT) {
uart_port_unlock_irqrestore(&up->port, flags);
return 0;
@@ -54,12 +54,12 @@ int fsl8250_handle_irq(struct uart_port *port)
if (unlikely((iir & UART_IIR_ID) == UART_IIR_RLSI &&
(up->lsr_saved_flags & UART_LSR_BI))) {
up->lsr_saved_flags &= ~UART_LSR_BI;
- port->serial_in(port, UART_RX);
+ serial_port_in(port, UART_RX);
uart_port_unlock_irqrestore(&up->port, flags);
return 1;
}
- lsr = orig_lsr = up->port.serial_in(&up->port, UART_LSR);
+ lsr = orig_lsr = serial_port_in(port, UART_LSR);
/* Process incoming characters first */
if ((lsr & (UART_LSR_DR | UART_LSR_BI)) &&
@@ -71,7 +71,7 @@ int fsl8250_handle_irq(struct uart_port *port)
if ((orig_lsr & UART_LSR_OE) && (up->overrun_backoff_time_ms > 0)) {
unsigned long delay;
- up->ier = port->serial_in(port, UART_IER);
+ up->ier = serial_port_in(port, UART_IER);
if (up->ier & (UART_IER_RLSI | UART_IER_RDI)) {
port->ops->stop_rx(port);
} else {
diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c
index 0f4ce0c69114..dc9e3e25d55f 100644
--- a/drivers/tty/serial/8250/8250_omap.c
+++ b/drivers/tty/serial/8250/8250_omap.c
@@ -688,7 +688,7 @@ static irqreturn_t omap8250_irq(int irq, void *dev_id)
/* Synchronize UART_IER access against the console. */
uart_port_lock(port);
- up->ier = port->serial_in(port, UART_IER);
+ up->ier = serial_port_in(port, UART_IER);
if (up->ier & (UART_IER_RLSI | UART_IER_RDI)) {
port->ops->stop_rx(port);
} else {
--
2.54.0
^ permalink raw reply related
* [PATCH 6.12.y 6/8] serial: 8250_dw: Rework dw8250_handle_irq() locking and IIR handling
From: Ionut Nechita (Wind River) @ 2026-05-10 13:40 UTC (permalink / raw)
To: ilpo.jarvinen, gregkh
Cc: andriy.shevchenko, linux-serial, Bandal, Shankar, Murthy, Shanth,
stable, Ionut Nechita
In-Reply-To: <20260510134011.618215-1-ionut.nechita@windriver.com>
From: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
commit 883c5a2bc934c165c4491d1ef7da0ac4e9765077 upstream.
dw8250_handle_irq() takes port's lock multiple times with no good
reason to release it in between and calls serial8250_handle_irq()
that also takes port's lock.
Take port's lock only once in dw8250_handle_irq() and use
serial8250_handle_irq_locked() to avoid releasing port's lock in
between.
As IIR_NO_INT check in serial8250_handle_irq() was outside of port's
lock, it has to be done already in dw8250_handle_irq().
DW UART can, in addition to IIR_NO_INT, report BUSY_DETECT (0x7) which
collided with the IIR_NO_INT (0x1) check in serial8250_handle_irq()
(because & is used instead of ==) meaning that no other work is done by
serial8250_handle_irq() during an BUSY_DETECT interrupt.
This allows reorganizing code in dw8250_handle_irq() to do both
IIR_NO_INT and BUSY_DETECT handling right at the start simplifying
the logic.
Tested-by: Bandal, Shankar <shankar.bandal@intel.com>
Tested-by: Murthy, Shanth <shanth.murthy@intel.com>
Cc: stable <stable@kernel.org>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Link: https://patch.msgid.link/20260203171049.4353-5-ilpo.jarvinen@linux.intel.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
[Ionut: adapt to 6.12.y - replace guard(uart_port_lock_irqsave)(p)
in dw8250_handle_irq() with explicit uart_port_lock_irqsave/
unlock_irqrestore around the post-switch body; the cleanup-based
uart_port_lock_irqsave guard class is not present in 6.12.y.]
Signed-off-by: Ionut Nechita <ionut.nechita@windriver.com>
---
drivers/tty/serial/8250/8250_dw.c | 36 +++++++++++++++++++------------
1 file changed, 22 insertions(+), 14 deletions(-)
diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c
index 05e45b63e5f5..1076b72c120b 100644
--- a/drivers/tty/serial/8250/8250_dw.c
+++ b/drivers/tty/serial/8250/8250_dw.c
@@ -9,6 +9,9 @@
* LCR is written whilst busy. If it is, then a busy detect interrupt is
* raised, the LCR needs to be rewritten and the uart status register read.
*/
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/cleanup.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/device.h>
@@ -40,6 +43,8 @@
#define RZN1_UART_RDMACR 0x110 /* DMA Control Register Receive Mode */
/* DesignWare specific register fields */
+#define DW_UART_IIR_IID GENMASK(3, 0)
+
#define DW_UART_MCR_SIRE BIT(6)
/* Renesas specific register fields */
@@ -314,6 +319,19 @@ static int dw8250_handle_irq(struct uart_port *p)
unsigned int status;
unsigned long flags;
+ switch (FIELD_GET(DW_UART_IIR_IID, iir)) {
+ case UART_IIR_NO_INT:
+ return 0;
+
+ case UART_IIR_BUSY:
+ /* Clear the USR */
+ serial_port_in(p, d->pdata->usr_reg);
+
+ return 1;
+ }
+
+ uart_port_lock_irqsave(p, &flags);
+
/*
* There are ways to get Designware-based UARTs into a state where
* they are asserting UART_IIR_RX_TIMEOUT but there is no actual
@@ -325,20 +343,15 @@ static int dw8250_handle_irq(struct uart_port *p)
* so we limit the workaround only to non-DMA mode.
*/
if (!up->dma && rx_timeout) {
- uart_port_lock_irqsave(p, &flags);
status = serial_lsr_in(up);
if (!(status & (UART_LSR_DR | UART_LSR_BI)))
serial_port_in(p, UART_RX);
-
- uart_port_unlock_irqrestore(p, flags);
}
/* Manually stop the Rx DMA transfer when acting as flow controller */
if (quirks & DW_UART_QUIRK_IS_DMA_FC && up->dma && up->dma->rx_running && rx_timeout) {
- uart_port_lock_irqsave(p, &flags);
status = serial_lsr_in(up);
- uart_port_unlock_irqrestore(p, flags);
if (status & (UART_LSR_DR | UART_LSR_BI)) {
dw8250_writel_ext(p, RZN1_UART_RDMACR, 0);
@@ -346,17 +359,11 @@ static int dw8250_handle_irq(struct uart_port *p)
}
}
- if (serial8250_handle_irq(p, iir))
- return 1;
-
- if ((iir & UART_IIR_BUSY) == UART_IIR_BUSY) {
- /* Clear the USR */
- serial_port_in(p, d->pdata->usr_reg);
+ serial8250_handle_irq_locked(p, iir);
- return 1;
- }
+ uart_port_unlock_irqrestore(p, flags);
- return 0;
+ return 1;
}
static void dw8250_clk_work_cb(struct work_struct *work)
@@ -859,6 +866,7 @@ static struct platform_driver dw8250_platform_driver = {
module_platform_driver(dw8250_platform_driver);
+MODULE_IMPORT_NS("SERIAL_8250");
MODULE_AUTHOR("Jamie Iles");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Synopsys DesignWare 8250 serial port driver");
--
2.54.0
^ permalink raw reply related
* [PATCH 6.12.y 5/8] serial: 8250: Add serial8250_handle_irq_locked()
From: Ionut Nechita (Wind River) @ 2026-05-10 13:40 UTC (permalink / raw)
To: ilpo.jarvinen, gregkh
Cc: andriy.shevchenko, linux-serial, Bandal, Shankar, Murthy, Shanth,
stable, Ionut Nechita
In-Reply-To: <20260510134011.618215-1-ionut.nechita@windriver.com>
From: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
commit 8324a54f604da18f21070702a8ad82ab2062787b upstream.
8250_port exports serial8250_handle_irq() to HW specific 8250 drivers.
It takes port's lock within but a HW specific 8250 driver may want to
take port's lock itself, do something, and then call the generic
handler in 8250_port but to do that, the caller has to release port's
lock for no good reason.
Introduce serial8250_handle_irq_locked() which a HW specific driver can
call while already holding port's lock.
As this is new export, put it straight into a namespace (where all 8250
exports should eventually be moved).
Tested-by: Bandal, Shankar <shankar.bandal@intel.com>
Tested-by: Murthy, Shanth <shanth.murthy@intel.com>
Cc: stable <stable@kernel.org>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Link: https://patch.msgid.link/20260203171049.4353-4-ilpo.jarvinen@linux.intel.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
[Ionut: adapt to 6.12.y -
- replace guard(uart_port_lock_irqsave)(port) in
serial8250_handle_irq() with explicit uart_port_lock_irqsave/
unlock_irqrestore; the cleanup-based uart_port_lock_irqsave
guard class is not present in 6.12.y;
- use the bare-identifier form EXPORT_SYMBOL_NS_GPL(...,
SERIAL_8250) rather than the string form ("SERIAL_8250");
in 6.12.y the macro stringifies the namespace argument via
__stringify(ns), so it must be an unquoted identifier.]
Signed-off-by: Ionut Nechita <ionut.nechita@windriver.com>
---
drivers/tty/serial/8250/8250_port.c | 27 +++++++++++++++++++--------
include/linux/serial_8250.h | 1 +
2 files changed, 20 insertions(+), 8 deletions(-)
diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c
index d0f2e634ac30..1e6dca739eab 100644
--- a/drivers/tty/serial/8250/8250_port.c
+++ b/drivers/tty/serial/8250/8250_port.c
@@ -18,6 +18,7 @@
#include <linux/irq.h>
#include <linux/console.h>
#include <linux/gpio/consumer.h>
+#include <linux/lockdep.h>
#include <linux/sysrq.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
@@ -1884,20 +1885,16 @@ static bool handle_rx_dma(struct uart_8250_port *up, unsigned int iir)
}
/*
- * This handles the interrupt from one port.
+ * Context: port's lock must be held by the caller.
*/
-int serial8250_handle_irq(struct uart_port *port, unsigned int iir)
+void serial8250_handle_irq_locked(struct uart_port *port, unsigned int iir)
{
struct uart_8250_port *up = up_to_u8250p(port);
struct tty_port *tport = &port->state->port;
bool skip_rx = false;
- unsigned long flags;
u16 status;
- if (iir & UART_IIR_NO_INT)
- return 0;
-
- uart_port_lock_irqsave(port, &flags);
+ lockdep_assert_held_once(&port->lock);
status = serial_lsr_in(up);
@@ -1930,8 +1927,22 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir)
else if (!up->dma->tx_running)
__stop_tx(up);
}
+}
+EXPORT_SYMBOL_NS_GPL(serial8250_handle_irq_locked, SERIAL_8250);
- uart_unlock_and_check_sysrq_irqrestore(port, flags);
+/*
+ * This handles the interrupt from one port.
+ */
+int serial8250_handle_irq(struct uart_port *port, unsigned int iir)
+{
+ unsigned long flags;
+
+ if (iir & UART_IIR_NO_INT)
+ return 0;
+
+ uart_port_lock_irqsave(port, &flags);
+ serial8250_handle_irq_locked(port, iir);
+ uart_port_unlock_irqrestore(port, flags);
return 1;
}
diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h
index e0717c8393d7..7e0439ecd496 100644
--- a/include/linux/serial_8250.h
+++ b/include/linux/serial_8250.h
@@ -195,6 +195,7 @@ void serial8250_do_set_mctrl(struct uart_port *port, unsigned int mctrl);
void serial8250_do_set_divisor(struct uart_port *port, unsigned int baud,
unsigned int quot);
int fsl8250_handle_irq(struct uart_port *port);
+void serial8250_handle_irq_locked(struct uart_port *port, unsigned int iir);
int serial8250_handle_irq(struct uart_port *port, unsigned int iir);
u16 serial8250_rx_chars(struct uart_8250_port *up, u16 lsr);
void serial8250_read_char(struct uart_8250_port *up, u16 lsr);
--
2.54.0
^ permalink raw reply related
* [PATCH 6.12.y 3/8] serial: 8250: Protect LCR write in shutdown
From: Ionut Nechita (Wind River) @ 2026-05-10 13:40 UTC (permalink / raw)
To: ilpo.jarvinen, gregkh
Cc: andriy.shevchenko, linux-serial, Bandal, Shankar, Murthy, Shanth,
stable, Ionut Nechita
In-Reply-To: <20260510134011.618215-1-ionut.nechita@windriver.com>
From: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
commit 59a33d83bbe6d73d2071d7ae21590b29faed0503 upstream.
The 8250_dw driver needs to potentially perform very complex operations
during LCR writes because its BUSY handling prevents updates to LCR
while UART is BUSY (which is not fully under our control without those
complex operations). Thus, LCR writes should occur under port's lock.
Move LCR write under port's lock in serial8250_do_shutdown(). Also
split the LCR RMW so that the logic is on a separate line for clarity.
Reported-by: Bandal, Shankar <shankar.bandal@intel.com>
Tested-by: Bandal, Shankar <shankar.bandal@intel.com>
Tested-by: Murthy, Shanth <shanth.murthy@intel.com>
Cc: stable <stable@kernel.org>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Link: https://patch.msgid.link/20260203171049.4353-2-ilpo.jarvinen@linux.intel.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
[Ionut: adapt to 6.12.y - keep both 'unsigned long flags' and the
new 'u32 lcr' declaration; place the LCR RMW inside the existing
explicit uart_port_lock_irqsave/unlock_irqrestore region rather
than the scoped_guard() block used upstream (cleanup-based
uart_port_lock_irqsave guard class is not present in 6.12.y).]
Signed-off-by: Ionut Nechita <ionut.nechita@windriver.com>
---
drivers/tty/serial/8250/8250_port.c | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c
index b4c8388ea6fc..d0f2e634ac30 100644
--- a/drivers/tty/serial/8250/8250_port.c
+++ b/drivers/tty/serial/8250/8250_port.c
@@ -2462,6 +2462,7 @@ void serial8250_do_shutdown(struct uart_port *port)
{
struct uart_8250_port *up = up_to_u8250p(port);
unsigned long flags;
+ u32 lcr;
serial8250_rpm_get(up);
/*
@@ -2488,13 +2489,13 @@ void serial8250_do_shutdown(struct uart_port *port)
port->mctrl &= ~TIOCM_OUT2;
serial8250_set_mctrl(port, port->mctrl);
+
+ /* Disable break condition */
+ lcr = serial_port_in(port, UART_LCR);
+ lcr &= ~UART_LCR_SBC;
+ serial_port_out(port, UART_LCR, lcr);
uart_port_unlock_irqrestore(port, flags);
- /*
- * Disable break condition and FIFOs
- */
- serial_port_out(port, UART_LCR,
- serial_port_in(port, UART_LCR) & ~UART_LCR_SBC);
serial8250_clear_fifos(up);
#ifdef CONFIG_SERIAL_8250_RSA
--
2.54.0
^ permalink raw reply related
* [PATCH 6.12.y 7/8] serial: 8250_dw: Rework IIR_NO_INT handling to stop interrupt storm
From: Ionut Nechita (Wind River) @ 2026-05-10 13:40 UTC (permalink / raw)
To: ilpo.jarvinen, gregkh
Cc: andriy.shevchenko, linux-serial, stable, Bandal, Shankar,
Murthy, Shanth, Ionut Nechita
In-Reply-To: <20260510134011.618215-1-ionut.nechita@windriver.com>
From: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
commit 73a4ed8f9efaaaf8207614ccc1c9d5ca1888f23a upstream.
INTC10EE UART can end up into an interrupt storm where it reports
IIR_NO_INT (0x1). If the storm happens during active UART operation, it
is promptly stopped by IIR value change due to Rx or Tx events.
However, when there is no activity, either due to idle serial line or
due to specific circumstances such as during shutdown that writes
IER=0, there is nothing to stop the storm.
During shutdown the storm is particularly problematic because
serial8250_do_shutdown() calls synchronize_irq() that will hang in
waiting for the storm to finish which never happens.
This problem can also result in triggering a warning:
irq 45: nobody cared (try booting with the "irqpoll" option)
[...snip...]
handlers:
serial8250_interrupt
Disabling IRQ #45
Normal means to reset interrupt status by reading LSR, MSR, USR, or RX
register do not result in the UART deasserting the IRQ.
Add a quirk to INTC10EE UARTs to enable Tx interrupts if UART's Tx is
currently empty and inactive. Rework IIR_NO_INT to keep track of the
number of consecutive IIR_NO_INT, and on fourth one perform the quirk.
Enabling Tx interrupts should change IIR value from IIR_NO_INT to
IIR_THRI which has been observed to stop the storm.
Fixes: e92fad024929 ("serial: 8250_dw: Add ACPI ID for Granite Rapids-D UART")
Cc: stable <stable@kernel.org>
Reported-by: Bandal, Shankar <shankar.bandal@intel.com>
Tested-by: Bandal, Shankar <shankar.bandal@intel.com>
Tested-by: Murthy, Shanth <shanth.murthy@intel.com>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Link: https://patch.msgid.link/20260203171049.4353-6-ilpo.jarvinen@linux.intel.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
[Ionut: adapt to 6.12.y -
- move uart_port_lock_irqsave() before the switch (matching the
upstream guard() placement) and convert the early returns into
a goto-out pattern with uart_port_unlock_irqrestore;
- drop the dw8250_setup_dma_filter() call (helper introduced by
c213375e3283 'serial: 8250_dw: Call dw8250_quirks() conditionally'
which is not backported);
- keep only the new p->shutdown = dw8250_shutdown assignment in the
existing 'else if (data->pdata)' branch; in 6.12.y p->handle_irq
is unconditionally set earlier in dw8250_probe() so the upstream
reassignment is unnecessary here.]
Signed-off-by: Ionut Nechita <ionut.nechita@windriver.com>
---
drivers/tty/serial/8250/8250_dw.c | 75 ++++++++++++++++++++++++++++---
1 file changed, 69 insertions(+), 6 deletions(-)
diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c
index 1076b72c120b..1650107d1cbc 100644
--- a/drivers/tty/serial/8250/8250_dw.c
+++ b/drivers/tty/serial/8250/8250_dw.c
@@ -61,6 +61,13 @@
#define DW_UART_QUIRK_IS_DMA_FC BIT(3)
#define DW_UART_QUIRK_APMC0D08 BIT(4)
#define DW_UART_QUIRK_CPR_VALUE BIT(5)
+#define DW_UART_QUIRK_IER_KICK BIT(6)
+
+/*
+ * Number of consecutive IIR_NO_INT interrupts required to trigger interrupt
+ * storm prevention code.
+ */
+#define DW_UART_QUIRK_IER_KICK_THRES 4
struct dw8250_platform_data {
u8 usr_reg;
@@ -82,6 +89,8 @@ struct dw8250_data {
unsigned int skip_autocfg:1;
unsigned int uart_16550_compatible:1;
+
+ u8 no_int_count;
};
static inline struct dw8250_data *to_dw8250_data(struct dw8250_port_data *data)
@@ -308,6 +317,29 @@ static unsigned int dw8250_serial_in32be(struct uart_port *p, int offset)
return dw8250_modify_msr(p, offset, value);
}
+/*
+ * INTC10EE UART can IRQ storm while reporting IIR_NO_INT. Inducing IIR value
+ * change has been observed to break the storm.
+ *
+ * If Tx is empty (THRE asserted), we use here IER_THRI to cause IIR_NO_INT ->
+ * IIR_THRI transition.
+ */
+static void dw8250_quirk_ier_kick(struct uart_port *p)
+{
+ struct uart_8250_port *up = up_to_u8250p(p);
+ u32 lsr;
+
+ if (up->ier & UART_IER_THRI)
+ return;
+
+ lsr = serial_lsr_in(up);
+ if (!(lsr & UART_LSR_THRE))
+ return;
+
+ serial_port_out(p, UART_IER, up->ier | UART_IER_THRI);
+ serial_port_in(p, UART_LCR); /* safe, no side-effects */
+ serial_port_out(p, UART_IER, up->ier);
+}
static int dw8250_handle_irq(struct uart_port *p)
{
@@ -318,19 +350,35 @@ static int dw8250_handle_irq(struct uart_port *p)
unsigned int quirks = d->pdata->quirks;
unsigned int status;
unsigned long flags;
+ int ret = 1;
+
+ uart_port_lock_irqsave(p, &flags);
switch (FIELD_GET(DW_UART_IIR_IID, iir)) {
case UART_IIR_NO_INT:
- return 0;
+ if (d->uart_16550_compatible || up->dma) {
+ ret = 0;
+ goto out;
+ }
+
+ if (quirks & DW_UART_QUIRK_IER_KICK &&
+ d->no_int_count == (DW_UART_QUIRK_IER_KICK_THRES - 1))
+ dw8250_quirk_ier_kick(p);
+ d->no_int_count = (d->no_int_count + 1) % DW_UART_QUIRK_IER_KICK_THRES;
+
+ ret = 0;
+ goto out;
case UART_IIR_BUSY:
/* Clear the USR */
serial_port_in(p, d->pdata->usr_reg);
- return 1;
+ d->no_int_count = 0;
+
+ goto out;
}
- uart_port_lock_irqsave(p, &flags);
+ d->no_int_count = 0;
/*
* There are ways to get Designware-based UARTs into a state where
@@ -361,9 +409,9 @@ static int dw8250_handle_irq(struct uart_port *p)
serial8250_handle_irq_locked(p, iir);
+out:
uart_port_unlock_irqrestore(p, flags);
-
- return 1;
+ return ret;
}
static void dw8250_clk_work_cb(struct work_struct *work)
@@ -561,6 +609,14 @@ static void dw8250_reset_control_assert(void *data)
reset_control_assert(data);
}
+static void dw8250_shutdown(struct uart_port *port)
+{
+ struct dw8250_data *d = to_dw8250_data(port->private_data);
+
+ serial8250_do_shutdown(port);
+ d->no_int_count = 0;
+}
+
static int dw8250_probe(struct platform_device *pdev)
{
struct uart_8250_port uart = {}, *up = &uart;
@@ -688,6 +744,8 @@ static int dw8250_probe(struct platform_device *pdev)
/* If the Busy Functionality is not implemented, don't handle it */
if (data->uart_16550_compatible)
p->handle_irq = NULL;
+ else if (data->pdata)
+ p->shutdown = dw8250_shutdown;
if (!data->skip_autocfg)
dw8250_setup_port(p);
@@ -819,6 +877,11 @@ static const struct dw8250_platform_data dw8250_skip_set_rate_data = {
.quirks = DW_UART_QUIRK_SKIP_SET_RATE,
};
+static const struct dw8250_platform_data dw8250_intc10ee = {
+ .usr_reg = DW_UART_USR,
+ .quirks = DW_UART_QUIRK_IER_KICK,
+};
+
static const struct of_device_id dw8250_of_match[] = {
{ .compatible = "snps,dw-apb-uart", .data = &dw8250_dw_apb },
{ .compatible = "cavium,octeon-3860-uart", .data = &dw8250_octeon_3860_data },
@@ -848,7 +911,7 @@ static const struct acpi_device_id dw8250_acpi_match[] = {
{ "INT33C5", (kernel_ulong_t)&dw8250_dw_apb },
{ "INT3434", (kernel_ulong_t)&dw8250_dw_apb },
{ "INT3435", (kernel_ulong_t)&dw8250_dw_apb },
- { "INTC10EE", (kernel_ulong_t)&dw8250_dw_apb },
+ { "INTC10EE", (kernel_ulong_t)&dw8250_intc10ee },
{ },
};
MODULE_DEVICE_TABLE(acpi, dw8250_acpi_match);
--
2.54.0
^ permalink raw reply related
* [PATCH 6.12.y 4/8] serial: 8250_dw: Avoid unnecessary LCR writes
From: Ionut Nechita (Wind River) @ 2026-05-10 13:40 UTC (permalink / raw)
To: ilpo.jarvinen, gregkh
Cc: andriy.shevchenko, linux-serial, Bandal, Shankar, Murthy, Shanth,
stable, Ionut Nechita
In-Reply-To: <20260510134011.618215-1-ionut.nechita@windriver.com>
From: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
commit 8002d6d6d0d8a36a7d6ca523b17a51cb0fa7c3c3 upstream.
When DW UART is configured with BUSY flag, LCR writes may not always
succeed which can make any LCR write complex and very expensive.
Performing write directly can trigger IRQ and the driver has to perform
complex and distruptive sequence while retrying the write.
Therefore, it's better to avoid doing LCR write that would not change
the value of the LCR register. Add LCR write avoidance code into the
8250_dw driver's .serial_out() functions.
Reported-by: Bandal, Shankar <shankar.bandal@intel.com>
Tested-by: Bandal, Shankar <shankar.bandal@intel.com>
Tested-by: Murthy, Shanth <shanth.murthy@intel.com>
Cc: stable <stable@kernel.org>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Link: https://patch.msgid.link/20260203171049.4353-3-ilpo.jarvinen@linux.intel.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Ionut Nechita <ionut.nechita@windriver.com>
---
drivers/tty/serial/8250/8250_dw.c | 31 +++++++++++++++++++++++++++++++
1 file changed, 31 insertions(+)
diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c
index 88c55336d50f..05e45b63e5f5 100644
--- a/drivers/tty/serial/8250/8250_dw.c
+++ b/drivers/tty/serial/8250/8250_dw.c
@@ -181,6 +181,22 @@ static void dw8250_check_lcr(struct uart_port *p, int offset, int value)
*/
}
+/*
+ * With BUSY, LCR writes can be very expensive (IRQ + complex retry logic).
+ * If the write does not change the value of the LCR register, skip it entirely.
+ */
+static bool dw8250_can_skip_reg_write(struct uart_port *p, unsigned int offset, u32 value)
+{
+ struct dw8250_data *d = to_dw8250_data(p->private_data);
+ u32 lcr;
+
+ if (offset != UART_LCR || d->uart_16550_compatible)
+ return false;
+
+ lcr = serial_port_in(p, offset);
+ return lcr == value;
+}
+
/* Returns once the transmitter is empty or we run out of retries */
static void dw8250_tx_wait_empty(struct uart_port *p)
{
@@ -207,12 +223,18 @@ static void dw8250_tx_wait_empty(struct uart_port *p)
static void dw8250_serial_out(struct uart_port *p, int offset, int value)
{
+ if (dw8250_can_skip_reg_write(p, offset, value))
+ return;
+
writeb(value, p->membase + (offset << p->regshift));
dw8250_check_lcr(p, offset, value);
}
static void dw8250_serial_out38x(struct uart_port *p, int offset, int value)
{
+ if (dw8250_can_skip_reg_write(p, offset, value))
+ return;
+
/* Allow the TX to drain before we reconfigure */
if (offset == UART_LCR)
dw8250_tx_wait_empty(p);
@@ -237,6 +259,9 @@ static unsigned int dw8250_serial_inq(struct uart_port *p, int offset)
static void dw8250_serial_outq(struct uart_port *p, int offset, int value)
{
+ if (dw8250_can_skip_reg_write(p, offset, value))
+ return;
+
value &= 0xff;
__raw_writeq(value, p->membase + (offset << p->regshift));
/* Read back to ensure register write ordering. */
@@ -248,6 +273,9 @@ static void dw8250_serial_outq(struct uart_port *p, int offset, int value)
static void dw8250_serial_out32(struct uart_port *p, int offset, int value)
{
+ if (dw8250_can_skip_reg_write(p, offset, value))
+ return;
+
writel(value, p->membase + (offset << p->regshift));
dw8250_check_lcr(p, offset, value);
}
@@ -261,6 +289,9 @@ static unsigned int dw8250_serial_in32(struct uart_port *p, int offset)
static void dw8250_serial_out32be(struct uart_port *p, int offset, int value)
{
+ if (dw8250_can_skip_reg_write(p, offset, value))
+ return;
+
iowrite32be(value, p->membase + (offset << p->regshift));
dw8250_check_lcr(p, offset, value);
}
--
2.54.0
^ permalink raw reply related
* [PATCH 6.12.y 8/8] serial: 8250_dw: Ensure BUSY is deasserted
From: Ionut Nechita (Wind River) @ 2026-05-10 13:40 UTC (permalink / raw)
To: ilpo.jarvinen, gregkh
Cc: andriy.shevchenko, linux-serial, stable, qianfan Zhao,
Adriana Nicolae, Bandal, Shankar, Murthy, Shanth, Ionut Nechita
In-Reply-To: <20260510134011.618215-1-ionut.nechita@windriver.com>
From: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
commit a7b9ce39fbe4ae2919fe4f7ac16c293cb6632d30 upstream.
DW UART cannot write to LCR, DLL, and DLH while BUSY is asserted.
Existance of BUSY depends on uart_16550_compatible, if UART HW is
configured with it those registers can always be written.
There currently is dw8250_force_idle() which attempts to achieve
non-BUSY state by disabling FIFO, however, the solution is unreliable
when Rx keeps getting more and more characters.
Create a sequence of operations that ensures UART cannot keep BUSY
asserted indefinitely. The new sequence relies on enabling loopback mode
temporarily to prevent incoming Rx characters keeping UART BUSY.
Ensure no Tx in ongoing while the UART is switches into the loopback
mode (requires exporting serial8250_fifo_wait_for_lsr_thre() and adding
DMA Tx pause/resume functions).
According to tests performed by Adriana Nicolae <adriana@arista.com>,
simply disabling FIFO or clearing FIFOs only once does not always
ensure BUSY is deasserted but up to two tries may be needed. This could
be related to ongoing Rx of a character (a guess, not known for sure).
Therefore, retry FIFO clearing a few times (retry limit 4 is arbitrary
number but using, e.g., p->fifosize seems overly large). Tests
performed by others did not exhibit similar challenge but it does not
seem harmful to leave the FIFO clearing loop in place for all DW UARTs
with BUSY functionality.
Use the new dw8250_idle_enter/exit() to do divisor writes and LCR
writes. In case of plain LCR writes, opportunistically try to update
LCR first and only invoke dw8250_idle_enter() if the write did not
succeed (it has been observed that in practice most LCR writes do
succeed without complications).
This issue was first reported by qianfan Zhao who put lots of debugging
effort into understanding the solution space.
Fixes: c49436b657d0 ("serial: 8250_dw: Improve unwritable LCR workaround")
Fixes: 7d4008ebb1c9 ("tty: add a DesignWare 8250 driver")
Cc: stable <stable@kernel.org>
Reported-by: qianfan Zhao <qianfanguijin@163.com>
Link: https://lore.kernel.org/linux-serial/289bb78a-7509-1c5c-2923-a04ed3b6487d@163.com/
Reported-by: Adriana Nicolae <adriana@arista.com>
Link: https://lore.kernel.org/linux-serial/20250819182322.3451959-1-adriana@arista.com/
Reported-by: Bandal, Shankar <shankar.bandal@intel.com>
Tested-by: Bandal, Shankar <shankar.bandal@intel.com>
Tested-by: Murthy, Shanth <shanth.murthy@intel.com>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Link: https://patch.msgid.link/20260203171049.4353-8-ilpo.jarvinen@linux.intel.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
[Ionut: adapt to 6.12.y -
- trivial conflict in dw8250_check_lcr(): 6.12.y still has the
(int offset, int value) signature (the 'unsigned int offset,
u32 value' rework from fc9ceb501e38 is not backported), but
the conflicting tries/loop body is removed by this patch
anyway - took the new code as-is;
- use the bare-identifier form EXPORT_SYMBOL_NS_GPL(...,
SERIAL_8250) and MODULE_IMPORT_NS(SERIAL_8250) rather than
the string forms; in 6.12.y both macros stringify the
namespace argument via __stringify(ns), so it must be an
unquoted identifier.]
Signed-off-by: Ionut Nechita <ionut.nechita@windriver.com>
---
drivers/tty/serial/8250/8250.h | 25 +++++
drivers/tty/serial/8250/8250_dw.c | 165 ++++++++++++++++++++--------
drivers/tty/serial/8250/8250_port.c | 28 ++---
3 files changed, 162 insertions(+), 56 deletions(-)
diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h
index 10a706fe4b24..1fe8222ffacd 100644
--- a/drivers/tty/serial/8250/8250.h
+++ b/drivers/tty/serial/8250/8250.h
@@ -184,7 +184,9 @@ static unsigned int __maybe_unused serial_icr_read(struct uart_8250_port *up,
return value;
}
+void serial8250_clear_fifos(struct uart_8250_port *p);
void serial8250_clear_and_reinit_fifos(struct uart_8250_port *p);
+void serial8250_fifo_wait_for_lsr_thre(struct uart_8250_port *up, unsigned int count);
static inline u32 serial_dl_read(struct uart_8250_port *up)
{
@@ -402,6 +404,26 @@ static inline bool serial8250_tx_dma_running(struct uart_8250_port *p)
return dma && dma->tx_running;
}
+
+static inline void serial8250_tx_dma_pause(struct uart_8250_port *p)
+{
+ struct uart_8250_dma *dma = p->dma;
+
+ if (!dma->tx_running)
+ return;
+
+ dmaengine_pause(dma->txchan);
+}
+
+static inline void serial8250_tx_dma_resume(struct uart_8250_port *p)
+{
+ struct uart_8250_dma *dma = p->dma;
+
+ if (!dma->tx_running)
+ return;
+
+ dmaengine_resume(dma->txchan);
+}
#else
static inline int serial8250_tx_dma(struct uart_8250_port *p)
{
@@ -423,6 +445,9 @@ static inline bool serial8250_tx_dma_running(struct uart_8250_port *p)
{
return false;
}
+
+static inline void serial8250_tx_dma_pause(struct uart_8250_port *p) { }
+static inline void serial8250_tx_dma_resume(struct uart_8250_port *p) { }
#endif
static inline int ns16550a_goto_highspeed(struct uart_8250_port *up)
diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c
index 1650107d1cbc..630cc7227494 100644
--- a/drivers/tty/serial/8250/8250_dw.c
+++ b/drivers/tty/serial/8250/8250_dw.c
@@ -16,6 +16,7 @@
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/io.h>
+#include <linux/lockdep.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/notifier.h>
@@ -47,6 +48,8 @@
#define DW_UART_MCR_SIRE BIT(6)
+#define DW_UART_USR_BUSY BIT(0)
+
/* Renesas specific register fields */
#define RZN1_UART_xDMACR_DMA_EN BIT(0)
#define RZN1_UART_xDMACR_1_WORD_BURST (0 << 1)
@@ -89,6 +92,7 @@ struct dw8250_data {
unsigned int skip_autocfg:1;
unsigned int uart_16550_compatible:1;
+ unsigned int in_idle:1;
u8 no_int_count;
};
@@ -121,78 +125,151 @@ static inline int dw8250_modify_msr(struct uart_port *p, int offset, int value)
return value;
}
+static void dw8250_idle_exit(struct uart_port *p)
+{
+ struct dw8250_data *d = to_dw8250_data(p->private_data);
+ struct uart_8250_port *up = up_to_u8250p(p);
+
+ if (d->uart_16550_compatible)
+ return;
+
+ if (up->capabilities & UART_CAP_FIFO)
+ serial_port_out(p, UART_FCR, up->fcr);
+ serial_port_out(p, UART_MCR, up->mcr);
+ serial_port_out(p, UART_IER, up->ier);
+
+ /* DMA Rx is restarted by IRQ handler as needed. */
+ if (up->dma)
+ serial8250_tx_dma_resume(up);
+
+ d->in_idle = 0;
+}
+
/*
- * This function is being called as part of the uart_port::serial_out()
- * routine. Hence, it must not call serial_port_out() or serial_out()
- * against the modified registers here, i.e. LCR.
+ * Ensure BUSY is not asserted. If DW UART is configured with
+ * !uart_16550_compatible, the writes to LCR, DLL, and DLH fail while
+ * BUSY is asserted.
+ *
+ * Context: port's lock must be held
*/
-static void dw8250_force_idle(struct uart_port *p)
+static int dw8250_idle_enter(struct uart_port *p)
{
+ struct dw8250_data *d = to_dw8250_data(p->private_data);
+ unsigned int usr_reg = d->pdata ? d->pdata->usr_reg : DW_UART_USR;
struct uart_8250_port *up = up_to_u8250p(p);
- unsigned int lsr;
+ int retries;
+ u32 lsr;
- /*
- * The following call currently performs serial_out()
- * against the FCR register. Because it differs to LCR
- * there will be no infinite loop, but if it ever gets
- * modified, we might need a new custom version of it
- * that avoids infinite recursion.
- */
- serial8250_clear_and_reinit_fifos(up);
+ lockdep_assert_held_once(&p->lock);
+
+ if (d->uart_16550_compatible)
+ return 0;
+
+ d->in_idle = 1;
+
+ /* Prevent triggering interrupt from RBR filling */
+ serial_port_out(p, UART_IER, 0);
+
+ if (up->dma) {
+ serial8250_rx_dma_flush(up);
+ if (serial8250_tx_dma_running(up))
+ serial8250_tx_dma_pause(up);
+ }
/*
- * With PSLVERR_RESP_EN parameter set to 1, the device generates an
- * error response when an attempt to read an empty RBR with FIFO
- * enabled.
+ * Wait until Tx becomes empty + one extra frame time to ensure all bits
+ * have been sent on the wire.
+ *
+ * FIXME: frame_time delay is too long with very low baudrates.
*/
- if (up->fcr & UART_FCR_ENABLE_FIFO) {
- lsr = serial_port_in(p, UART_LSR);
- if (!(lsr & UART_LSR_DR))
- return;
+ serial8250_fifo_wait_for_lsr_thre(up, p->fifosize);
+ ndelay(p->frame_time);
+
+ serial_port_out(p, UART_MCR, up->mcr | UART_MCR_LOOP);
+
+ retries = 4; /* Arbitrary limit, 2 was always enough in tests */
+ do {
+ serial8250_clear_fifos(up);
+ if (!(serial_port_in(p, usr_reg) & DW_UART_USR_BUSY))
+ break;
+ /* FIXME: frame_time delay is too long with very low baudrates. */
+ ndelay(p->frame_time);
+ } while (--retries);
+
+ lsr = serial_lsr_in(up);
+ if (lsr & UART_LSR_DR) {
+ serial_port_in(p, UART_RX);
+ up->lsr_saved_flags = 0;
+ }
+
+ /* Now guaranteed to have BUSY deasserted? Just sanity check */
+ if (serial_port_in(p, usr_reg) & DW_UART_USR_BUSY) {
+ dw8250_idle_exit(p);
+ return -EBUSY;
}
- serial_port_in(p, UART_RX);
+ return 0;
+}
+
+static void dw8250_set_divisor(struct uart_port *p, unsigned int baud,
+ unsigned int quot, unsigned int quot_frac)
+{
+ struct uart_8250_port *up = up_to_u8250p(p);
+ int ret;
+
+ ret = dw8250_idle_enter(p);
+ if (ret < 0)
+ return;
+
+ serial_port_out(p, UART_LCR, up->lcr | UART_LCR_DLAB);
+ if (!(serial_port_in(p, UART_LCR) & UART_LCR_DLAB))
+ goto idle_failed;
+
+ serial_dl_write(up, quot);
+ serial_port_out(p, UART_LCR, up->lcr);
+
+idle_failed:
+ dw8250_idle_exit(p);
}
/*
* This function is being called as part of the uart_port::serial_out()
- * routine. Hence, it must not call serial_port_out() or serial_out()
- * against the modified registers here, i.e. LCR.
+ * routine. Hence, special care must be taken when serial_port_out() or
+ * serial_out() against the modified registers here, i.e. LCR (d->in_idle is
+ * used to break recursion loop).
*/
static void dw8250_check_lcr(struct uart_port *p, int offset, int value)
{
struct dw8250_data *d = to_dw8250_data(p->private_data);
- void __iomem *addr = p->membase + (offset << p->regshift);
- int tries = 1000;
+ u32 lcr;
+ int ret;
if (offset != UART_LCR || d->uart_16550_compatible)
return;
+ lcr = serial_port_in(p, UART_LCR);
+
/* Make sure LCR write wasn't ignored */
- while (tries--) {
- unsigned int lcr = serial_port_in(p, offset);
+ if ((value & ~UART_LCR_SPAR) == (lcr & ~UART_LCR_SPAR))
+ return;
- if ((value & ~UART_LCR_SPAR) == (lcr & ~UART_LCR_SPAR))
- return;
+ if (d->in_idle)
+ goto write_err;
- dw8250_force_idle(p);
+ ret = dw8250_idle_enter(p);
+ if (ret < 0)
+ goto write_err;
-#ifdef CONFIG_64BIT
- if (p->type == PORT_OCTEON)
- __raw_writeq(value & 0xff, addr);
- else
-#endif
- if (p->iotype == UPIO_MEM32)
- writel(value, addr);
- else if (p->iotype == UPIO_MEM32BE)
- iowrite32be(value, addr);
- else
- writeb(value, addr);
- }
+ serial_port_out(p, UART_LCR, value);
+ dw8250_idle_exit(p);
+ return;
+
+write_err:
/*
* FIXME: this deadlocks if port->lock is already held
* dev_err(p->dev, "Couldn't set LCR to %d\n", value);
*/
+ return; /* Silences "label at the end of compound statement" */
}
/*
@@ -636,8 +713,10 @@ static int dw8250_probe(struct platform_device *pdev)
p->type = PORT_8250;
p->flags = UPF_FIXED_PORT;
p->dev = dev;
+
p->set_ldisc = dw8250_set_ldisc;
p->set_termios = dw8250_set_termios;
+ p->set_divisor = dw8250_set_divisor;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
@@ -929,7 +1008,7 @@ static struct platform_driver dw8250_platform_driver = {
module_platform_driver(dw8250_platform_driver);
-MODULE_IMPORT_NS("SERIAL_8250");
+MODULE_IMPORT_NS(SERIAL_8250);
MODULE_AUTHOR("Jamie Iles");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Synopsys DesignWare 8250 serial port driver");
diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c
index 1e6dca739eab..ce26d1649ca5 100644
--- a/drivers/tty/serial/8250/8250_port.c
+++ b/drivers/tty/serial/8250/8250_port.c
@@ -478,7 +478,7 @@ serial_port_out_sync(struct uart_port *p, int offset, int value)
/*
* FIFO support.
*/
-static void serial8250_clear_fifos(struct uart_8250_port *p)
+void serial8250_clear_fifos(struct uart_8250_port *p)
{
if (p->capabilities & UART_CAP_FIFO) {
serial_out(p, UART_FCR, UART_FCR_ENABLE_FIFO);
@@ -487,6 +487,7 @@ static void serial8250_clear_fifos(struct uart_8250_port *p)
serial_out(p, UART_FCR, 0);
}
}
+EXPORT_SYMBOL_NS_GPL(serial8250_clear_fifos, SERIAL_8250);
static enum hrtimer_restart serial8250_em485_handle_start_tx(struct hrtimer *t);
static enum hrtimer_restart serial8250_em485_handle_stop_tx(struct hrtimer *t);
@@ -3288,6 +3289,17 @@ void serial8250_set_defaults(struct uart_8250_port *up)
}
EXPORT_SYMBOL_GPL(serial8250_set_defaults);
+void serial8250_fifo_wait_for_lsr_thre(struct uart_8250_port *up, unsigned int count)
+{
+ unsigned int i;
+
+ for (i = 0; i < count; i++) {
+ if (wait_for_lsr(up, UART_LSR_THRE))
+ return;
+ }
+}
+EXPORT_SYMBOL_NS_GPL(serial8250_fifo_wait_for_lsr_thre, SERIAL_8250);
+
#ifdef CONFIG_SERIAL_8250_CONSOLE
static void serial8250_console_putchar(struct uart_port *port, unsigned char ch)
@@ -3324,16 +3336,6 @@ static void serial8250_console_restore(struct uart_8250_port *up)
serial8250_out_MCR(up, up->mcr | UART_MCR_DTR | UART_MCR_RTS);
}
-static void fifo_wait_for_lsr(struct uart_8250_port *up, unsigned int count)
-{
- unsigned int i;
-
- for (i = 0; i < count; i++) {
- if (wait_for_lsr(up, UART_LSR_THRE))
- return;
- }
-}
-
/*
* Print a string to the serial port using the device FIFO
*
@@ -3351,7 +3353,7 @@ static void serial8250_console_fifo_write(struct uart_8250_port *up,
while (s != end) {
/* Allow timeout for each byte of a possibly full FIFO */
- fifo_wait_for_lsr(up, fifosize);
+ serial8250_fifo_wait_for_lsr_thre(up, fifosize);
for (i = 0; i < fifosize && s != end; ++i) {
if (*s == '\n' && !cr_sent) {
@@ -3369,7 +3371,7 @@ static void serial8250_console_fifo_write(struct uart_8250_port *up,
* Allow timeout for each byte written since the caller will only wait
* for UART_LSR_BOTH_EMPTY using the timeout of a single character
*/
- fifo_wait_for_lsr(up, tx_count);
+ serial8250_fifo_wait_for_lsr_thre(up, tx_count);
}
/*
--
2.54.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox