Linux Serial subsystem development
 help / color / mirror / Atom feed
* [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: 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

* 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: [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: 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

* [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: [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

* 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 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] 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: 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 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 net-next 2/3] ppp: unify two channel structs
From: Qingfang Deng @ 2026-05-07  8:59 UTC (permalink / raw)
  To: Sebastian Andrzej Siewior
  Cc: Paolo Abeni, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Jiri Kosina, David Sterba, Greg Kroah-Hartman,
	Jiri Slaby, Chas Williams, Simon Horman, James Chapman, Kees Cook,
	Taegu Ha, Guillaume Nault, Eric Woudstra, Arnd Bergmann,
	Dawid Osuchowski, Breno Leitao, linux-ppp, netdev, linux-kernel,
	linux-serial, linux-atm-general
In-Reply-To: <20260507084645.mpK7rdPn@linutronix.de>

On 2026/5/7 16:46, Sebastian Andrzej Siewior wrote:
> On 2026-05-07 16:33:36 [+0800], Qingfang Deng wrote:
>> On 2026/5/7 15:40, Sebastian Andrzej Siewior wrote:
>>> On 2026-05-07 13:53:30 [+0800], Qingfang Deng wrote:
>>>>> This patch is IMHO a bit too big and should be split. Also this kind of
>>>>> refactor looks very invasive and potentially regression prone. I think
>>>>> it should include a signficant self-test coverage increase.
>>>> This is indeed too big. But how do I split it without breaking the build?
>>> The current ppp tests would yell if you accidentally broke something?
>> By "breaking the build" I meant compile-time errors (due to API changes).
> If this change would flip the logic somewhere and as such break ppp at
> runtime.
> Would the existing test suite be able to catch it?


The current self-test only covers PPP async and PPPoE, and that's why 
Paolo suggests more self-tests.


^ permalink raw reply

* Re: [PATCH net-next 2/3] ppp: unify two channel structs
From: Sebastian Andrzej Siewior @ 2026-05-07  8:46 UTC (permalink / raw)
  To: Qingfang Deng
  Cc: Paolo Abeni, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Jiri Kosina, David Sterba, Greg Kroah-Hartman,
	Jiri Slaby, Chas Williams, Simon Horman, James Chapman, Kees Cook,
	Taegu Ha, Guillaume Nault, Eric Woudstra, Arnd Bergmann,
	Dawid Osuchowski, Breno Leitao, linux-ppp, netdev, linux-kernel,
	linux-serial, linux-atm-general
In-Reply-To: <a4216fa5-9576-4836-b202-d9c35f0e546a@linux.dev>

On 2026-05-07 16:33:36 [+0800], Qingfang Deng wrote:
> On 2026/5/7 15:40, Sebastian Andrzej Siewior wrote:
> > On 2026-05-07 13:53:30 [+0800], Qingfang Deng wrote:
> > > > This patch is IMHO a bit too big and should be split. Also this kind of
> > > > refactor looks very invasive and potentially regression prone. I think
> > > > it should include a signficant self-test coverage increase.
> > > This is indeed too big. But how do I split it without breaking the build?
> > The current ppp tests would yell if you accidentally broke something?
> By "breaking the build" I meant compile-time errors (due to API changes).

If this change would flip the logic somewhere and as such break ppp at
runtime.
Would the existing test suite be able to catch it?

Sebastian

^ permalink raw reply

* Re: [PATCH net-next 2/3] ppp: unify two channel structs
From: Qingfang Deng @ 2026-05-07  8:33 UTC (permalink / raw)
  To: Sebastian Andrzej Siewior
  Cc: Paolo Abeni, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Jiri Kosina, David Sterba, Greg Kroah-Hartman,
	Jiri Slaby, Chas Williams, Simon Horman, James Chapman, Kees Cook,
	Taegu Ha, Guillaume Nault, Eric Woudstra, Arnd Bergmann,
	Dawid Osuchowski, Breno Leitao, linux-ppp, netdev, linux-kernel,
	linux-serial, linux-atm-general
In-Reply-To: <20260507074051.mqO5DaWL@linutronix.de>

On 2026/5/7 15:40, Sebastian Andrzej Siewior wrote:
> On 2026-05-07 13:53:30 [+0800], Qingfang Deng wrote:
>>> This patch is IMHO a bit too big and should be split. Also this kind of
>>> refactor looks very invasive and potentially regression prone. I think
>>> it should include a signficant self-test coverage increase.
>> This is indeed too big. But how do I split it without breaking the build?
> The current ppp tests would yell if you accidentally broke something?
By "breaking the build" I meant compile-time errors (due to API changes).

^ permalink raw reply

* Re: [PATCH net-next 2/3] ppp: unify two channel structs
From: Sebastian Andrzej Siewior @ 2026-05-07  7:40 UTC (permalink / raw)
  To: Qingfang Deng
  Cc: Paolo Abeni, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Jiri Kosina, David Sterba, Greg Kroah-Hartman,
	Jiri Slaby, Mitchell Blank Jr, Chas Williams, Simon Horman,
	James Chapman, Kees Cook, Taegu Ha, Guillaume Nault,
	Eric Woudstra, Arnd Bergmann, Dawid Osuchowski, Breno Leitao,
	linux-ppp, netdev, linux-kernel, linux-serial, linux-atm-general
In-Reply-To: <c9993ee6-4023-4331-a1c1-4e30952146fe@linux.dev>

On 2026-05-07 13:53:30 [+0800], Qingfang Deng wrote:
> > This patch is IMHO a bit too big and should be split. Also this kind of
> > refactor looks very invasive and potentially regression prone. I think
> > it should include a signficant self-test coverage increase.
> This is indeed too big. But how do I split it without breaking the build?

The current ppp tests would yell if you accidentally broke something?

Sebastian

^ permalink raw reply

* Re: [PATCH net-next 2/3] ppp: unify two channel structs
From: Paolo Abeni @ 2026-05-07  7:32 UTC (permalink / raw)
  To: Qingfang Deng, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Jiri Kosina, David Sterba, Greg Kroah-Hartman,
	Jiri Slaby, Mitchell Blank Jr, Chas Williams, Simon Horman,
	James Chapman, Kees Cook, Sebastian Andrzej Siewior, Taegu Ha,
	Guillaume Nault, Eric Woudstra, Arnd Bergmann, Dawid Osuchowski,
	Breno Leitao, linux-ppp, netdev, linux-kernel, linux-serial,
	linux-atm-general
In-Reply-To: <c9993ee6-4023-4331-a1c1-4e30952146fe@linux.dev>

On 5/7/26 7:53 AM, Qingfang Deng wrote:
> On 2026/5/5 19:16, Paolo Abeni wrote:
>> On 4/30/26 11:05 AM, Qingfang Deng wrote:
>>> Historically, PPP maintained two separate structures for a channel:
>>> 'struct channel' was internal to ppp_generic.c, while 'struct ppp_channel'
>>> was the public interface that drivers were required to embed. This
>>> duplication was redundant and forced drivers to manage the lifecycle of
>>> the public structure.
>>>
>>> Unify these two structures into a single 'struct ppp_channel', which is
>>> now internal to ppp_generic.c. Drivers now use a 'ppp_channel_conf'
>>> structure to specify registration parameters and receive an opaque
>>> pointer to the allocated channel.
>>>
>>> Key changes:
>>> - ppp_register_channel() and ppp_register_net_channel() now return
>>>    a 'struct ppp_channel *' instead of taking a pointer to a driver-
>>>    embedded structure.
>>> - 'struct ppp_channel_ops' methods now take the driver's 'private'
>>>    pointer directly as their first argument, simplifying driver logic.
>>> - ppp_unregister_channel() now takes the opaque pointer.
>>> - Multilink-specific fields are unified and handled via the new
>>>    configuration structure.
>>>
>>> This cleanup simplifies the driver interface and makes the channel
>>> lifecycle management more robust by centralizing allocation in the PPP
>>> generic layer.
>>>
>>> Assisted-by: Gemini:gemini-3-flash
>>> Signed-off-by: Qingfang Deng <qingfang.deng@linux.dev>
>>> ---
>>>   drivers/net/ppp/ppp_async.c      |  51 +++++-----
>>>   drivers/net/ppp/ppp_generic.c    | 161 +++++++++++++++----------------
>>>   drivers/net/ppp/ppp_synctty.c    |  51 +++++-----
>>>   drivers/net/ppp/pppoe.c          |  34 ++++---
>>>   drivers/net/ppp/pppox.c          |   4 +-
>>>   drivers/net/ppp/pptp.c           |  40 ++++----
>>>   drivers/tty/ipwireless/network.c |  30 +++---
>>>   include/linux/if_pppox.h         |   2 +-
>>>   include/linux/ppp_channel.h      |  49 ++++++----
>>>   net/atm/pppoatm.c                |  61 ++++++------
>>>   net/l2tp/l2tp_ppp.c              |  34 ++++---
>>>   11 files changed, 271 insertions(+), 246 deletions(-)
>> This patch is IMHO a bit too big and should be split. Also this kind of
>> refactor looks very invasive and potentially regression prone. I think
>> it should include a signficant self-test coverage increase.
> This is indeed too big. But how do I split it without breaking the build?

This is indeed a good question, but I'm really unable to give you a good
answer without allocating to this topic much more time than I have
available.

I think that the (indeed smallish) mtu changes could easily go in a
separate patch.

You could try introducing the struct and/or variables renaming
separately, with no actual functional change, i.e.

- one patch to rename ppp_channel -> ppp_channel_conf
- one patch to rename channel -> ppp_channel
(possibly adjust accordingly the variables name if can done mechanically)

no idea if the end result would be more palatable, but possibly worth a try.

/P


^ permalink raw reply

* Re: [PATCH net-next 2/3] ppp: unify two channel structs
From: Qingfang Deng @ 2026-05-07  5:53 UTC (permalink / raw)
  To: Paolo Abeni, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Jiri Kosina, David Sterba, Greg Kroah-Hartman,
	Jiri Slaby, Mitchell Blank Jr, Chas Williams, Simon Horman,
	James Chapman, Kees Cook, Sebastian Andrzej Siewior, Taegu Ha,
	Guillaume Nault, Eric Woudstra, Arnd Bergmann, Dawid Osuchowski,
	Breno Leitao, linux-ppp, netdev, linux-kernel, linux-serial,
	linux-atm-general
In-Reply-To: <590d7931-02b0-45d6-8f43-ef909c9bde89@redhat.com>

On 2026/5/5 19:16, Paolo Abeni wrote:
> On 4/30/26 11:05 AM, Qingfang Deng wrote:
>> Historically, PPP maintained two separate structures for a channel:
>> 'struct channel' was internal to ppp_generic.c, while 'struct ppp_channel'
>> was the public interface that drivers were required to embed. This
>> duplication was redundant and forced drivers to manage the lifecycle of
>> the public structure.
>>
>> Unify these two structures into a single 'struct ppp_channel', which is
>> now internal to ppp_generic.c. Drivers now use a 'ppp_channel_conf'
>> structure to specify registration parameters and receive an opaque
>> pointer to the allocated channel.
>>
>> Key changes:
>> - ppp_register_channel() and ppp_register_net_channel() now return
>>    a 'struct ppp_channel *' instead of taking a pointer to a driver-
>>    embedded structure.
>> - 'struct ppp_channel_ops' methods now take the driver's 'private'
>>    pointer directly as their first argument, simplifying driver logic.
>> - ppp_unregister_channel() now takes the opaque pointer.
>> - Multilink-specific fields are unified and handled via the new
>>    configuration structure.
>>
>> This cleanup simplifies the driver interface and makes the channel
>> lifecycle management more robust by centralizing allocation in the PPP
>> generic layer.
>>
>> Assisted-by: Gemini:gemini-3-flash
>> Signed-off-by: Qingfang Deng <qingfang.deng@linux.dev>
>> ---
>>   drivers/net/ppp/ppp_async.c      |  51 +++++-----
>>   drivers/net/ppp/ppp_generic.c    | 161 +++++++++++++++----------------
>>   drivers/net/ppp/ppp_synctty.c    |  51 +++++-----
>>   drivers/net/ppp/pppoe.c          |  34 ++++---
>>   drivers/net/ppp/pppox.c          |   4 +-
>>   drivers/net/ppp/pptp.c           |  40 ++++----
>>   drivers/tty/ipwireless/network.c |  30 +++---
>>   include/linux/if_pppox.h         |   2 +-
>>   include/linux/ppp_channel.h      |  49 ++++++----
>>   net/atm/pppoatm.c                |  61 ++++++------
>>   net/l2tp/l2tp_ppp.c              |  34 ++++---
>>   11 files changed, 271 insertions(+), 246 deletions(-)
> This patch is IMHO a bit too big and should be split. Also this kind of
> refactor looks very invasive and potentially regression prone. I think
> it should include a signficant self-test coverage increase.
This is indeed too big. But how do I split it without breaking the build?

^ permalink raw reply

* [PATCH v3 10/10] MIPS: DEC: Ensure RTC platform device deregistration upon failure
From: Maciej W. Rozycki @ 2026-05-06 22:43 UTC (permalink / raw)
  To: Thomas Bogendoerfer, Greg Kroah-Hartman, Jiri Slaby
  Cc: linux-mips, linux-serial, linux-serial
In-Reply-To: <alpine.DEB.2.21.2605062240290.46195@angie.orcam.me.uk>

Switch RTC platform device registration from platform_device_register() 
to platform_add_devices() so as to make sure any failure will result in 
automatic device unregistration.

Fixes: fae67ad43114 ("arch/mips/dec: switch DECstation systems to rtc-cmos")
Signed-off-by: Maciej W. Rozycki <macro@orcam.me.uk>
---
Change from v2,
<https://lore.kernel.org/r/alpine.DEB.2.21.2605012105320.11074@angie.orcam.me.uk/>:

- Fix a minor style issue in the commit description.

No change from v1 (8/8),
<https://lore.kernel.org/r/alpine.DEB.2.21.2604110042130.29980@angie.orcam.me.uk/>.
---
 arch/mips/dec/platform.c |    6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

linux-mips-dec-platform-rtc-unregister.diff
Index: linux-macro/arch/mips/dec/platform.c
===================================================================
--- linux-macro.orig/arch/mips/dec/platform.c
+++ linux-macro/arch/mips/dec/platform.c
@@ -38,6 +38,10 @@ static struct platform_device dec_rtc_de
 	.num_resources = ARRAY_SIZE(dec_rtc_resources),
 };
 
+static struct platform_device *dec_rtc_devices[] __initdata = {
+	&dec_rtc_device,
+};
+
 static struct resource dec_dz_resources[] = {
 	{ .name = "dz", .flags = IORESOURCE_MEM, },
 	{ .name = "dz", .flags = IORESOURCE_IRQ, },
@@ -137,7 +141,7 @@ static int __init dec_add_devices(void)
 	}
 	num_zs = i;
 
-	ret1 = platform_device_register(&dec_rtc_device);
+	ret1 = platform_add_devices(dec_rtc_devices, 1);
 	ret2 = IS_ENABLED(CONFIG_32BIT) ?
 	       platform_add_devices(dec_dz_devices, num_dz) : 0;
 	ret3 = platform_add_devices(dec_zs_devices, num_zs);

^ permalink raw reply

* [PATCH v3 09/10] serial: dz: Enable modular build
From: Maciej W. Rozycki @ 2026-05-06 22:42 UTC (permalink / raw)
  To: Thomas Bogendoerfer, Greg Kroah-Hartman, Jiri Slaby
  Cc: linux-mips, linux-serial, linux-serial
In-Reply-To: <alpine.DEB.2.21.2605062240290.46195@angie.orcam.me.uk>

Enable modular build since the driver now has a proper module_exit() 
handler.  There's nothing specific to DZ hardware to prevent driver 
unloading and reloading from working.

Signed-off-by: Maciej W. Rozycki <macro@orcam.me.uk>
---
No change from v2,
<https://lore.kernel.org/r/alpine.DEB.2.21.2605012103360.11074@angie.orcam.me.uk/>.

No change from v1 (7/8),
<https://lore.kernel.org/r/alpine.DEB.2.21.2604130133470.29980@angie.orcam.me.uk/>.
---
 drivers/tty/serial/Kconfig |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

linux-serial-dz-module.diff
Index: linux-macro/drivers/tty/serial/Kconfig
===================================================================
--- linux-macro.orig/drivers/tty/serial/Kconfig
+++ linux-macro/drivers/tty/serial/Kconfig
@@ -335,7 +335,7 @@ config SERIAL_MAX310X
 	  Say Y here if you want to support this ICs.
 
 config SERIAL_DZ
-	bool "DECstation DZ serial driver"
+	tristate "DECstation DZ serial driver"
 	depends on MACH_DECSTATION && 32BIT
 	select SERIAL_CORE
 	default y

^ permalink raw reply

* [PATCH v3 08/10] serial: zs: Convert to use a platform device
From: Maciej W. Rozycki @ 2026-05-06 22:42 UTC (permalink / raw)
  To: Thomas Bogendoerfer, Greg Kroah-Hartman, Jiri Slaby
  Cc: linux-mips, linux-serial, linux-serial
In-Reply-To: <alpine.DEB.2.21.2605062240290.46195@angie.orcam.me.uk>

Prevent a crash from happening as the first serial port is initialised:

  Console: switching to mono frame buffer device 160x64
  fb0: PMAG-AA frame buffer device at tc0
  DECstation Z85C30 serial driver version 0.10
  CPU 0 Unable to handle kernel paging request at virtual address 0000002c, epc == 803ab00c, ra == 803aafe0
  Oops[#1]:
  CPU: 0 PID: 1 Comm: swapper Not tainted 6.4.0-rc3-00031-g84a9582fd203-dirty #57
  $ 0   : 00000000 10012c00 803aaeb0 00000000
  $ 4   : 80e12f60 80e12f50 80e12f58 81000030
  $ 8   : 00000000 805ff37c 00000000 33433538
  $12   : 65732030 00000006 80c2915d 6c616972
  $16   : 80e12f00 807b7630 00000000 00000000
  $20   : 00000004 00000348 000001a0 807623b8
  $24   : 00000018 00000000                  
  $28   : 80c24000 80c25d60 8078b148 803aafe0
  Hi    : 00000000
  Lo    : 00000000
  epc   : 803ab00c serial_base_ctrl_add+0x78/0xf4
  ra    : 803aafe0 serial_base_ctrl_add+0x4c/0xf4
  Status: 10012c03	KERNEL EXL IE 
  Cause : 00000008 (ExcCode 02)
  BadVA : 0000002c
  PrId  : 00000440 (R4400SC)
  Modules linked in:
  Process swapper (pid: 1, threadinfo=(ptrval), task=(ptrval), tls=00000000)
  Stack : 80760000 00000cc0 00400044 00400040 803aa02c 80d61ab8 00000000 807b7630
          80760000 807623b8 807b7628 803aa644 80386998 00000000 80e17780 80220f68
          80e17780 80d61ab8 80c17d80 80e17780 80e17780 8063c798 80e17780 80383fa0
          00000010 80e17780 00000000 80386998 807a0000 00000000 00400040 8038f848
          807623b8 80d61ab8 00000004 80e17780 00000000 803a68e4 80c25e2c 803bb884
          ...
  Call Trace:
  [<803ab00c>] serial_base_ctrl_add+0x78/0xf4
  [<803aa644>] serial_core_register_port+0x174/0x69c
  [<8077e9ac>] zs_init+0xc8/0xfc
  [<800404d4>] do_one_initcall+0x40/0x2ac
  [<8076cecc>] kernel_init_freeable+0x1e4/0x270
  [<80605bec>] kernel_init+0x20/0x108
  [<800431e8>] ret_from_kernel_thread+0x14/0x1c
  
  Code: 2442aeb0  ae120024  ae0200d0 <8c67002c> 50e00001  8c670000  3c06806e  3c05806e  afb30010 
  
  ---[ end trace 0000000000000000 ]---

(report at the offending commit) -- where a pointer is dereferenced that 
has been derived from a null pointer to the port's parent device.

Since no device is available with legacy probing and it's not anymore a
preferable way to discover devices anyway, switch the driver to using a
platform device and use it as the port's parent device.  Update resource
handling accordingly and only request the actual span of addresses used
within the slot, which will have had its resource already requested by
generic platform device code.

Use platform_driver_probe() not just because SCC devices are fixed with 
solder on board and not straightforward to remove, but foremost because 
the associated TTY's major device number is the same as used by the dz 
driver and the first driver to claim it will prevent the other one from 
using it.  Either one DZ device or some SCC devices will be present in a 
given system but never both at a time, and therefore we want the major 
device number to be claimed by the first driver to actually successfully 
bind to its device and platform_driver_probe() is a way to fulfil that.

An unfortunate consequence of the switch to a platform device is we now
hand the console over from the bootconsole much later in the bootstrap.
The firmware console handler appears good enough though to work so late
and in particular with interrupts enabled.

Since there is one way only remaining to reach zs_reset() now, remove 
the port initialisation marker as no longer needed and go through the 
channel reset unconditionally.

Fixes: 84a9582fd203 ("serial: core: Start managing serial controllers to enable runtime PM")
Signed-off-by: Maciej W. Rozycki <macro@orcam.me.uk>
Cc: stable@vger.kernel.org # needs to use .remove_new for <= 6.10
---
No change from v2,
<https://lore.kernel.org/r/alpine.DEB.2.21.2605012100520.11074@angie.orcam.me.uk/>.

No change from v1 (6/8),
<https://lore.kernel.org/r/alpine.DEB.2.21.2604110034160.29980@angie.orcam.me.uk/>.
---
 arch/mips/dec/platform.c |   60 ++++++++++++++-
 drivers/tty/serial/zs.c  |  184 +++++++++++++++++------------------------------
 drivers/tty/serial/zs.h  |    1 
 3 files changed, 125 insertions(+), 120 deletions(-)

linux-serial-zs-platform.diff
Index: linux-macro/arch/mips/dec/platform.c
===================================================================
--- linux-macro.orig/arch/mips/dec/platform.c
+++ linux-macro/arch/mips/dec/platform.c
@@ -13,6 +13,7 @@
 #include <asm/bootinfo.h>
 
 #include <asm/dec/interrupts.h>
+#include <asm/dec/ioasic_addrs.h>
 #include <asm/dec/kn01.h>
 #include <asm/dec/kn02.h>
 #include <asm/dec/system.h>
@@ -53,10 +54,37 @@ static struct platform_device *dec_dz_de
 	&dec_dz_device,
 };
 
+static struct resource dec_zs_resources[][2] = {
+	{
+		{ .name = "scc0", .flags = IORESOURCE_MEM, },
+		{ .name = "scc0", .flags = IORESOURCE_IRQ, },
+	},
+	{
+		{ .name = "scc1", .flags = IORESOURCE_MEM, },
+		{ .name = "scc1", .flags = IORESOURCE_IRQ, },
+	},
+};
+
+static struct platform_device dec_zs_device[] = {
+	{
+		.name = "zs",
+		.id = 0,
+		.resource = dec_zs_resources[0],
+		.num_resources = ARRAY_SIZE(dec_zs_resources[0]),
+	},
+	{
+		.name = "zs",
+		.id = 1,
+		.resource = dec_zs_resources[1],
+		.num_resources = ARRAY_SIZE(dec_zs_resources[1]),
+	},
+};
+
 static int __init dec_add_devices(void)
 {
-	int ret1, ret2;
-	int num_dz;
+	struct platform_device *dec_zs_devices[ARRAY_SIZE(dec_zs_device)];
+	int ret1, ret2, ret3;
+	int num_dz, num_zs;
 	int irq, i;
 
 	dec_rtc_resources[0].start = RTC_PORT(0);
@@ -84,10 +112,36 @@ static int __init dec_add_devices(void)
 	}
 	num_dz = i;
 
+	i = 0;
+	irq = dec_interrupt[DEC_IRQ_SCC0];
+	if (irq >= 0) {
+		resource_size_t base = dec_kn_slot_base + IOASIC_SCC0;
+
+		dec_zs_device[i].resource[0].start = base;
+		dec_zs_device[i].resource[0].end = base + dec_kn_slot_size - 1;
+		dec_zs_device[i].resource[1].start = irq;
+		dec_zs_device[i].resource[1].end = irq;
+		dec_zs_devices[i] = &dec_zs_device[i];
+		i++;
+	}
+	irq = dec_interrupt[DEC_IRQ_SCC1];
+	if (irq >= 0) {
+		resource_size_t base = dec_kn_slot_base + IOASIC_SCC1;
+
+		dec_zs_device[i].resource[0].start = base;
+		dec_zs_device[i].resource[0].end = base + dec_kn_slot_size - 1;
+		dec_zs_device[i].resource[1].start = irq;
+		dec_zs_device[i].resource[1].end = irq;
+		dec_zs_devices[i] = &dec_zs_device[i];
+		i++;
+	}
+	num_zs = i;
+
 	ret1 = platform_device_register(&dec_rtc_device);
 	ret2 = IS_ENABLED(CONFIG_32BIT) ?
 	       platform_add_devices(dec_dz_devices, num_dz) : 0;
-	return ret1 ? ret1 : ret2;
+	ret3 = platform_add_devices(dec_zs_devices, num_zs);
+	return ret1 ? ret1 : ret2 ? ret2 : ret3;
 }
 
 device_initcall(dec_add_devices);
Index: linux-macro/drivers/tty/serial/zs.c
===================================================================
--- linux-macro.orig/drivers/tty/serial/zs.c
+++ linux-macro/drivers/tty/serial/zs.c
@@ -56,6 +56,7 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/major.h>
+#include <linux/platform_device.h>
 #include <linux/serial.h>
 #include <linux/serial_core.h>
 #include <linux/spinlock.h>
@@ -66,10 +67,6 @@
 
 #include <linux/atomic.h>
 
-#include <asm/dec/interrupts.h>
-#include <asm/dec/ioasic_addrs.h>
-#include <asm/dec/system.h>
-
 #include "zs.h"
 
 
@@ -79,7 +76,7 @@ MODULE_LICENSE("GPL");
 
 
 static char zs_name[] __initdata = "DECstation Z85C30 serial driver version ";
-static char zs_version[] __initdata = "0.10";
+static char zs_version[] __initdata = "0.11";
 
 /*
  * It would be nice to dynamically allocate everything that
@@ -98,12 +95,8 @@ static char zs_version[] __initdata = "0
 
 #define to_zport(uport) container_of(uport, struct zs_port, port)
 
-struct zs_parms {
-	resource_size_t scc[ZS_NUM_SCCS];
-	int irq[ZS_NUM_SCCS];
-};
-
 static struct zs_scc zs_sccs[ZS_NUM_SCCS];
+static struct uart_driver zs_reg;
 
 /*
  * Set parameters in WR5, WR12, WR13 such as not to interfere
@@ -839,16 +832,15 @@ static void zs_reset(struct zs_port *zpo
 
 	spin_lock_irqsave(&scc->zlock, flags);
 	irq = !irqs_disabled_flags(flags);
-	if (!zport->initialised) {
-		/* Reset the pointer first, just in case...  */
-		read_zsreg(zport, R0);
-		/* And let the current transmission finish.  */
-		zs_line_drain(zport, irq);
-		write_zsreg(zport, R9, zport == zport_a ? CHRA : CHRB);
-		udelay(10);
-		write_zsreg(zport, R9, 0);
-		zport->initialised = 1;
-	}
+
+	/* Reset the pointer first, just in case...  */
+	read_zsreg(zport, R0);
+	/* And let the current transmission finish.  */
+	zs_line_drain(zport, irq);
+	write_zsreg(zport, R9, zport == zport_a ? CHRA : CHRB);
+	udelay(10);
+	write_zsreg(zport, R9, 0);
+
 	load_zsregs(zport, zport->regs, irq);
 	spin_unlock_irqrestore(&scc->zlock, flags);
 }
@@ -1055,63 +1047,62 @@ static const struct uart_ops zs_ops = {
 /*
  * Initialize Z85C30 port structures.
  */
-static int __init zs_probe_sccs(void)
+static int __init zs_probe(struct platform_device *pdev)
 {
-	static int probed;
-	struct zs_parms zs_parms;
-	int chip, side, irq;
-	int n_chips = 0;
+	struct resource *mem_resource, *irq_resource;
+	int chip, side;
 	int i;
 
-	if (probed)
-		return 0;
-
-	irq = dec_interrupt[DEC_IRQ_SCC0];
-	if (irq >= 0) {
-		zs_parms.scc[n_chips] = IOASIC_SCC0;
-		zs_parms.irq[n_chips] = dec_interrupt[DEC_IRQ_SCC0];
-		n_chips++;
-	}
-	irq = dec_interrupt[DEC_IRQ_SCC1];
-	if (irq >= 0) {
-		zs_parms.scc[n_chips] = IOASIC_SCC1;
-		zs_parms.irq[n_chips] = dec_interrupt[DEC_IRQ_SCC1];
-		n_chips++;
-	}
-	if (!n_chips)
-		return -ENXIO;
+	mem_resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	irq_resource = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (!mem_resource || !irq_resource)
+		return -ENODEV;
 
-	probed = 1;
+	chip = pdev->id;
+	spin_lock_init(&zs_sccs[chip].zlock);
+	for (side = 0; side < ZS_NUM_CHAN; side++) {
+		struct zs_port *zport = &zs_sccs[chip].zport[side];
+		struct uart_port *uport = &zport->port;
 
-	for (chip = 0; chip < n_chips; chip++) {
-		spin_lock_init(&zs_sccs[chip].zlock);
-		for (side = 0; side < ZS_NUM_CHAN; side++) {
-			struct zs_port *zport = &zs_sccs[chip].zport[side];
-			struct uart_port *uport = &zport->port;
+		zport->scc	= &zs_sccs[chip];
+		zport->clk_mode	= 16;
 
-			zport->scc	= &zs_sccs[chip];
-			zport->clk_mode	= 16;
+		uport->dev	= &pdev->dev;
+		uport->has_sysrq = IS_ENABLED(CONFIG_SERIAL_ZS_CONSOLE);
+		uport->irq	= irq_resource->start;
+		uport->uartclk	= ZS_CLOCK;
+		uport->fifosize	= 1;
+		uport->iotype	= UPIO_MEM;
+		uport->flags	= UPF_BOOT_AUTOCONF;
+		uport->ops	= &zs_ops;
+		uport->line	= chip * ZS_NUM_CHAN + side;
+		uport->mapbase	= mem_resource->start +
+				  (side ^ ZS_CHAN_B) * ZS_CHAN_IO_SIZE;
 
-			uport->has_sysrq = IS_ENABLED(CONFIG_SERIAL_ZS_CONSOLE);
-			uport->irq	= zs_parms.irq[chip];
-			uport->uartclk	= ZS_CLOCK;
-			uport->fifosize	= 1;
-			uport->iotype	= UPIO_MEM;
-			uport->flags	= UPF_BOOT_AUTOCONF;
-			uport->ops	= &zs_ops;
-			uport->line	= chip * ZS_NUM_CHAN + side;
-			uport->mapbase	= dec_kn_slot_base +
-					  zs_parms.scc[chip] +
-					  (side ^ ZS_CHAN_B) * ZS_CHAN_IO_SIZE;
+		for (i = 0; i < ZS_NUM_REGS; i++)
+			zport->regs[i] = zs_init_regs[i];
 
-			for (i = 0; i < ZS_NUM_REGS; i++)
-				zport->regs[i] = zs_init_regs[i];
-		}
+		if (uart_add_one_port(&zs_reg, uport))
+			uport->dev = NULL;
 	}
 
 	return 0;
 }
 
+static void __exit zs_remove(struct platform_device *pdev)
+{
+	int chip, side;
+
+	chip = pdev->id;
+	for (side = ZS_NUM_CHAN - 1; side >= 0; side--) {
+		struct zs_port *zport = &zs_sccs[chip].zport[side];
+		struct uart_port *uport = &zport->port;
+
+		if (uport->dev)
+			uart_remove_one_port(&zs_reg, uport);
+	}
+}
+
 
 #ifdef CONFIG_SERIAL_ZS_CONSOLE
 static void zs_console_putchar(struct uart_port *uport, unsigned char ch)
@@ -1192,20 +1183,14 @@ static int __init zs_console_setup(struc
 	int bits = 8;
 	int parity = 'n';
 	int flow = 'n';
-	int ret;
-
-	ret = zs_map_port(uport);
-	if (ret)
-		return ret;
-
-	zs_reset(zport);
 
+	if (!zport->scc)
+		return -ENODEV;
 	if (options)
 		uart_parse_options(options, &baud, &parity, &bits, &flow);
 	return uart_set_options(uport, co, baud, parity, bits, flow);
 }
 
-static struct uart_driver zs_reg;
 static struct console zs_console = {
 	.name	= "ttyS",
 	.write	= zs_console_write,
@@ -1216,23 +1201,6 @@ static struct console zs_console = {
 	.data	= &zs_reg,
 };
 
-/*
- *	Register console.
- */
-static int __init zs_serial_console_init(void)
-{
-	int ret;
-
-	ret = zs_probe_sccs();
-	if (ret)
-		return ret;
-	register_console(&zs_console);
-
-	return 0;
-}
-
-console_initcall(zs_serial_console_init);
-
 #define SERIAL_ZS_CONSOLE	&zs_console
 #else
 #define SERIAL_ZS_CONSOLE	NULL
@@ -1248,47 +1216,31 @@ static struct uart_driver zs_reg = {
 	.cons			= SERIAL_ZS_CONSOLE,
 };
 
+static struct platform_driver zs_driver = {
+	.remove = __exit_p(zs_remove),
+	.driver = { .name = "zs" },
+};
+
 /* zs_init inits the driver. */
 static int __init zs_init(void)
 {
-	int i, ret;
+	int ret;
 
 	pr_info("%s%s\n", zs_name, zs_version);
 
-	/* Find out how many Z85C30 SCCs we have.  */
-	ret = zs_probe_sccs();
-	if (ret)
-		return ret;
-
 	ret = uart_register_driver(&zs_reg);
 	if (ret)
 		return ret;
+	ret = platform_driver_probe(&zs_driver, zs_probe);
+	if (ret)
+		uart_unregister_driver(&zs_reg);
 
-	for (i = 0; i < ZS_NUM_SCCS * ZS_NUM_CHAN; i++) {
-		struct zs_scc *scc = &zs_sccs[i / ZS_NUM_CHAN];
-		struct zs_port *zport = &scc->zport[i % ZS_NUM_CHAN];
-		struct uart_port *uport = &zport->port;
-
-		if (zport->scc)
-			uart_add_one_port(&zs_reg, uport);
-	}
-
-	return 0;
+	return ret;
 }
 
 static void __exit zs_exit(void)
 {
-	int i;
-
-	for (i = ZS_NUM_SCCS * ZS_NUM_CHAN - 1; i >= 0; i--) {
-		struct zs_scc *scc = &zs_sccs[i / ZS_NUM_CHAN];
-		struct zs_port *zport = &scc->zport[i % ZS_NUM_CHAN];
-		struct uart_port *uport = &zport->port;
-
-		if (zport->scc)
-			uart_remove_one_port(&zs_reg, uport);
-	}
-
+	platform_driver_unregister(&zs_driver);
 	uart_unregister_driver(&zs_reg);
 }
 
Index: linux-macro/drivers/tty/serial/zs.h
===================================================================
--- linux-macro.orig/drivers/tty/serial/zs.h
+++ linux-macro/drivers/tty/serial/zs.h
@@ -22,7 +22,6 @@
 struct zs_port {
 	struct zs_scc	*scc;			/* Containing SCC.  */
 	struct uart_port port;			/* Underlying UART.  */
-	int		initialised;		/* For the console port.  */
 
 	int		clk_mode;		/* May be 1, 16, 32, or 64.  */
 

^ permalink raw reply

* [PATCH v3 07/10] serial: dz: Convert to use a platform device
From: Maciej W. Rozycki @ 2026-05-06 22:42 UTC (permalink / raw)
  To: Thomas Bogendoerfer, Greg Kroah-Hartman, Jiri Slaby
  Cc: linux-mips, linux-serial, linux-serial
In-Reply-To: <alpine.DEB.2.21.2605062240290.46195@angie.orcam.me.uk>

Prevent a crash from happening as the first serial port is initialised:

  Console: switching to colour frame buffer device 160x64
  tgafb: SFB+ detected, rev=0x02
  fb0: Digital ZLX-E1 frame buffer device at 0x1e000000
  DECstation DZ serial driver version 1.04
  CPU 0 Unable to handle kernel paging request at virtual address 000000bc, epc == 8048b3a4, ra == 80470a78
  Oops[#1]:
  CPU: 0 UID: 0 PID: 1 Comm: swapper/0 Not tainted 6.19.0-dirty #35 NONE 
  $ 0   : 00000000 1000ac00 00000004 804707ac
  $ 4   : 00000000 80e20850 80e20858 81000030
  $ 8   : 00000000 8072c81c 00000008 fefefeff
  $12   : 6c616972 00000006 80c5917f 69726420
  $16   : 80e20800 00000000 808f8968 80e20800
  $20   : 00000000 807f5a90 808b0094 808d3bc8
  $24   : 00000018 80479030                  
  $28   : 80c2e000 80c2fd70 00000069 80470a78
  Hi    : 00000004
  Lo    : 00000000
  epc   : 8048b3a4 __dev_fwnode+0x0/0xc
  ra    : 80470a78 serial_base_ctrl_add+0xa0/0x168
  Status: 1000ac04	IEp 
  Cause : 30000008 (ExcCode 02)
  BadVA : 000000bc
  PrId  : 00000220 (R3000)
  Modules linked in:
  Process swapper/0 (pid: 1, threadinfo=(ptrval), task=(ptrval), tls=00000000)
  Stack : 00400044 00400040 8046f4cc 00000000 808a6148 808a0000 808f8968 8086983c
          808e0000 8046fc84 1000ac01 00000028 80e20700 802ba3f8 80e20700 80d34a94
          80c1b900 80e20700 80e20700 80e20700 80e20700 80444650 00000000 00000000
          00000000 807f5a90 808b0094 80447080 00400040 808e0000 80d34a94 808a6148
          80d34a94 00000004 80e20700 00000000 8076974c 80469810 80c2fe3c 1000ac01
          ...
  Call Trace:
  [<8048b3a4>] __dev_fwnode+0x0/0xc
  [<80470a78>] serial_base_ctrl_add+0xa0/0x168
  [<8046fc84>] serial_core_register_port+0x1c8/0x974
  [<808c6af0>] dz_init+0x74/0xc8
  [<800470e0>] do_one_initcall+0x44/0x2d4
  [<808b111c>] kernel_init_freeable+0x258/0x308
  [<8072e434>] kernel_init+0x20/0x114
  [<80049cd0>] ret_from_kernel_thread+0x14/0x1c
  
  Code: 27bd0018  03e00008  2402ffea <8c8200bc> 03e00008  00000000  27bdffc0  afbe0038  afb30024 
  
  ---[ end trace 0000000000000000 ]---

-- where a pointer is dereferenced that has been derived from a null 
pointer to the port's parent device.

Since no device is available with legacy probing and it's not anymore a 
preferable way to discover devices anyway, switch the driver to using a 
platform device and use it as the port's parent device.  Update resource 
handling accordingly and only request the actual span of addresses used 
within the slot, which will have had its resource already requested by 
generic platform device code.

Use platform_driver_probe() not just because the DZ device is fixed with 
solder on board and not straightforward to remove, but foremost because 
the associated TTY's major device number is the same as used by the zs 
driver and the first driver to claim it will prevent the other one from 
using it.  Either one DZ device or some SCC devices will be present in a 
given system but never both at a time, and therefore we want the major 
device number to be claimed by the first driver to actually successfully 
bind to its device and platform_driver_probe() is a way to fulfil that.

An unfortunate consequence of the switch to a platform device is we now 
hand the console over from the bootconsole much later in the bootstrap.  
The firmware console handler appears good enough though to work so late 
and in particular with interrupts enabled.

Conversely only starting the console port so late lets the reset code 
fully utilise our delay handlers, so switch from udelay() to fsleep() 
for transmitter draining so as to avoid busy-waiting for an excessive 
amount of time.

Fixes: 84a9582fd203 ("serial: core: Start managing serial controllers to enable runtime PM")
Signed-off-by: Maciej W. Rozycki <macro@orcam.me.uk>
Cc: stable@vger.kernel.org # needs to use .remove_new for <= 6.10
---
No change from v2,
<https://lore.kernel.org/r/alpine.DEB.2.21.2605012043570.11074@angie.orcam.me.uk/>.

No change from v1 (5/8),
<https://lore.kernel.org/r/alpine.DEB.2.21.2604102351280.29980@angie.orcam.me.uk/>.
---
 arch/mips/dec/platform.c |   55 +++++++++++++++++++++-
 drivers/tty/serial/dz.c  |  116 ++++++++++++++++++++++-------------------------
 2 files changed, 110 insertions(+), 61 deletions(-)

linux-serial-dz-platform.diff
Index: linux-macro/arch/mips/dec/platform.c
===================================================================
--- linux-macro.orig/arch/mips/dec/platform.c
+++ linux-macro/arch/mips/dec/platform.c
@@ -10,6 +10,13 @@
 #include <linux/mc146818rtc.h>
 #include <linux/platform_device.h>
 
+#include <asm/bootinfo.h>
+
+#include <asm/dec/interrupts.h>
+#include <asm/dec/kn01.h>
+#include <asm/dec/kn02.h>
+#include <asm/dec/system.h>
+
 static struct resource dec_rtc_resources[] = {
 	{
 		.name = "rtc",
@@ -30,11 +37,57 @@ static struct platform_device dec_rtc_de
 	.num_resources = ARRAY_SIZE(dec_rtc_resources),
 };
 
+static struct resource dec_dz_resources[] = {
+	{ .name = "dz", .flags = IORESOURCE_MEM, },
+	{ .name = "dz", .flags = IORESOURCE_IRQ, },
+};
+
+static struct platform_device dec_dz_device = {
+	.name = "dz",
+	.id = PLATFORM_DEVID_NONE,
+	.resource = dec_dz_resources,
+	.num_resources = ARRAY_SIZE(dec_dz_resources),
+};
+
+static struct platform_device *dec_dz_devices[] __initdata = {
+	&dec_dz_device,
+};
+
 static int __init dec_add_devices(void)
 {
+	int ret1, ret2;
+	int num_dz;
+	int irq, i;
+
 	dec_rtc_resources[0].start = RTC_PORT(0);
 	dec_rtc_resources[0].end = RTC_PORT(0) + dec_kn_slot_size - 1;
-	return platform_device_register(&dec_rtc_device);
+
+	i = 0;
+	irq = dec_interrupt[DEC_IRQ_DZ11];
+	if (IS_ENABLED(CONFIG_32BIT) && irq >= 0) {
+		resource_size_t base;
+
+		switch (mips_machtype) {
+		case MACH_DS23100:
+		case MACH_DS5100:
+			base = dec_kn_slot_base + KN01_DZ11;
+			break;
+		default:
+			base = dec_kn_slot_base + KN02_DZ11;
+			break;
+		}
+		dec_dz_device.resource[0].start = base;
+		dec_dz_device.resource[0].end = base + dec_kn_slot_size - 1;
+		dec_dz_device.resource[1].start = irq;
+		dec_dz_device.resource[1].end = irq;
+		i++;
+	}
+	num_dz = i;
+
+	ret1 = platform_device_register(&dec_rtc_device);
+	ret2 = IS_ENABLED(CONFIG_32BIT) ?
+	       platform_add_devices(dec_dz_devices, num_dz) : 0;
+	return ret1 ? ret1 : ret2;
 }
 
 device_initcall(dec_add_devices);
Index: linux-macro/drivers/tty/serial/dz.c
===================================================================
--- linux-macro.orig/drivers/tty/serial/dz.c
+++ linux-macro/drivers/tty/serial/dz.c
@@ -40,6 +40,7 @@
 #include <linux/kernel.h>
 #include <linux/major.h>
 #include <linux/module.h>
+#include <linux/platform_device.h>
 #include <linux/serial.h>
 #include <linux/serial_core.h>
 #include <linux/sysrq.h>
@@ -48,14 +49,6 @@
 
 #include <linux/atomic.h>
 #include <linux/io.h>
-#include <asm/bootinfo.h>
-
-#include <asm/dec/interrupts.h>
-#include <asm/dec/kn01.h>
-#include <asm/dec/kn02.h>
-#include <asm/dec/machtype.h>
-#include <asm/dec/prom.h>
-#include <asm/dec/system.h>
 
 #include "dz.h"
 
@@ -65,7 +58,9 @@ MODULE_LICENSE("GPL");
 
 
 static char dz_name[] __initdata = "DECstation DZ serial driver version ";
-static char dz_version[] __initdata = "1.04";
+static char dz_version[] __initdata = "1.05";
+
+#define DZ_IO_SIZE 0x20			/* IOMEM space size.  */
 
 struct dz_port {
 	struct dz_mux		*mux;
@@ -81,6 +76,7 @@ struct dz_mux {
 };
 
 static struct dz_mux dz_mux;
+static struct uart_driver dz_reg;
 
 static inline struct dz_port *to_dport(struct uart_port *uport)
 {
@@ -564,7 +560,7 @@ static void dz_reset(struct dz_port *dpo
 			iob();
 			udelay(2);		/* 1.4us TRDY recovery.  */
 		}
-		udelay(1200);			/* Transmitter drain.  */
+		fsleep(1200);			/* Transmitter drain.  */
 	}
 
 	dz_out(dport, DZ_CSR, DZ_CLR);
@@ -681,14 +677,13 @@ static void dz_release_port(struct uart_
 
 	map_guard = atomic_add_return(-1, &mux->map_guard);
 	if (!map_guard)
-		release_mem_region(uport->mapbase, dec_kn_slot_size);
+		release_mem_region(uport->mapbase, DZ_IO_SIZE);
 }
 
 static int dz_map_port(struct uart_port *uport)
 {
 	if (!uport->membase)
-		uport->membase = ioremap(uport->mapbase,
-						 dec_kn_slot_size);
+		uport->membase = ioremap(uport->mapbase, DZ_IO_SIZE);
 	if (!uport->membase) {
 		printk(KERN_ERR "dz: Cannot map MMIO\n");
 		return -ENOMEM;
@@ -704,8 +699,7 @@ static int dz_request_port(struct uart_p
 
 	map_guard = atomic_add_return(1, &mux->map_guard);
 	if (map_guard == 1) {
-		if (!request_mem_region(uport->mapbase, dec_kn_slot_size,
-					"dz")) {
+		if (!request_mem_region(uport->mapbase, DZ_IO_SIZE, "dz")) {
 			atomic_add(-1, &mux->map_guard);
 			printk(KERN_ERR
 			       "dz: Unable to reserve MMIO resource\n");
@@ -716,7 +710,7 @@ static int dz_request_port(struct uart_p
 	if (ret) {
 		map_guard = atomic_add_return(-1, &mux->map_guard);
 		if (!map_guard)
-			release_mem_region(uport->mapbase, dec_kn_slot_size);
+			release_mem_region(uport->mapbase, DZ_IO_SIZE);
 		return ret;
 	}
 	return 0;
@@ -768,20 +762,15 @@ static const struct uart_ops dz_ops = {
 	.verify_port	= dz_verify_port,
 };
 
-static void __init dz_init_ports(void)
+static int __init dz_probe(struct platform_device *pdev)
 {
-	static int first = 1;
-	unsigned long base;
+	struct resource *mem_resource, *irq_resource;
 	int line;
 
-	if (!first)
-		return;
-	first = 0;
-
-	if (mips_machtype == MACH_DS23100 || mips_machtype == MACH_DS5100)
-		base = dec_kn_slot_base + KN01_DZ11;
-	else
-		base = dec_kn_slot_base + KN02_DZ11;
+	mem_resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	irq_resource = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (!mem_resource || !irq_resource)
+		return -ENODEV;
 
 	for (line = 0; line < DZ_NB_PORT; line++) {
 		struct dz_port *dport = &dz_mux.dport[line];
@@ -789,14 +778,33 @@ static void __init dz_init_ports(void)
 
 		dport->mux	= &dz_mux;
 
-		uport->irq	= dec_interrupt[DEC_IRQ_DZ11];
+		uport->dev	= &pdev->dev;
+		uport->irq	= irq_resource->start;
 		uport->fifosize	= 1;
 		uport->iotype	= UPIO_MEM;
 		uport->flags	= UPF_BOOT_AUTOCONF;
 		uport->ops	= &dz_ops;
 		uport->line	= line;
-		uport->mapbase	= base;
+		uport->mapbase	= mem_resource->start;
 		uport->has_sysrq = IS_ENABLED(CONFIG_SERIAL_DZ_CONSOLE);
+
+		if (uart_add_one_port(&dz_reg, uport))
+			uport->dev = NULL;
+	}
+
+	return 0;
+}
+
+static void __exit dz_remove(struct platform_device *pdev)
+{
+	int line;
+
+	for (line = DZ_NB_PORT - 1; line >= 0; line--) {
+		struct dz_port *dport = &dz_mux.dport[line];
+		struct uart_port *uport = &dport->port;
+
+		if (uport->dev)
+			uart_remove_one_port(&dz_reg, uport);
 	}
 }
 
@@ -879,21 +887,14 @@ static int __init dz_console_setup(struc
 	int bits = 8;
 	int parity = 'n';
 	int flow = 'n';
-	int ret;
-
-	ret = dz_map_port(uport);
-	if (ret)
-		return ret;
-
-	dz_reset(dport);
 
+	if (!dport->mux)
+		return -ENODEV;
 	if (options)
 		uart_parse_options(options, &baud, &parity, &bits, &flow);
-
-	return uart_set_options(&dport->port, co, baud, parity, bits, flow);
+	return uart_set_options(uport, co, baud, parity, bits, flow);
 }
 
-static struct uart_driver dz_reg;
 static struct console dz_console = {
 	.name	= "ttyS",
 	.write	= dz_console_print,
@@ -904,18 +905,6 @@ static struct console dz_console = {
 	.data	= &dz_reg,
 };
 
-static int __init dz_serial_console_init(void)
-{
-	if (!IOASIC) {
-		dz_init_ports();
-		register_console(&dz_console);
-		return 0;
-	} else
-		return -ENXIO;
-}
-
-console_initcall(dz_serial_console_init);
-
 #define SERIAL_DZ_CONSOLE	&dz_console
 #else
 #define SERIAL_DZ_CONSOLE	NULL
@@ -931,25 +920,32 @@ static struct uart_driver dz_reg = {
 	.cons			= SERIAL_DZ_CONSOLE,
 };
 
+static struct platform_driver dz_driver = {
+	.remove = __exit_p(dz_remove),
+	.driver = { .name = "dz" },
+};
+
 static int __init dz_init(void)
 {
-	int ret, i;
-
-	if (IOASIC)
-		return -ENXIO;
+	int ret;
 
 	printk("%s%s\n", dz_name, dz_version);
 
-	dz_init_ports();
-
 	ret = uart_register_driver(&dz_reg);
 	if (ret)
 		return ret;
+	ret = platform_driver_probe(&dz_driver, dz_probe);
+	if (ret)
+		uart_unregister_driver(&dz_reg);
 
-	for (i = 0; i < DZ_NB_PORT; i++)
-		uart_add_one_port(&dz_reg, &dz_mux.dport[i].port);
+	return ret;
+}
 
-	return 0;
+static void __exit dz_exit(void)
+{
+	platform_driver_unregister(&dz_driver);
+	uart_unregister_driver(&dz_reg);
 }
 
 module_init(dz_init);
+module_exit(dz_exit);

^ permalink raw reply

* [PATCH v3 06/10] serial: zs: Switch to using channel reset
From: Maciej W. Rozycki @ 2026-05-06 22:42 UTC (permalink / raw)
  To: Thomas Bogendoerfer, Greg Kroah-Hartman, Jiri Slaby
  Cc: linux-mips, linux-serial, linux-serial
In-Reply-To: <alpine.DEB.2.21.2605062240290.46195@angie.orcam.me.uk>

Switch the driver to using the channel reset rather than hardware reset, 
simplifying handling by removing an interference between channels that 
causes the other channel to become uninitialised afterwards.

There is little difference between the two kinds of reset in terms of 
register settings that result, and we initialise the whole register set 
right away anyway.  However this prevents a hang from happening should 
the console output handler in the firmware try to access the other port 
whose transmitter has been disabled and line parameters messed up.

For example this will happen if the keyboard port (port A) is chosen for 
the system console, unusually but not insanely for a headless system, as 
the port is wired to a standard DA-15 connector and an adapter can be 
easily made.  Or with the next change in place this would happen for the 
regular console port (port B), since the keyboard port (port A) will be 
initialised first.

Just remove the unnecessary complication then, a channel reset is good 
enough.  We still need the initialisation marker, now per channel rather 
than per SCC, as for the console port zs_reset() will be called twice: 
once early on via zs_serial_console_init() for the console setup only, 
and then again via zs_config_port() as the port is associated with a TTY 
device.

Fixes: 8b4a40809e53 ("zs: move to the serial subsystem")
Signed-off-by: Maciej W. Rozycki <macro@orcam.me.uk>
Cc: stable@vger.kernel.org # v2.6.23+
---
No change from v2,
<https://lore.kernel.org/r/alpine.DEB.2.21.2605012041220.11074@angie.orcam.me.uk/>.

No change from v1 (4/8),
<https://lore.kernel.org/r/alpine.DEB.2.21.2604122134330.29980@angie.orcam.me.uk/>.
---
 drivers/tty/serial/zs.c |    7 ++++---
 drivers/tty/serial/zs.h |    2 +-
 2 files changed, 5 insertions(+), 4 deletions(-)

linux-serial-zs-reset-channel.diff
Index: linux-macro/drivers/tty/serial/zs.c
===================================================================
--- linux-macro.orig/drivers/tty/serial/zs.c
+++ linux-macro/drivers/tty/serial/zs.c
@@ -832,21 +832,22 @@ static void zs_shutdown(struct uart_port
 
 static void zs_reset(struct zs_port *zport)
 {
+	struct zs_port *zport_a = &zport->scc->zport[ZS_CHAN_A];
 	struct zs_scc *scc = zport->scc;
 	int irq;
 	unsigned long flags;
 
 	spin_lock_irqsave(&scc->zlock, flags);
 	irq = !irqs_disabled_flags(flags);
-	if (!scc->initialised) {
+	if (!zport->initialised) {
 		/* Reset the pointer first, just in case...  */
 		read_zsreg(zport, R0);
 		/* And let the current transmission finish.  */
 		zs_line_drain(zport, irq);
-		write_zsreg(zport, R9, FHWRES);
+		write_zsreg(zport, R9, zport == zport_a ? CHRA : CHRB);
 		udelay(10);
 		write_zsreg(zport, R9, 0);
-		scc->initialised = 1;
+		zport->initialised = 1;
 	}
 	load_zsregs(zport, zport->regs, irq);
 	spin_unlock_irqrestore(&scc->zlock, flags);
Index: linux-macro/drivers/tty/serial/zs.h
===================================================================
--- linux-macro.orig/drivers/tty/serial/zs.h
+++ linux-macro/drivers/tty/serial/zs.h
@@ -22,6 +22,7 @@
 struct zs_port {
 	struct zs_scc	*scc;			/* Containing SCC.  */
 	struct uart_port port;			/* Underlying UART.  */
+	int		initialised;		/* For the console port.  */
 
 	int		clk_mode;		/* May be 1, 16, 32, or 64.  */
 
@@ -41,7 +42,6 @@ struct zs_scc {
 	struct zs_port	zport[2];
 	spinlock_t	zlock;
 	atomic_t	irq_guard;
-	int		initialised;
 };
 
 #endif /* __KERNEL__ */

^ permalink raw reply

* [PATCH v3 05/10] serial: zs: Fix bootconsole handover lockup
From: Maciej W. Rozycki @ 2026-05-06 22:42 UTC (permalink / raw)
  To: Thomas Bogendoerfer, Greg Kroah-Hartman, Jiri Slaby
  Cc: linux-mips, linux-serial, linux-serial
In-Reply-To: <alpine.DEB.2.21.2605062240290.46195@angie.orcam.me.uk>

Calling zs_reset() in the course of setting up the serial device causes 
line parameters to be reset and the transmitter disabled.  We've been 
lucky in that no message is usually produced to the kernel log between 
this call and the later call to uart_set_options() in the course of 
console setup done by zs_serial_console_init(), or the system would hang 
as the console output handler in the firmware tried to access a port the
transmitter of which has been disabled and line parameters messed up.

This will change with the next change to the driver, so fix zs_reset() 
such that line parameters are set for 9600n8 console operation as with 
the system firmware and the transmitter re-enabled after reset.  This 
also means zs_pm() serves no purpose anymore, so drop it.

Fixes: 8b4a40809e53 ("zs: move to the serial subsystem")
Signed-off-by: Maciej W. Rozycki <macro@orcam.me.uk>
Cc: stable@vger.kernel.org # v2.6.23+
---
Changes from v2,
<https://lore.kernel.org/r/alpine.DEB.2.21.2605012016460.11074@angie.orcam.me.uk/>:

- Make a minor style improvement to the commit description.

- Drop a leftover paragraph in the commit description (doh!).

No change from v1 (3/8),
<https://lore.kernel.org/r/alpine.DEB.2.21.2604102346290.29980@angie.orcam.me.uk/>.
---
 drivers/tty/serial/zs.c |   29 ++++++++---------------------
 1 file changed, 8 insertions(+), 21 deletions(-)

linux-serial-zs-prom-console.diff
Index: linux-macro/drivers/tty/serial/zs.c
===================================================================
--- linux-macro.orig/drivers/tty/serial/zs.c
+++ linux-macro/drivers/tty/serial/zs.c
@@ -105,18 +105,24 @@ struct zs_parms {
 
 static struct zs_scc zs_sccs[ZS_NUM_SCCS];
 
+/*
+ * Set parameters in WR5, WR12, WR13 such as not to interfere
+ * with the initial PROM-based console.  Otherwise any output
+ * produced before the console handover would cause the system
+ * firmware to hang (TxENAB) or produce rubbish (Tx8, B9600).
+ */
 static u8 zs_init_regs[ZS_NUM_REGS] __initdata = {
 	0,				/* write 0 */
 	PAR_SPEC,			/* write 1 */
 	0,				/* write 2 */
 	0,				/* write 3 */
 	X16CLK | SB1,			/* write 4 */
-	0,				/* write 5 */
+	Tx8 | TxENAB,			/* write 5 */
 	0, 0, 0,			/* write 6, 7, 8 */
 	MIE | DLC | NV,			/* write 9 */
 	NRZ,				/* write 10 */
 	TCBR | RCBR,			/* write 11 */
-	0, 0,				/* BRG time constant, write 12 + 13 */
+	0x16, 0x00,			/* BRG time constant, write 12 + 13 */
 	BRSRC | BRENABL,		/* write 14 */
 	0,				/* write 15 */
 };
@@ -956,23 +962,6 @@ static void zs_set_termios(struct uart_p
 	spin_unlock_irqrestore(&scc->zlock, flags);
 }
 
-/*
- * Hack alert!
- * Required solely so that the initial PROM-based console
- * works undisturbed in parallel with this one.
- */
-static void zs_pm(struct uart_port *uport, unsigned int state,
-		  unsigned int oldstate)
-{
-	struct zs_port *zport = to_zport(uport);
-
-	if (state < 3)
-		zport->regs[5] |= TxENAB;
-	else
-		zport->regs[5] &= ~TxENAB;
-	write_zsreg(zport, R5, zport->regs[5]);
-}
-
 
 static const char *zs_type(struct uart_port *uport)
 {
@@ -1055,7 +1044,6 @@ static const struct uart_ops zs_ops = {
 	.startup	= zs_startup,
 	.shutdown	= zs_shutdown,
 	.set_termios	= zs_set_termios,
-	.pm		= zs_pm,
 	.type		= zs_type,
 	.release_port	= zs_release_port,
 	.request_port	= zs_request_port,
@@ -1210,7 +1198,6 @@ static int __init zs_console_setup(struc
 		return ret;
 
 	zs_reset(zport);
-	zs_pm(uport, 0, -1);
 
 	if (options)
 		uart_parse_options(options, &baud, &parity, &bits, &flow);

^ permalink raw reply

* [PATCH v3 04/10] serial: dz: Fix bootconsole handover lockup
From: Maciej W. Rozycki @ 2026-05-06 22:42 UTC (permalink / raw)
  To: Thomas Bogendoerfer, Greg Kroah-Hartman, Jiri Slaby
  Cc: linux-mips, linux-serial, linux-serial
In-Reply-To: <alpine.DEB.2.21.2605062240290.46195@angie.orcam.me.uk>

Calling dz_reset() in the course of setting up the serial device causes 
line parameters to be reset and the transmitter disabled.  We've been 
lucky in that no message is usually produced to the kernel log between 
this call and the later call to uart_set_options() in the course of 
console setup done by dz_serial_console_init(), or the system would hang 
as the console output handler in the firmware tried to access a port the 
transmitter of which has been disabled and line parameters messed up.

This will change with the next change to the driver, so fix dz_reset() 
such that line parameters are set for 9600n8 console operation as with 
the system firmware and the transmitter re-enabled after reset.  This 
also means dz_pm() serves no purpose anymore, so drop it.

Fixes: e6ee512f5a77 ("dz.c: Resource management")
Signed-off-by: Maciej W. Rozycki <macro@orcam.me.uk>
Cc: stable@vger.kernel.org # v2.6.25+
---
Changes from v2,
<https://lore.kernel.org/r/alpine.DEB.2.21.2605012013410.11074@angie.orcam.me.uk/>:

- Make a minor style improvement to the commit description.

No change from v1 (2/8),
<https://lore.kernel.org/r/alpine.DEB.2.21.2604102338300.29980@angie.orcam.me.uk/>.
---
 drivers/tty/serial/dz.c |   36 ++++++++++++------------------------
 1 file changed, 12 insertions(+), 24 deletions(-)

linux-serial-dz-prom-console.diff
Index: linux-macro/drivers/tty/serial/dz.c
===================================================================
--- linux-macro.orig/drivers/tty/serial/dz.c
+++ linux-macro/drivers/tty/serial/dz.c
@@ -571,6 +571,18 @@ static void dz_reset(struct dz_port *dpo
 	while (dz_in(dport, DZ_CSR) & DZ_CLR);
 	iob();
 
+	/*
+	 * Set parameters across all lines such as not to interfere
+	 * with the initial PROM-based console.  Otherwise any output
+	 * produced before the console handover would cause the system
+	 * firmware to produce rubbish.
+	 */
+	for (int line = 0; line < DZ_NB_PORT; line++)
+		dz_out(dport, DZ_LPR, DZ_B9600 | DZ_CS8 | line);
+
+	/* Re-enable transmission for the initial PROM-based console.  */
+	dz_out(dport, DZ_TCR, tcr);
+
 	/* Enable scanning.  */
 	dz_out(dport, DZ_CSR, DZ_MSE);
 
@@ -654,26 +666,6 @@ static void dz_set_termios(struct uart_p
 	uart_port_unlock_irqrestore(&dport->port, flags);
 }
 
-/*
- * Hack alert!
- * Required solely so that the initial PROM-based console
- * works undisturbed in parallel with this one.
- */
-static void dz_pm(struct uart_port *uport, unsigned int state,
-		  unsigned int oldstate)
-{
-	struct dz_port *dport = to_dport(uport);
-	unsigned long flags;
-
-	uart_port_lock_irqsave(&dport->port, &flags);
-	if (state < 3)
-		dz_start_tx(&dport->port);
-	else
-		dz_stop_tx(&dport->port);
-	uart_port_unlock_irqrestore(&dport->port, flags);
-}
-
-
 static const char *dz_type(struct uart_port *uport)
 {
 	return "DZ";
@@ -769,7 +761,6 @@ static const struct uart_ops dz_ops = {
 	.startup	= dz_startup,
 	.shutdown	= dz_shutdown,
 	.set_termios	= dz_set_termios,
-	.pm		= dz_pm,
 	.type		= dz_type,
 	.release_port	= dz_release_port,
 	.request_port	= dz_request_port,
@@ -894,10 +885,7 @@ static int __init dz_console_setup(struc
 	if (ret)
 		return ret;
 
-	spin_lock_init(&dport->port.lock);	/* For dz_pm().  */
-
 	dz_reset(dport);
-	dz_pm(uport, 0, -1);
 
 	if (options)
 		uart_parse_options(options, &baud, &parity, &bits, &flow);

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox