Linux Serial subsystem development
 help / color / mirror / Atom feed
* [PATCH v2] serial: 8250: Fix null ptr deref has_acpi_companion
From: Filip Jensen @ 2026-03-09 14:28 UTC (permalink / raw)
  To: gregkh, jirislaby, ilpo.jarvinen
  Cc: linux-kernel, linux-serial, Filip Jensen,
	Jose Javier Rodriguez Barbarin

uart->port.dev will have a value in serial8250_register_8250_port if the
port number is under the initial CONFIG_SERIAL_8250_RUNTIME_UARTS. If
unset, it will depend upon up->port.dev being set by the particular serial
driver. A faulty driver might not set this value and this leads to null
pointer dereferenced later in has_acpi_companion and later at
uart_add_one_port call:

Oops: general protection fault, probably for non-canonical address
KASAN: null-ptr-deref in range
RIP: 0010:serial8250_register_8250_port+0xd34/0x2030
drivers/tty/serial/8250/8250_core.c

Reviewed-by: Jose Javier Rodriguez Barbarin <dev-josejavier.rodriguez@duagon.com>
Signed-off-by: Filip Jensen <dev-Felipe.Jensen@duagon.com>
---

Thank you very much for the revision, Jiri. As suggested, I have removed
people from the recipients list that have not made relevant changes related
to this patch recently.

V1 -> V2: Fixed that the return value was not setting an error value.
Rewriten the subject to make clear that this is a fix to a posible null
pointer deref and slightly rewriten the description for the changelog.

V1: https://lore.kernel.org/linux-serial/20260305163358.42599-1-dev-Felipe.Jensen@duagon.com/

For a case of a low level driver not setting this, cf.
Link: https://lore.kernel.org/linux-serial/20260305162815.41818-1-dev-Felipe.Jensen@duagon.com/

 drivers/tty/serial/8250/8250_core.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
index d2e2c5dfef99..d758f871fbbd 100644
--- a/drivers/tty/serial/8250/8250_core.c
+++ b/drivers/tty/serial/8250/8250_core.c
@@ -756,6 +756,8 @@ int serial8250_register_8250_port(const struct uart_8250_port *up)
 		if (ret)
 			goto err;
 	}
+	if (!uart->port.dev)
+		return -EINVAL;
 
 	if (up->port.flags & UPF_FIXED_TYPE)
 		uart->port.type = up->port.type;
-- 
2.34.1

^ permalink raw reply related

* [PATCH] serial: 8250_pci: add support for the AX99100
From: Martin Roukala (né Peres) @ 2026-03-09 13:53 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Jiri Slaby
  Cc: linux-kernel, linux-serial, Martin Roukala (né Peres)

This is found in popular brands such as StarTech.com or Delock, and has
been a source of frustration to quite a few people, if I can trust
Amazon comments complaining about Linux support via the official
out-of-the-tree driver.

Signed-off-by: Martin Roukala (né Peres) <martin.roukala@mupuf.org>
---
I have been carrying this patch for 5+ years, without experiencing any
problem related to it... so I thought it would be a good time to start
upstreaming it and solving the lack of support for the AX99100 for more
than just me and users of my packages.
---
 drivers/tty/serial/8250/8250_pci.c | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c
index aa1ab4da9ff1..6cfd1b2af5b7 100644
--- a/drivers/tty/serial/8250/8250_pci.c
+++ b/drivers/tty/serial/8250/8250_pci.c
@@ -137,6 +137,8 @@ struct serial_private {
 };
 
 #define PCI_DEVICE_ID_HPE_PCI_SERIAL	0x37e
+#define PCIE_VENDOR_ID_ASIX		0x125B
+#define PCIE_DEVICE_ID_AX99100		0x9100
 
 static const struct pci_device_id pci_use_msi[] = {
 	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9900,
@@ -149,6 +151,8 @@ static const struct pci_device_id pci_use_msi[] = {
 			 0xA000, 0x1000) },
 	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_HP_3PAR, PCI_DEVICE_ID_HPE_PCI_SERIAL,
 			 PCI_ANY_ID, PCI_ANY_ID) },
+	{ PCI_DEVICE_SUB(PCIE_VENDOR_ID_ASIX, PCIE_DEVICE_ID_AX99100,
+			 0xA000, 0x1000) },
 	{ }
 };
 
@@ -920,6 +924,7 @@ static int pci_netmos_init(struct pci_dev *dev)
 	case PCI_DEVICE_ID_NETMOS_9912:
 	case PCI_DEVICE_ID_NETMOS_9922:
 	case PCI_DEVICE_ID_NETMOS_9900:
+	case PCIE_DEVICE_ID_AX99100:
 		num_serial = pci_netmos_9900_numports(dev);
 		break;
 
@@ -2544,6 +2549,14 @@ static struct pci_serial_quirk pci_serial_quirks[] = {
 		.init		= pci_netmos_init,
 		.setup		= pci_netmos_9900_setup,
 	},
+	{
+		.vendor		= PCIE_VENDOR_ID_ASIX,
+		.device		= PCI_ANY_ID,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.init		= pci_netmos_init,
+		.setup		= pci_netmos_9900_setup,
+	},
 	/*
 	 * EndRun Technologies
 	*/
@@ -6065,6 +6078,10 @@ static const struct pci_device_id serial_pci_tbl[] = {
 		0xA000, 0x3002,
 		0, 0, pbn_NETMOS9900_2s_115200 },
 
+	{	PCIE_VENDOR_ID_ASIX, PCIE_DEVICE_ID_AX99100,
+		0xA000, 0x1000,
+		0, 0, pbn_b0_1_115200 },
+
 	/*
 	 * Best Connectivity and Rosewill PCI Multi I/O cards
 	 */

---
base-commit: 1f318b96cc84d7c2ab792fcc0bfd42a7ca890681
change-id: 20260309-8250_pci_ax99100-d4ee75bbdab3

Best regards,
-- 
Martin Roukala (né Peres) <martin.roukala@mupuf.org>


^ permalink raw reply related

* Re: [PATCH 0/3] vt: add modifier support to cursor and navigation keys
From: Alexey Gladkov @ 2026-03-09 12:35 UTC (permalink / raw)
  To: Greg Kroah-Hartman; +Cc: Nicolas Pitre, Jiri Slaby, linux-serial, linux-kernel
In-Reply-To: <q19s9nr3-6o47-4pq9-7r49-8q25p0021o1q@syhkavp.arg>

On Sun, Feb 08, 2026 at 11:22:27AM -0500, Nicolas Pitre wrote:
> On Sun, 8 Feb 2026, Greg Kroah-Hartman wrote:
> 
> > Argh, sorry, I saw this patch series too late for this merge window.
> > I'll review it after -rc1 is out.
> 
> Too bad.
> 
> But please at least consider  this one now
> https://lkml.org/lkml/2026/1/27/1886
> and queue it for the stable tree as well.

Greg, a kindly reminder about the patchset.

-- 
Rgrds, legion


^ permalink raw reply

* [PATCH v2] serial: remove drivers for espressif esp32
From: Julian Braha @ 2026-03-09 12:23 UTC (permalink / raw)
  To: gregkh, jcmvbkbc
  Cc: rdunlap, ilpo.jarvinen, conor+dt, robh+dt, jirislaby,
	linux-serial, devicetree, linux-kernel, Julian Braha

These drivers were added about 3 years ago, and depend on the
XTENSA_PLATFORM_ESP32 config option which has never existed,
so no device can actually use them.
They can only be compiled with COMPILE_TEST.

In a previous conversation [1], Greg suggested removing the
drivers, and Max, the original submitter of the drivers, agreed
due to a lack of foreseeable development.

Link: https://lore.kernel.org/all/20260308131412.1102749-1-julianbraha@gmail.com/ [1]
Signed-off-by: Julian Braha <julianbraha@gmail.com>
---
v2: added details to patch description
---
 drivers/tty/serial/Kconfig      |  26 --
 drivers/tty/serial/Makefile     |   2 -
 drivers/tty/serial/esp32_acm.c  | 459 -------------------
 drivers/tty/serial/esp32_uart.c | 779 --------------------------------
 4 files changed, 1266 deletions(-)
 delete mode 100644 drivers/tty/serial/esp32_acm.c
 delete mode 100644 drivers/tty/serial/esp32_uart.c

diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index f86775cfdcc9..686e7fb073b8 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -1593,32 +1593,6 @@ config SERIAL_NUVOTON_MA35D1_CONSOLE
 	  but you can alter that using a kernel command line option such as
 	  "console=ttyNVTx".
 
-config SERIAL_ESP32
-	tristate "Espressif ESP32 UART support"
-	depends on XTENSA_PLATFORM_ESP32 || (COMPILE_TEST && OF)
-	select SERIAL_CORE
-	select SERIAL_CORE_CONSOLE
-	select SERIAL_EARLYCON
-	help
-	  Driver for the UART controllers of the Espressif ESP32xx SoCs.
-	  When earlycon option is enabled the following kernel command line
-	  snippets may be used:
-	    earlycon=esp32s3uart,mmio32,0x60000000,115200n8,40000000
-	    earlycon=esp32uart,mmio32,0x3ff40000,115200n8
-
-config SERIAL_ESP32_ACM
-	tristate "Espressif ESP32 USB ACM gadget support"
-	depends on XTENSA_PLATFORM_ESP32 || (COMPILE_TEST && OF)
-	select SERIAL_CORE
-	select SERIAL_CORE_CONSOLE
-	select SERIAL_EARLYCON
-	help
-	  Driver for the CDC ACM gadget controller of the Espressif ESP32S3
-	  SoCs that share separate USB controller with the JTAG adapter.
-	  When earlycon option is enabled the following kernel command line
-	  snippet may be used:
-	    earlycon=esp32s3acm,mmio32,0x60038000
-
 endmenu
 
 config SERIAL_MCTRL_GPIO
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index a2ccbc508ec5..bba7b21a4a1d 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -37,8 +37,6 @@ obj-$(CONFIG_SERIAL_CLPS711X)		+= clps711x.o
 obj-$(CONFIG_SERIAL_CPM)		+= cpm_uart.o
 obj-$(CONFIG_SERIAL_CONEXANT_DIGICOLOR)	+= digicolor-usart.o
 obj-$(CONFIG_SERIAL_DZ)			+= dz.o
-obj-$(CONFIG_SERIAL_ESP32)		+= esp32_uart.o
-obj-$(CONFIG_SERIAL_ESP32_ACM)		+= esp32_acm.o
 obj-$(CONFIG_SERIAL_FSL_LINFLEXUART)	+= fsl_linflexuart.o
 obj-$(CONFIG_SERIAL_FSL_LPUART)		+= fsl_lpuart.o
 obj-$(CONFIG_SERIAL_ICOM)		+= icom.o
diff --git a/drivers/tty/serial/esp32_acm.c b/drivers/tty/serial/esp32_acm.c
deleted file mode 100644
index bb7cc65427f0..000000000000
--- a/drivers/tty/serial/esp32_acm.c
+++ /dev/null
@@ -1,459 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include <linux/bitfield.h>
-#include <linux/bits.h>
-#include <linux/console.h>
-#include <linux/delay.h>
-#include <linux/io.h>
-#include <linux/irq.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/platform_device.h>
-#include <linux/serial_core.h>
-#include <linux/slab.h>
-#include <linux/tty_flip.h>
-#include <asm/serial.h>
-
-#define DRIVER_NAME	"esp32s3-acm"
-#define DEV_NAME	"ttyGS"
-#define UART_NR		4
-
-#define ESP32S3_ACM_TX_FIFO_SIZE	64
-
-#define USB_SERIAL_JTAG_EP1_REG		0x00
-#define USB_SERIAL_JTAG_EP1_CONF_REG	0x04
-#define USB_SERIAL_JTAG_WR_DONE				BIT(0)
-#define USB_SERIAL_JTAG_SERIAL_IN_EP_DATA_FREE		BIT(1)
-#define USB_SERIAL_JTAG_INT_ST_REG	0x0c
-#define USB_SERIAL_JTAG_SERIAL_OUT_RECV_PKT_INT_ST	BIT(2)
-#define USB_SERIAL_JTAG_SERIAL_IN_EMPTY_INT_ST		BIT(3)
-#define USB_SERIAL_JTAG_INT_ENA_REG	0x10
-#define USB_SERIAL_JTAG_SERIAL_OUT_RECV_PKT_INT_ENA	BIT(2)
-#define USB_SERIAL_JTAG_SERIAL_IN_EMPTY_INT_ENA		BIT(3)
-#define USB_SERIAL_JTAG_INT_CLR_REG	0x14
-#define USB_SERIAL_JTAG_IN_EP1_ST_REG	0x2c
-#define USB_SERIAL_JTAG_IN_EP1_WR_ADDR			GENMASK(8, 2)
-#define USB_SERIAL_JTAG_OUT_EP1_ST_REG	0x3c
-#define USB_SERIAL_JTAG_OUT_EP1_REC_DATA_CNT		GENMASK(22, 16)
-
-static const struct of_device_id esp32s3_acm_dt_ids[] = {
-	{
-		.compatible = "esp,esp32s3-acm",
-	}, { /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(of, esp32s3_acm_dt_ids);
-
-static struct uart_port *esp32s3_acm_ports[UART_NR];
-
-static void esp32s3_acm_write(struct uart_port *port, unsigned long reg, u32 v)
-{
-	writel(v, port->membase + reg);
-}
-
-static u32 esp32s3_acm_read(struct uart_port *port, unsigned long reg)
-{
-	return readl(port->membase + reg);
-}
-
-static u32 esp32s3_acm_tx_fifo_free(struct uart_port *port)
-{
-	u32 status = esp32s3_acm_read(port, USB_SERIAL_JTAG_EP1_CONF_REG);
-
-	return status & USB_SERIAL_JTAG_SERIAL_IN_EP_DATA_FREE;
-}
-
-static u32 esp32s3_acm_tx_fifo_cnt(struct uart_port *port)
-{
-	u32 status = esp32s3_acm_read(port, USB_SERIAL_JTAG_IN_EP1_ST_REG);
-
-	return FIELD_GET(USB_SERIAL_JTAG_IN_EP1_WR_ADDR, status);
-}
-
-static u32 esp32s3_acm_rx_fifo_cnt(struct uart_port *port)
-{
-	u32 status = esp32s3_acm_read(port, USB_SERIAL_JTAG_OUT_EP1_ST_REG);
-
-	return FIELD_GET(USB_SERIAL_JTAG_OUT_EP1_REC_DATA_CNT, status);
-}
-
-/* return TIOCSER_TEMT when transmitter is not busy */
-static unsigned int esp32s3_acm_tx_empty(struct uart_port *port)
-{
-	return esp32s3_acm_tx_fifo_cnt(port) == 0 ? TIOCSER_TEMT : 0;
-}
-
-static void esp32s3_acm_set_mctrl(struct uart_port *port, unsigned int mctrl)
-{
-}
-
-static unsigned int esp32s3_acm_get_mctrl(struct uart_port *port)
-{
-	return TIOCM_CAR;
-}
-
-static void esp32s3_acm_stop_tx(struct uart_port *port)
-{
-	u32 int_ena;
-
-	int_ena = esp32s3_acm_read(port, USB_SERIAL_JTAG_INT_ENA_REG);
-	int_ena &= ~USB_SERIAL_JTAG_SERIAL_IN_EMPTY_INT_ENA;
-	esp32s3_acm_write(port, USB_SERIAL_JTAG_INT_ENA_REG, int_ena);
-}
-
-static void esp32s3_acm_rxint(struct uart_port *port)
-{
-	struct tty_port *tty_port = &port->state->port;
-	u32 rx_fifo_cnt = esp32s3_acm_rx_fifo_cnt(port);
-	unsigned long flags;
-	u32 i;
-
-	if (!rx_fifo_cnt)
-		return;
-
-	spin_lock_irqsave(&port->lock, flags);
-
-	for (i = 0; i < rx_fifo_cnt; ++i) {
-		u32 rx = esp32s3_acm_read(port, USB_SERIAL_JTAG_EP1_REG);
-
-		++port->icount.rx;
-		tty_insert_flip_char(tty_port, rx, TTY_NORMAL);
-	}
-	spin_unlock_irqrestore(&port->lock, flags);
-
-	tty_flip_buffer_push(tty_port);
-}
-
-static void esp32s3_acm_push(struct uart_port *port)
-{
-	if (esp32s3_acm_tx_fifo_free(port))
-		esp32s3_acm_write(port, USB_SERIAL_JTAG_EP1_CONF_REG,
-				  USB_SERIAL_JTAG_WR_DONE);
-}
-
-static void esp32s3_acm_put_char(struct uart_port *port, u8 c)
-{
-	esp32s3_acm_write(port, USB_SERIAL_JTAG_EP1_REG, c);
-}
-
-static void esp32s3_acm_put_char_sync(struct uart_port *port, u8 c)
-{
-	unsigned long timeout = jiffies + HZ;
-
-	while (!esp32s3_acm_tx_fifo_free(port)) {
-		if (time_after(jiffies, timeout)) {
-			dev_warn(port->dev, "timeout waiting for TX FIFO\n");
-			return;
-		}
-		cpu_relax();
-	}
-	esp32s3_acm_put_char(port, c);
-	esp32s3_acm_push(port);
-}
-
-static void esp32s3_acm_transmit_buffer(struct uart_port *port)
-{
-	u32 tx_fifo_used;
-	unsigned int pending;
-	u8 ch;
-
-	if (!esp32s3_acm_tx_fifo_free(port))
-		return;
-
-	tx_fifo_used = esp32s3_acm_tx_fifo_cnt(port);
-	pending = uart_port_tx_limited(port, ch,
-				       ESP32S3_ACM_TX_FIFO_SIZE - tx_fifo_used,
-				       true, esp32s3_acm_put_char(port, ch),
-				       ({}));
-	if (pending) {
-		u32 int_ena;
-
-		int_ena = esp32s3_acm_read(port, USB_SERIAL_JTAG_INT_ENA_REG);
-		int_ena |= USB_SERIAL_JTAG_SERIAL_IN_EMPTY_INT_ENA;
-		esp32s3_acm_write(port, USB_SERIAL_JTAG_INT_ENA_REG, int_ena);
-	}
-	esp32s3_acm_push(port);
-}
-
-static void esp32s3_acm_txint(struct uart_port *port)
-{
-	esp32s3_acm_transmit_buffer(port);
-}
-
-static irqreturn_t esp32s3_acm_int(int irq, void *dev_id)
-{
-	struct uart_port *port = dev_id;
-	u32 status;
-
-	status = esp32s3_acm_read(port, USB_SERIAL_JTAG_INT_ST_REG);
-	esp32s3_acm_write(port, USB_SERIAL_JTAG_INT_CLR_REG, status);
-
-	if (status & USB_SERIAL_JTAG_SERIAL_OUT_RECV_PKT_INT_ST)
-		esp32s3_acm_rxint(port);
-	if (status & USB_SERIAL_JTAG_SERIAL_IN_EMPTY_INT_ST)
-		esp32s3_acm_txint(port);
-
-	return IRQ_RETVAL(status);
-}
-
-static void esp32s3_acm_start_tx(struct uart_port *port)
-{
-	esp32s3_acm_transmit_buffer(port);
-}
-
-static void esp32s3_acm_stop_rx(struct uart_port *port)
-{
-	u32 int_ena;
-
-	int_ena = esp32s3_acm_read(port, USB_SERIAL_JTAG_INT_ENA_REG);
-	int_ena &= ~USB_SERIAL_JTAG_SERIAL_OUT_RECV_PKT_INT_ENA;
-	esp32s3_acm_write(port, USB_SERIAL_JTAG_INT_ENA_REG, int_ena);
-}
-
-static int esp32s3_acm_startup(struct uart_port *port)
-{
-	int ret;
-
-	ret = request_irq(port->irq, esp32s3_acm_int, 0, DRIVER_NAME, port);
-	if (ret)
-		return ret;
-	esp32s3_acm_write(port, USB_SERIAL_JTAG_INT_ENA_REG,
-			  USB_SERIAL_JTAG_SERIAL_OUT_RECV_PKT_INT_ENA);
-
-	return 0;
-}
-
-static void esp32s3_acm_shutdown(struct uart_port *port)
-{
-	esp32s3_acm_write(port, USB_SERIAL_JTAG_INT_ENA_REG, 0);
-	free_irq(port->irq, port);
-}
-
-static void esp32s3_acm_set_termios(struct uart_port *port,
-				    struct ktermios *termios,
-				    const struct ktermios *old)
-{
-}
-
-static const char *esp32s3_acm_type(struct uart_port *port)
-{
-	return "ESP32S3 ACM";
-}
-
-/* configure/auto-configure the port */
-static void esp32s3_acm_config_port(struct uart_port *port, int flags)
-{
-	if (flags & UART_CONFIG_TYPE)
-		port->type = PORT_GENERIC;
-}
-
-#ifdef CONFIG_CONSOLE_POLL
-static void esp32s3_acm_poll_put_char(struct uart_port *port, unsigned char c)
-{
-	esp32s3_acm_put_char_sync(port, c);
-}
-
-static int esp32s3_acm_poll_get_char(struct uart_port *port)
-{
-	if (esp32s3_acm_rx_fifo_cnt(port))
-		return esp32s3_acm_read(port, USB_SERIAL_JTAG_EP1_REG);
-	else
-		return NO_POLL_CHAR;
-}
-#endif
-
-static const struct uart_ops esp32s3_acm_pops = {
-	.tx_empty	= esp32s3_acm_tx_empty,
-	.set_mctrl	= esp32s3_acm_set_mctrl,
-	.get_mctrl	= esp32s3_acm_get_mctrl,
-	.stop_tx	= esp32s3_acm_stop_tx,
-	.start_tx	= esp32s3_acm_start_tx,
-	.stop_rx	= esp32s3_acm_stop_rx,
-	.startup	= esp32s3_acm_startup,
-	.shutdown	= esp32s3_acm_shutdown,
-	.set_termios	= esp32s3_acm_set_termios,
-	.type		= esp32s3_acm_type,
-	.config_port	= esp32s3_acm_config_port,
-#ifdef CONFIG_CONSOLE_POLL
-	.poll_put_char	= esp32s3_acm_poll_put_char,
-	.poll_get_char	= esp32s3_acm_poll_get_char,
-#endif
-};
-
-static void esp32s3_acm_string_write(struct uart_port *port, const char *s,
-				     unsigned int count)
-{
-	uart_console_write(port, s, count, esp32s3_acm_put_char_sync);
-}
-
-static void
-esp32s3_acm_console_write(struct console *co, const char *s, unsigned int count)
-{
-	struct uart_port *port = esp32s3_acm_ports[co->index];
-	unsigned long flags;
-	bool locked = true;
-
-	if (port->sysrq)
-		locked = false;
-	else if (oops_in_progress)
-		locked = spin_trylock_irqsave(&port->lock, flags);
-	else
-		spin_lock_irqsave(&port->lock, flags);
-
-	esp32s3_acm_string_write(port, s, count);
-
-	if (locked)
-		spin_unlock_irqrestore(&port->lock, flags);
-}
-
-static struct uart_driver esp32s3_acm_reg;
-static struct console esp32s3_acm_console = {
-	.name		= DEV_NAME,
-	.write		= esp32s3_acm_console_write,
-	.device		= uart_console_device,
-	.flags		= CON_PRINTBUFFER,
-	.index		= -1,
-	.data		= &esp32s3_acm_reg,
-};
-
-static void esp32s3_acm_earlycon_write(struct console *con, const char *s,
-				      unsigned int n)
-{
-	struct earlycon_device *dev = con->data;
-
-	uart_console_write(&dev->port, s, n, esp32s3_acm_put_char_sync);
-}
-
-#ifdef CONFIG_CONSOLE_POLL
-static int esp32s3_acm_earlycon_read(struct console *con, char *s, unsigned int n)
-{
-	struct earlycon_device *dev = con->data;
-	unsigned int num_read = 0;
-
-	while (num_read < n) {
-		int c = esp32s3_acm_poll_get_char(&dev->port);
-
-		if (c == NO_POLL_CHAR)
-			break;
-		s[num_read++] = c;
-	}
-	return num_read;
-}
-#endif
-
-static int __init esp32s3_acm_early_console_setup(struct earlycon_device *device,
-						   const char *options)
-{
-	if (!device->port.membase)
-		return -ENODEV;
-
-	device->con->write = esp32s3_acm_earlycon_write;
-#ifdef CONFIG_CONSOLE_POLL
-	device->con->read = esp32s3_acm_earlycon_read;
-#endif
-	return 0;
-}
-
-OF_EARLYCON_DECLARE(esp32s3acm, "esp,esp32s3-acm",
-		    esp32s3_acm_early_console_setup);
-
-static struct uart_driver esp32s3_acm_reg = {
-	.owner		= THIS_MODULE,
-	.driver_name	= DRIVER_NAME,
-	.dev_name	= DEV_NAME,
-	.nr		= ARRAY_SIZE(esp32s3_acm_ports),
-	.cons		= &esp32s3_acm_console,
-};
-
-static int esp32s3_acm_probe(struct platform_device *pdev)
-{
-	struct device_node *np = pdev->dev.of_node;
-	struct uart_port *port;
-	struct resource *res;
-	int ret;
-
-	port = devm_kzalloc(&pdev->dev, sizeof(*port), GFP_KERNEL);
-	if (!port)
-		return -ENOMEM;
-
-	ret = of_alias_get_id(np, "serial");
-	if (ret < 0) {
-		dev_err(&pdev->dev, "failed to get alias id, errno %d\n", ret);
-		return ret;
-	}
-	if (ret >= UART_NR) {
-		dev_err(&pdev->dev, "driver limited to %d serial ports\n",
-			UART_NR);
-		return -ENOMEM;
-	}
-
-	port->line = ret;
-
-	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	if (!res)
-		return -ENODEV;
-
-	port->mapbase = res->start;
-	port->membase = devm_ioremap_resource(&pdev->dev, res);
-	if (IS_ERR(port->membase))
-		return PTR_ERR(port->membase);
-
-	port->dev = &pdev->dev;
-	port->type = PORT_GENERIC;
-	port->iotype = UPIO_MEM;
-	port->irq = platform_get_irq(pdev, 0);
-	port->ops = &esp32s3_acm_pops;
-	port->flags = UPF_BOOT_AUTOCONF;
-	port->has_sysrq = 1;
-	port->fifosize = ESP32S3_ACM_TX_FIFO_SIZE;
-
-	esp32s3_acm_ports[port->line] = port;
-
-	platform_set_drvdata(pdev, port);
-
-	return uart_add_one_port(&esp32s3_acm_reg, port);
-}
-
-static void esp32s3_acm_remove(struct platform_device *pdev)
-{
-	struct uart_port *port = platform_get_drvdata(pdev);
-
-	uart_remove_one_port(&esp32s3_acm_reg, port);
-}
-
-
-static struct platform_driver esp32s3_acm_driver = {
-	.probe		= esp32s3_acm_probe,
-	.remove		= esp32s3_acm_remove,
-	.driver		= {
-		.name	= DRIVER_NAME,
-		.of_match_table	= esp32s3_acm_dt_ids,
-	},
-};
-
-static int __init esp32s3_acm_init(void)
-{
-	int ret;
-
-	ret = uart_register_driver(&esp32s3_acm_reg);
-	if (ret)
-		return ret;
-
-	ret = platform_driver_register(&esp32s3_acm_driver);
-	if (ret)
-		uart_unregister_driver(&esp32s3_acm_reg);
-
-	return ret;
-}
-
-static void __exit esp32s3_acm_exit(void)
-{
-	platform_driver_unregister(&esp32s3_acm_driver);
-	uart_unregister_driver(&esp32s3_acm_reg);
-}
-
-module_init(esp32s3_acm_init);
-module_exit(esp32s3_acm_exit);
-
-MODULE_AUTHOR("Max Filippov <jcmvbkbc@gmail.com>");
-MODULE_DESCRIPTION("Espressif ESP32 USB ACM gadget support");
-MODULE_LICENSE("GPL");
diff --git a/drivers/tty/serial/esp32_uart.c b/drivers/tty/serial/esp32_uart.c
deleted file mode 100644
index 667c2198a03a..000000000000
--- a/drivers/tty/serial/esp32_uart.c
+++ /dev/null
@@ -1,779 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include <linux/bitfield.h>
-#include <linux/bits.h>
-#include <linux/clk.h>
-#include <linux/console.h>
-#include <linux/delay.h>
-#include <linux/io.h>
-#include <linux/irq.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/platform_device.h>
-#include <linux/property.h>
-#include <linux/serial_core.h>
-#include <linux/slab.h>
-#include <linux/tty_flip.h>
-#include <asm/serial.h>
-
-#define DRIVER_NAME	"esp32-uart"
-#define DEV_NAME	"ttyS"
-#define UART_NR		3
-
-#define ESP32_UART_TX_FIFO_SIZE	127
-#define ESP32_UART_RX_FIFO_SIZE	127
-
-#define UART_FIFO_REG			0x00
-#define UART_INT_RAW_REG		0x04
-#define UART_INT_ST_REG			0x08
-#define UART_INT_ENA_REG		0x0c
-#define UART_INT_CLR_REG		0x10
-#define UART_RXFIFO_FULL_INT			BIT(0)
-#define UART_TXFIFO_EMPTY_INT			BIT(1)
-#define UART_BRK_DET_INT			BIT(7)
-#define UART_CLKDIV_REG			0x14
-#define ESP32_UART_CLKDIV			GENMASK(19, 0)
-#define ESP32S3_UART_CLKDIV			GENMASK(11, 0)
-#define UART_CLKDIV_SHIFT			0
-#define UART_CLKDIV_FRAG			GENMASK(23, 20)
-#define UART_STATUS_REG			0x1c
-#define ESP32_UART_RXFIFO_CNT			GENMASK(7, 0)
-#define ESP32S3_UART_RXFIFO_CNT			GENMASK(9, 0)
-#define UART_RXFIFO_CNT_SHIFT			0
-#define UART_DSRN				BIT(13)
-#define UART_CTSN				BIT(14)
-#define ESP32_UART_TXFIFO_CNT			GENMASK(23, 16)
-#define ESP32S3_UART_TXFIFO_CNT			GENMASK(25, 16)
-#define UART_TXFIFO_CNT_SHIFT			16
-#define UART_CONF0_REG			0x20
-#define UART_PARITY				BIT(0)
-#define UART_PARITY_EN				BIT(1)
-#define UART_BIT_NUM				GENMASK(3, 2)
-#define UART_BIT_NUM_5				0
-#define UART_BIT_NUM_6				1
-#define UART_BIT_NUM_7				2
-#define UART_BIT_NUM_8				3
-#define UART_STOP_BIT_NUM			GENMASK(5, 4)
-#define UART_STOP_BIT_NUM_1			1
-#define UART_STOP_BIT_NUM_2			3
-#define UART_SW_RTS				BIT(6)
-#define UART_SW_DTR				BIT(7)
-#define UART_LOOPBACK				BIT(14)
-#define UART_TX_FLOW_EN				BIT(15)
-#define UART_RTS_INV				BIT(23)
-#define UART_DTR_INV				BIT(24)
-#define UART_CONF1_REG			0x24
-#define UART_RXFIFO_FULL_THRHD_SHIFT		0
-#define ESP32_UART_TXFIFO_EMPTY_THRHD_SHIFT	8
-#define ESP32S3_UART_TXFIFO_EMPTY_THRHD_SHIFT	10
-#define ESP32_UART_RX_FLOW_EN			BIT(23)
-#define ESP32S3_UART_RX_FLOW_EN			BIT(22)
-#define ESP32S3_UART_CLK_CONF_REG	0x78
-#define ESP32S3_UART_SCLK_DIV_B			GENMASK(5, 0)
-#define ESP32S3_UART_SCLK_DIV_A			GENMASK(11, 6)
-#define ESP32S3_UART_SCLK_DIV_NUM		GENMASK(19, 12)
-#define ESP32S3_UART_SCLK_SEL			GENMASK(21, 20)
-#define APB_CLK					1
-#define RC_FAST_CLK				2
-#define XTAL_CLK				3
-#define ESP32S3_UART_SCLK_EN			BIT(22)
-#define ESP32S3_UART_RST_CORE			BIT(23)
-#define ESP32S3_UART_TX_SCLK_EN			BIT(24)
-#define ESP32S3_UART_RX_SCLK_EN			BIT(25)
-#define ESP32S3_UART_TX_RST_CORE		BIT(26)
-#define ESP32S3_UART_RX_RST_CORE		BIT(27)
-
-#define ESP32S3_UART_CLK_CONF_DEFAULT \
-	(ESP32S3_UART_RX_SCLK_EN | \
-	 ESP32S3_UART_TX_SCLK_EN | \
-	 ESP32S3_UART_SCLK_EN | \
-	 FIELD_PREP(ESP32S3_UART_SCLK_SEL, XTAL_CLK))
-
-struct esp32_port {
-	struct uart_port port;
-	struct clk *clk;
-};
-
-struct esp32_uart_variant {
-	u32 clkdiv_mask;
-	u32 rxfifo_cnt_mask;
-	u32 txfifo_cnt_mask;
-	u32 txfifo_empty_thrhd_shift;
-	u32 rx_flow_en;
-	const char *type;
-	bool has_clkconf;
-};
-
-static const struct esp32_uart_variant esp32_variant = {
-	.clkdiv_mask = ESP32_UART_CLKDIV,
-	.rxfifo_cnt_mask = ESP32_UART_RXFIFO_CNT,
-	.txfifo_cnt_mask = ESP32_UART_TXFIFO_CNT,
-	.txfifo_empty_thrhd_shift = ESP32_UART_TXFIFO_EMPTY_THRHD_SHIFT,
-	.rx_flow_en = ESP32_UART_RX_FLOW_EN,
-	.type = "ESP32 UART",
-};
-
-static const struct esp32_uart_variant esp32s3_variant = {
-	.clkdiv_mask = ESP32S3_UART_CLKDIV,
-	.rxfifo_cnt_mask = ESP32S3_UART_RXFIFO_CNT,
-	.txfifo_cnt_mask = ESP32S3_UART_TXFIFO_CNT,
-	.txfifo_empty_thrhd_shift = ESP32S3_UART_TXFIFO_EMPTY_THRHD_SHIFT,
-	.rx_flow_en = ESP32S3_UART_RX_FLOW_EN,
-	.type = "ESP32S3 UART",
-	.has_clkconf = true,
-};
-
-static const struct of_device_id esp32_uart_dt_ids[] = {
-	{
-		.compatible = "esp,esp32-uart",
-		.data = &esp32_variant,
-	}, {
-		.compatible = "esp,esp32s3-uart",
-		.data = &esp32s3_variant,
-	}, { /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(of, esp32_uart_dt_ids);
-
-static struct esp32_port *esp32_uart_ports[UART_NR];
-
-static const struct esp32_uart_variant *port_variant(struct uart_port *port)
-{
-	return port->private_data;
-}
-
-static void esp32_uart_write(struct uart_port *port, unsigned long reg, u32 v)
-{
-	writel(v, port->membase + reg);
-}
-
-static u32 esp32_uart_read(struct uart_port *port, unsigned long reg)
-{
-	return readl(port->membase + reg);
-}
-
-static u32 esp32_uart_tx_fifo_cnt(struct uart_port *port)
-{
-	u32 status = esp32_uart_read(port, UART_STATUS_REG);
-
-	return (status & port_variant(port)->txfifo_cnt_mask) >> UART_TXFIFO_CNT_SHIFT;
-}
-
-static u32 esp32_uart_rx_fifo_cnt(struct uart_port *port)
-{
-	u32 status = esp32_uart_read(port, UART_STATUS_REG);
-
-	return (status & port_variant(port)->rxfifo_cnt_mask) >> UART_RXFIFO_CNT_SHIFT;
-}
-
-/* return TIOCSER_TEMT when transmitter is not busy */
-static unsigned int esp32_uart_tx_empty(struct uart_port *port)
-{
-	return esp32_uart_tx_fifo_cnt(port) ? 0 : TIOCSER_TEMT;
-}
-
-static void esp32_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
-{
-	u32 conf0 = esp32_uart_read(port, UART_CONF0_REG);
-
-	conf0 &= ~(UART_LOOPBACK |
-		   UART_SW_RTS | UART_RTS_INV |
-		   UART_SW_DTR | UART_DTR_INV);
-
-	if (mctrl & TIOCM_RTS)
-		conf0 |= UART_SW_RTS;
-	if (mctrl & TIOCM_DTR)
-		conf0 |= UART_SW_DTR;
-	if (mctrl & TIOCM_LOOP)
-		conf0 |= UART_LOOPBACK;
-
-	esp32_uart_write(port, UART_CONF0_REG, conf0);
-}
-
-static unsigned int esp32_uart_get_mctrl(struct uart_port *port)
-{
-	u32 status = esp32_uart_read(port, UART_STATUS_REG);
-	unsigned int ret = TIOCM_CAR;
-
-	if (status & UART_DSRN)
-		ret |= TIOCM_DSR;
-	if (status & UART_CTSN)
-		ret |= TIOCM_CTS;
-
-	return ret;
-}
-
-static void esp32_uart_stop_tx(struct uart_port *port)
-{
-	u32 int_ena;
-
-	int_ena = esp32_uart_read(port, UART_INT_ENA_REG);
-	int_ena &= ~UART_TXFIFO_EMPTY_INT;
-	esp32_uart_write(port, UART_INT_ENA_REG, int_ena);
-}
-
-static void esp32_uart_rxint(struct uart_port *port)
-{
-	struct tty_port *tty_port = &port->state->port;
-	u32 rx_fifo_cnt = esp32_uart_rx_fifo_cnt(port);
-	unsigned long flags;
-	u32 i;
-
-	if (!rx_fifo_cnt)
-		return;
-
-	spin_lock_irqsave(&port->lock, flags);
-
-	for (i = 0; i < rx_fifo_cnt; ++i) {
-		u32 rx = esp32_uart_read(port, UART_FIFO_REG);
-
-		if (!rx &&
-		    (esp32_uart_read(port, UART_INT_ST_REG) & UART_BRK_DET_INT)) {
-			esp32_uart_write(port, UART_INT_CLR_REG, UART_BRK_DET_INT);
-			++port->icount.brk;
-			uart_handle_break(port);
-		} else {
-			if (uart_handle_sysrq_char(port, (unsigned char)rx))
-				continue;
-			tty_insert_flip_char(tty_port, rx, TTY_NORMAL);
-			++port->icount.rx;
-		}
-	}
-	spin_unlock_irqrestore(&port->lock, flags);
-
-	tty_flip_buffer_push(tty_port);
-}
-
-static void esp32_uart_put_char(struct uart_port *port, u8 c)
-{
-	esp32_uart_write(port, UART_FIFO_REG, c);
-}
-
-static void esp32_uart_put_char_sync(struct uart_port *port, u8 c)
-{
-	unsigned long timeout = jiffies + HZ;
-
-	while (esp32_uart_tx_fifo_cnt(port) >= ESP32_UART_TX_FIFO_SIZE) {
-		if (time_after(jiffies, timeout)) {
-			dev_warn(port->dev, "timeout waiting for TX FIFO\n");
-			return;
-		}
-		cpu_relax();
-	}
-	esp32_uart_put_char(port, c);
-}
-
-static void esp32_uart_transmit_buffer(struct uart_port *port)
-{
-	u32 tx_fifo_used = esp32_uart_tx_fifo_cnt(port);
-	unsigned int pending;
-	u8 ch;
-
-	if (tx_fifo_used >= ESP32_UART_TX_FIFO_SIZE)
-		return;
-
-	pending = uart_port_tx_limited(port, ch,
-				       ESP32_UART_TX_FIFO_SIZE - tx_fifo_used,
-				       true, esp32_uart_put_char(port, ch),
-				       ({}));
-	if (pending) {
-		u32 int_ena;
-
-		int_ena = esp32_uart_read(port, UART_INT_ENA_REG);
-		int_ena |= UART_TXFIFO_EMPTY_INT;
-		esp32_uart_write(port, UART_INT_ENA_REG, int_ena);
-	}
-}
-
-static void esp32_uart_txint(struct uart_port *port)
-{
-	esp32_uart_transmit_buffer(port);
-}
-
-static irqreturn_t esp32_uart_int(int irq, void *dev_id)
-{
-	struct uart_port *port = dev_id;
-	u32 status;
-
-	status = esp32_uart_read(port, UART_INT_ST_REG);
-
-	if (status & (UART_RXFIFO_FULL_INT | UART_BRK_DET_INT))
-		esp32_uart_rxint(port);
-	if (status & UART_TXFIFO_EMPTY_INT)
-		esp32_uart_txint(port);
-
-	esp32_uart_write(port, UART_INT_CLR_REG, status);
-
-	return IRQ_RETVAL(status);
-}
-
-static void esp32_uart_start_tx(struct uart_port *port)
-{
-	esp32_uart_transmit_buffer(port);
-}
-
-static void esp32_uart_stop_rx(struct uart_port *port)
-{
-	u32 int_ena;
-
-	int_ena = esp32_uart_read(port, UART_INT_ENA_REG);
-	int_ena &= ~UART_RXFIFO_FULL_INT;
-	esp32_uart_write(port, UART_INT_ENA_REG, int_ena);
-}
-
-static int esp32_uart_startup(struct uart_port *port)
-{
-	int ret = 0;
-	unsigned long flags;
-	struct esp32_port *sport = container_of(port, struct esp32_port, port);
-
-	ret = clk_prepare_enable(sport->clk);
-	if (ret)
-		return ret;
-
-	ret = request_irq(port->irq, esp32_uart_int, 0, DRIVER_NAME, port);
-	if (ret) {
-		clk_disable_unprepare(sport->clk);
-		return ret;
-	}
-
-	spin_lock_irqsave(&port->lock, flags);
-	if (port_variant(port)->has_clkconf)
-		esp32_uart_write(port, ESP32S3_UART_CLK_CONF_REG,
-				 ESP32S3_UART_CLK_CONF_DEFAULT);
-	esp32_uart_write(port, UART_CONF1_REG,
-			 (1 << UART_RXFIFO_FULL_THRHD_SHIFT) |
-			 (1 << port_variant(port)->txfifo_empty_thrhd_shift));
-	esp32_uart_write(port, UART_INT_CLR_REG, UART_RXFIFO_FULL_INT | UART_BRK_DET_INT);
-	esp32_uart_write(port, UART_INT_ENA_REG, UART_RXFIFO_FULL_INT | UART_BRK_DET_INT);
-	spin_unlock_irqrestore(&port->lock, flags);
-
-	return ret;
-}
-
-static void esp32_uart_shutdown(struct uart_port *port)
-{
-	struct esp32_port *sport = container_of(port, struct esp32_port, port);
-
-	esp32_uart_write(port, UART_INT_ENA_REG, 0);
-	free_irq(port->irq, port);
-	clk_disable_unprepare(sport->clk);
-}
-
-static bool esp32_uart_set_baud(struct uart_port *port, u32 baud)
-{
-	u32 sclk = port->uartclk;
-	u32 div = sclk / baud;
-
-	if (port_variant(port)->has_clkconf) {
-		u32 sclk_div = div / port_variant(port)->clkdiv_mask;
-
-		if (div > port_variant(port)->clkdiv_mask) {
-			sclk /= (sclk_div + 1);
-			div = sclk / baud;
-		}
-		esp32_uart_write(port, ESP32S3_UART_CLK_CONF_REG,
-				 FIELD_PREP(ESP32S3_UART_SCLK_DIV_NUM, sclk_div) |
-				 ESP32S3_UART_CLK_CONF_DEFAULT);
-	}
-
-	if (div <= port_variant(port)->clkdiv_mask) {
-		u32 frag = (sclk * 16) / baud - div * 16;
-
-		esp32_uart_write(port, UART_CLKDIV_REG,
-				 div | FIELD_PREP(UART_CLKDIV_FRAG, frag));
-		return true;
-	}
-
-	return false;
-}
-
-static void esp32_uart_set_termios(struct uart_port *port,
-				   struct ktermios *termios,
-				   const struct ktermios *old)
-{
-	unsigned long flags;
-	u32 conf0, conf1;
-	u32 baud;
-	const u32 rx_flow_en = port_variant(port)->rx_flow_en;
-	u32 max_div = port_variant(port)->clkdiv_mask;
-
-	termios->c_cflag &= ~CMSPAR;
-
-	if (port_variant(port)->has_clkconf)
-		max_div *= FIELD_MAX(ESP32S3_UART_SCLK_DIV_NUM);
-
-	baud = uart_get_baud_rate(port, termios, old,
-				  port->uartclk / max_div,
-				  port->uartclk / 16);
-
-	spin_lock_irqsave(&port->lock, flags);
-
-	conf0 = esp32_uart_read(port, UART_CONF0_REG);
-	conf0 &= ~(UART_PARITY_EN | UART_PARITY | UART_BIT_NUM | UART_STOP_BIT_NUM);
-
-	conf1 = esp32_uart_read(port, UART_CONF1_REG);
-	conf1 &= ~rx_flow_en;
-
-	if (termios->c_cflag & PARENB) {
-		conf0 |= UART_PARITY_EN;
-		if (termios->c_cflag & PARODD)
-			conf0 |= UART_PARITY;
-	}
-
-	switch (termios->c_cflag & CSIZE) {
-	case CS5:
-		conf0 |= FIELD_PREP(UART_BIT_NUM, UART_BIT_NUM_5);
-		break;
-	case CS6:
-		conf0 |= FIELD_PREP(UART_BIT_NUM, UART_BIT_NUM_6);
-		break;
-	case CS7:
-		conf0 |= FIELD_PREP(UART_BIT_NUM, UART_BIT_NUM_7);
-		break;
-	case CS8:
-		conf0 |= FIELD_PREP(UART_BIT_NUM, UART_BIT_NUM_8);
-		break;
-	}
-
-	if (termios->c_cflag & CSTOPB)
-		conf0 |= FIELD_PREP(UART_STOP_BIT_NUM, UART_STOP_BIT_NUM_2);
-	else
-		conf0 |= FIELD_PREP(UART_STOP_BIT_NUM, UART_STOP_BIT_NUM_1);
-
-	if (termios->c_cflag & CRTSCTS)
-		conf1 |= rx_flow_en;
-
-	esp32_uart_write(port, UART_CONF0_REG, conf0);
-	esp32_uart_write(port, UART_CONF1_REG, conf1);
-
-	if (baud) {
-		esp32_uart_set_baud(port, baud);
-		uart_update_timeout(port, termios->c_cflag, baud);
-	} else {
-		if (esp32_uart_set_baud(port, 115200)) {
-			baud = 115200;
-			tty_termios_encode_baud_rate(termios, baud, baud);
-			uart_update_timeout(port, termios->c_cflag, baud);
-		} else {
-			dev_warn(port->dev,
-				 "unable to set speed to %d baud or the default 115200\n",
-				 baud);
-		}
-	}
-	spin_unlock_irqrestore(&port->lock, flags);
-}
-
-static const char *esp32_uart_type(struct uart_port *port)
-{
-	return port_variant(port)->type;
-}
-
-/* configure/auto-configure the port */
-static void esp32_uart_config_port(struct uart_port *port, int flags)
-{
-	if (flags & UART_CONFIG_TYPE)
-		port->type = PORT_GENERIC;
-}
-
-#ifdef CONFIG_CONSOLE_POLL
-static int esp32_uart_poll_init(struct uart_port *port)
-{
-	struct esp32_port *sport = container_of(port, struct esp32_port, port);
-
-	return clk_prepare_enable(sport->clk);
-}
-
-static void esp32_uart_poll_put_char(struct uart_port *port, unsigned char c)
-{
-	esp32_uart_put_char_sync(port, c);
-}
-
-static int esp32_uart_poll_get_char(struct uart_port *port)
-{
-	if (esp32_uart_rx_fifo_cnt(port))
-		return esp32_uart_read(port, UART_FIFO_REG);
-	else
-		return NO_POLL_CHAR;
-
-}
-#endif
-
-static const struct uart_ops esp32_uart_pops = {
-	.tx_empty	= esp32_uart_tx_empty,
-	.set_mctrl	= esp32_uart_set_mctrl,
-	.get_mctrl	= esp32_uart_get_mctrl,
-	.stop_tx	= esp32_uart_stop_tx,
-	.start_tx	= esp32_uart_start_tx,
-	.stop_rx	= esp32_uart_stop_rx,
-	.startup	= esp32_uart_startup,
-	.shutdown	= esp32_uart_shutdown,
-	.set_termios	= esp32_uart_set_termios,
-	.type		= esp32_uart_type,
-	.config_port	= esp32_uart_config_port,
-#ifdef CONFIG_CONSOLE_POLL
-	.poll_init	= esp32_uart_poll_init,
-	.poll_put_char	= esp32_uart_poll_put_char,
-	.poll_get_char	= esp32_uart_poll_get_char,
-#endif
-};
-
-static void esp32_uart_console_putchar(struct uart_port *port, u8 c)
-{
-	esp32_uart_put_char_sync(port, c);
-}
-
-static void esp32_uart_string_write(struct uart_port *port, const char *s,
-				    unsigned int count)
-{
-	uart_console_write(port, s, count, esp32_uart_console_putchar);
-}
-
-static void
-esp32_uart_console_write(struct console *co, const char *s, unsigned int count)
-{
-	struct esp32_port *sport = esp32_uart_ports[co->index];
-	struct uart_port *port = &sport->port;
-	unsigned long flags;
-	bool locked = true;
-
-	if (port->sysrq)
-		locked = false;
-	else if (oops_in_progress)
-		locked = spin_trylock_irqsave(&port->lock, flags);
-	else
-		spin_lock_irqsave(&port->lock, flags);
-
-	esp32_uart_string_write(port, s, count);
-
-	if (locked)
-		spin_unlock_irqrestore(&port->lock, flags);
-}
-
-static int __init esp32_uart_console_setup(struct console *co, char *options)
-{
-	struct esp32_port *sport;
-	int baud = 115200;
-	int bits = 8;
-	int parity = 'n';
-	int flow = 'n';
-	int ret;
-
-	/*
-	 * check whether an invalid uart number has been specified, and
-	 * if so, search for the first available port that does have
-	 * console support.
-	 */
-	if (co->index == -1 || co->index >= ARRAY_SIZE(esp32_uart_ports))
-		co->index = 0;
-
-	sport = esp32_uart_ports[co->index];
-	if (!sport)
-		return -ENODEV;
-
-	ret = clk_prepare_enable(sport->clk);
-	if (ret)
-		return ret;
-
-	if (options)
-		uart_parse_options(options, &baud, &parity, &bits, &flow);
-
-	return uart_set_options(&sport->port, co, baud, parity, bits, flow);
-}
-
-static int esp32_uart_console_exit(struct console *co)
-{
-	struct esp32_port *sport = esp32_uart_ports[co->index];
-
-	clk_disable_unprepare(sport->clk);
-	return 0;
-}
-
-static struct uart_driver esp32_uart_reg;
-static struct console esp32_uart_console = {
-	.name		= DEV_NAME,
-	.write		= esp32_uart_console_write,
-	.device		= uart_console_device,
-	.setup		= esp32_uart_console_setup,
-	.exit		= esp32_uart_console_exit,
-	.flags		= CON_PRINTBUFFER,
-	.index		= -1,
-	.data		= &esp32_uart_reg,
-};
-
-static void esp32_uart_earlycon_putchar(struct uart_port *port, u8 c)
-{
-	esp32_uart_put_char_sync(port, c);
-}
-
-static void esp32_uart_earlycon_write(struct console *con, const char *s,
-				      unsigned int n)
-{
-	struct earlycon_device *dev = con->data;
-
-	uart_console_write(&dev->port, s, n, esp32_uart_earlycon_putchar);
-}
-
-#ifdef CONFIG_CONSOLE_POLL
-static int esp32_uart_earlycon_read(struct console *con, char *s, unsigned int n)
-{
-	struct earlycon_device *dev = con->data;
-	unsigned int num_read = 0;
-
-	while (num_read < n) {
-		int c = esp32_uart_poll_get_char(&dev->port);
-
-		if (c == NO_POLL_CHAR)
-			break;
-		s[num_read++] = c;
-	}
-	return num_read;
-}
-#endif
-
-static int __init esp32xx_uart_early_console_setup(struct earlycon_device *device,
-						   const char *options)
-{
-	if (!device->port.membase)
-		return -ENODEV;
-
-	device->con->write = esp32_uart_earlycon_write;
-#ifdef CONFIG_CONSOLE_POLL
-	device->con->read = esp32_uart_earlycon_read;
-#endif
-	if (device->port.uartclk != BASE_BAUD * 16)
-		esp32_uart_set_baud(&device->port, device->baud);
-
-	return 0;
-}
-
-static int __init esp32_uart_early_console_setup(struct earlycon_device *device,
-						 const char *options)
-{
-	device->port.private_data = (void *)&esp32_variant;
-
-	return esp32xx_uart_early_console_setup(device, options);
-}
-
-OF_EARLYCON_DECLARE(esp32uart, "esp,esp32-uart",
-		    esp32_uart_early_console_setup);
-
-static int __init esp32s3_uart_early_console_setup(struct earlycon_device *device,
-						   const char *options)
-{
-	device->port.private_data = (void *)&esp32s3_variant;
-
-	return esp32xx_uart_early_console_setup(device, options);
-}
-
-OF_EARLYCON_DECLARE(esp32s3uart, "esp,esp32s3-uart",
-		    esp32s3_uart_early_console_setup);
-
-static struct uart_driver esp32_uart_reg = {
-	.owner		= THIS_MODULE,
-	.driver_name	= DRIVER_NAME,
-	.dev_name	= DEV_NAME,
-	.nr		= ARRAY_SIZE(esp32_uart_ports),
-	.cons		= &esp32_uart_console,
-};
-
-static int esp32_uart_probe(struct platform_device *pdev)
-{
-	struct device_node *np = pdev->dev.of_node;
-	struct uart_port *port;
-	struct esp32_port *sport;
-	struct resource *res;
-	int ret;
-
-	sport = devm_kzalloc(&pdev->dev, sizeof(*sport), GFP_KERNEL);
-	if (!sport)
-		return -ENOMEM;
-
-	port = &sport->port;
-
-	ret = of_alias_get_id(np, "serial");
-	if (ret < 0) {
-		dev_err(&pdev->dev, "failed to get alias id, errno %d\n", ret);
-		return ret;
-	}
-	if (ret >= UART_NR) {
-		dev_err(&pdev->dev, "driver limited to %d serial ports\n", UART_NR);
-		return -ENOMEM;
-	}
-
-	port->line = ret;
-
-	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	if (!res)
-		return -ENODEV;
-
-	port->mapbase = res->start;
-	port->membase = devm_ioremap_resource(&pdev->dev, res);
-	if (IS_ERR(port->membase))
-		return PTR_ERR(port->membase);
-
-	sport->clk = devm_clk_get(&pdev->dev, NULL);
-	if (IS_ERR(sport->clk))
-		return PTR_ERR(sport->clk);
-
-	port->uartclk = clk_get_rate(sport->clk);
-	port->dev = &pdev->dev;
-	port->type = PORT_GENERIC;
-	port->iotype = UPIO_MEM;
-	port->irq = platform_get_irq(pdev, 0);
-	port->ops = &esp32_uart_pops;
-	port->flags = UPF_BOOT_AUTOCONF;
-	port->has_sysrq = 1;
-	port->fifosize = ESP32_UART_TX_FIFO_SIZE;
-	port->private_data = (void *)device_get_match_data(&pdev->dev);
-
-	esp32_uart_ports[port->line] = sport;
-
-	platform_set_drvdata(pdev, port);
-
-	return uart_add_one_port(&esp32_uart_reg, port);
-}
-
-static void esp32_uart_remove(struct platform_device *pdev)
-{
-	struct uart_port *port = platform_get_drvdata(pdev);
-
-	uart_remove_one_port(&esp32_uart_reg, port);
-}
-
-
-static struct platform_driver esp32_uart_driver = {
-	.probe		= esp32_uart_probe,
-	.remove		= esp32_uart_remove,
-	.driver		= {
-		.name	= DRIVER_NAME,
-		.of_match_table	= esp32_uart_dt_ids,
-	},
-};
-
-static int __init esp32_uart_init(void)
-{
-	int ret;
-
-	ret = uart_register_driver(&esp32_uart_reg);
-	if (ret)
-		return ret;
-
-	ret = platform_driver_register(&esp32_uart_driver);
-	if (ret)
-		uart_unregister_driver(&esp32_uart_reg);
-
-	return ret;
-}
-
-static void __exit esp32_uart_exit(void)
-{
-	platform_driver_unregister(&esp32_uart_driver);
-	uart_unregister_driver(&esp32_uart_reg);
-}
-
-module_init(esp32_uart_init);
-module_exit(esp32_uart_exit);
-
-MODULE_AUTHOR("Max Filippov <jcmvbkbc@gmail.com>");
-MODULE_DESCRIPTION("Espressif ESP32 UART support");
-MODULE_LICENSE("GPL");
-- 
2.51.2


^ permalink raw reply related

* Re: [PATCH] tty: ipwireless: Fix use-after-free in tasklet during device removal
From: Greg KH @ 2026-03-09 11:25 UTC (permalink / raw)
  To: Qingfang Deng
  Cc: David Sterba, Jiri Slaby, Jiri Kosina, duoming, linux-serial,
	linux-kernel, dsterba, kuba, alexander.deucher, akpm, pkshih,
	tglx, mingo
In-Reply-To: <20260309111827.550-1-dqfext@gmail.com>

On Mon, Mar 09, 2026 at 07:18:18PM +0800, Qingfang Deng wrote:
> Hi, David
> 
> On Wed, 18 Feb 2026 16:23:30 +0100, David Sterba wrote:
> > On Tue, Feb 17, 2026 at 09:03:25AM +0100, Jiri Slaby wrote:
> > > Hi,
> > > 
> > > On 09. 02. 26, 11:21, David Sterba wrote:
> > > > On Sun, Feb 08, 2026 at 06:25:38PM +0100, Jiri Kosina wrote:
> > > >> On Sun, 8 Feb 2026, Greg KH wrote:
> > > >>
> > > >>>> I don't have the real hardware. In order to reproduce the bug, I simulate
> > > >>>> the IPWireless PCMCIA card in the qemu by allocating and configuring the
> > > >>>> necessary resources(I/O ports, memory regions, interrupts and so on) to
> > > >>>> correspond with the hardware expected by the driver in the initialization
> > > >>>> code of the virtual device.
> > > >>>
> > > >>> I wonder if this device even is still around, given that pcmcia is all
> > > >>> but dead for a very long time.
> > > >>
> > > >> I doubt that this device is still around anywhere where reasonably new
> > > >> kernels (including LTS) would matter.
> > > >>
> > > >> I don't think I've seen this device (which was back then donated to me by
> > > >> T-Mobile CZ in order to get it supported in Linux, and I am not sure how
> > > >> much global adoption it got afterwards) for, let's say, past 15 years :)
> > > >>
> > > >> I think (let's see what David, ho took the maintainership over for me
> > > >> afterwards, has to say) we'd better deprecate and drop the whole thing,
> > > >> rather than trying to pretend that it's still actively being taken care
> > > >> of.
> > > > 
> > > > https://lore.kernel.org/all/20230223172403.GW10580@suse.cz/ last time
> > > > the question of keeping the driver was asked (2023). Back then I was
> > > > able to find the cards on second hand market but now I can't on a local
> > > > market and there's exactly one hit on global eBay.
> > > > 
> > > > Local linux related or telco support forums seem to mention the driver
> > > > until 2011 (root.cz, abclinuxu.cz, t-mobile.cz). It does not prove
> > > > nobody is using it but I think the chances are quite low to justify
> > > > keeping the driver.  It is simple enough to be built as an external
> > > > module eventually, I can help with that in case somebody really needs
> > > > that.
> > > 
> > > So, would you want to submit the removal? Or anyone else, if you don't 
> > > want to lose time with this? (I can do that, if noone wants to.)
> > 
> > Let me do it after rc1 so I can finish the journey of this driver which
> > was my first nontrivial contribution to linux kernel.
> 
> I found a memory leak in the driver:
> 
> - https://lore.kernel.org/linux-serial/20260306034058.386747-1-dqfext@gmail.com/T/
> 
> The bug has been present since the first version of this driver, but it
> seems no one has noticed it until now. So I believe the driver has not
> been actively used.

A small memory leak like that will probably not be noticed at all, so I
don't think that shows how many people are using the driver, sorry.

greg k-h

^ permalink raw reply

* Re: [PATCH] serial: remove drivers for espressif esp32
From: Greg KH @ 2026-03-09 11:24 UTC (permalink / raw)
  To: Julian Braha
  Cc: jcmvbkbc, rdunlap, ilpo.jarvinen, conor+dt, robh+dt, jirislaby,
	linux-serial, devicetree, linux-kernel
In-Reply-To: <20260309111848.1516412-1-julianbraha@gmail.com>

On Mon, Mar 09, 2026 at 11:18:48AM +0000, Julian Braha wrote:
> Following the conversation here:
> https://lore.kernel.org/all/20260308131412.1102749-1-julianbraha@gmail.com/
> 
> There was an agreement to delete these drivers for now.

As this is going to be in the changelog, perhaps summarize that
agreement and write more here?

thanks,

greg k-h

^ permalink raw reply

* [PATCH] serial: remove drivers for espressif esp32
From: Julian Braha @ 2026-03-09 11:18 UTC (permalink / raw)
  To: gregkh, jcmvbkbc
  Cc: rdunlap, ilpo.jarvinen, conor+dt, robh+dt, jirislaby,
	linux-serial, devicetree, linux-kernel, Julian Braha

Following the conversation here:
https://lore.kernel.org/all/20260308131412.1102749-1-julianbraha@gmail.com/

There was an agreement to delete these drivers for now.

Signed-off-by: Julian Braha <julianbraha@gmail.com>
---
 drivers/tty/serial/Kconfig      |  26 --
 drivers/tty/serial/Makefile     |   2 -
 drivers/tty/serial/esp32_acm.c  | 459 -------------------
 drivers/tty/serial/esp32_uart.c | 779 --------------------------------
 4 files changed, 1266 deletions(-)
 delete mode 100644 drivers/tty/serial/esp32_acm.c
 delete mode 100644 drivers/tty/serial/esp32_uart.c

diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index f86775cfdcc9..686e7fb073b8 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -1593,32 +1593,6 @@ config SERIAL_NUVOTON_MA35D1_CONSOLE
 	  but you can alter that using a kernel command line option such as
 	  "console=ttyNVTx".
 
-config SERIAL_ESP32
-	tristate "Espressif ESP32 UART support"
-	depends on XTENSA_PLATFORM_ESP32 || (COMPILE_TEST && OF)
-	select SERIAL_CORE
-	select SERIAL_CORE_CONSOLE
-	select SERIAL_EARLYCON
-	help
-	  Driver for the UART controllers of the Espressif ESP32xx SoCs.
-	  When earlycon option is enabled the following kernel command line
-	  snippets may be used:
-	    earlycon=esp32s3uart,mmio32,0x60000000,115200n8,40000000
-	    earlycon=esp32uart,mmio32,0x3ff40000,115200n8
-
-config SERIAL_ESP32_ACM
-	tristate "Espressif ESP32 USB ACM gadget support"
-	depends on XTENSA_PLATFORM_ESP32 || (COMPILE_TEST && OF)
-	select SERIAL_CORE
-	select SERIAL_CORE_CONSOLE
-	select SERIAL_EARLYCON
-	help
-	  Driver for the CDC ACM gadget controller of the Espressif ESP32S3
-	  SoCs that share separate USB controller with the JTAG adapter.
-	  When earlycon option is enabled the following kernel command line
-	  snippet may be used:
-	    earlycon=esp32s3acm,mmio32,0x60038000
-
 endmenu
 
 config SERIAL_MCTRL_GPIO
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index a2ccbc508ec5..bba7b21a4a1d 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -37,8 +37,6 @@ obj-$(CONFIG_SERIAL_CLPS711X)		+= clps711x.o
 obj-$(CONFIG_SERIAL_CPM)		+= cpm_uart.o
 obj-$(CONFIG_SERIAL_CONEXANT_DIGICOLOR)	+= digicolor-usart.o
 obj-$(CONFIG_SERIAL_DZ)			+= dz.o
-obj-$(CONFIG_SERIAL_ESP32)		+= esp32_uart.o
-obj-$(CONFIG_SERIAL_ESP32_ACM)		+= esp32_acm.o
 obj-$(CONFIG_SERIAL_FSL_LINFLEXUART)	+= fsl_linflexuart.o
 obj-$(CONFIG_SERIAL_FSL_LPUART)		+= fsl_lpuart.o
 obj-$(CONFIG_SERIAL_ICOM)		+= icom.o
diff --git a/drivers/tty/serial/esp32_acm.c b/drivers/tty/serial/esp32_acm.c
deleted file mode 100644
index bb7cc65427f0..000000000000
--- a/drivers/tty/serial/esp32_acm.c
+++ /dev/null
@@ -1,459 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include <linux/bitfield.h>
-#include <linux/bits.h>
-#include <linux/console.h>
-#include <linux/delay.h>
-#include <linux/io.h>
-#include <linux/irq.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/platform_device.h>
-#include <linux/serial_core.h>
-#include <linux/slab.h>
-#include <linux/tty_flip.h>
-#include <asm/serial.h>
-
-#define DRIVER_NAME	"esp32s3-acm"
-#define DEV_NAME	"ttyGS"
-#define UART_NR		4
-
-#define ESP32S3_ACM_TX_FIFO_SIZE	64
-
-#define USB_SERIAL_JTAG_EP1_REG		0x00
-#define USB_SERIAL_JTAG_EP1_CONF_REG	0x04
-#define USB_SERIAL_JTAG_WR_DONE				BIT(0)
-#define USB_SERIAL_JTAG_SERIAL_IN_EP_DATA_FREE		BIT(1)
-#define USB_SERIAL_JTAG_INT_ST_REG	0x0c
-#define USB_SERIAL_JTAG_SERIAL_OUT_RECV_PKT_INT_ST	BIT(2)
-#define USB_SERIAL_JTAG_SERIAL_IN_EMPTY_INT_ST		BIT(3)
-#define USB_SERIAL_JTAG_INT_ENA_REG	0x10
-#define USB_SERIAL_JTAG_SERIAL_OUT_RECV_PKT_INT_ENA	BIT(2)
-#define USB_SERIAL_JTAG_SERIAL_IN_EMPTY_INT_ENA		BIT(3)
-#define USB_SERIAL_JTAG_INT_CLR_REG	0x14
-#define USB_SERIAL_JTAG_IN_EP1_ST_REG	0x2c
-#define USB_SERIAL_JTAG_IN_EP1_WR_ADDR			GENMASK(8, 2)
-#define USB_SERIAL_JTAG_OUT_EP1_ST_REG	0x3c
-#define USB_SERIAL_JTAG_OUT_EP1_REC_DATA_CNT		GENMASK(22, 16)
-
-static const struct of_device_id esp32s3_acm_dt_ids[] = {
-	{
-		.compatible = "esp,esp32s3-acm",
-	}, { /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(of, esp32s3_acm_dt_ids);
-
-static struct uart_port *esp32s3_acm_ports[UART_NR];
-
-static void esp32s3_acm_write(struct uart_port *port, unsigned long reg, u32 v)
-{
-	writel(v, port->membase + reg);
-}
-
-static u32 esp32s3_acm_read(struct uart_port *port, unsigned long reg)
-{
-	return readl(port->membase + reg);
-}
-
-static u32 esp32s3_acm_tx_fifo_free(struct uart_port *port)
-{
-	u32 status = esp32s3_acm_read(port, USB_SERIAL_JTAG_EP1_CONF_REG);
-
-	return status & USB_SERIAL_JTAG_SERIAL_IN_EP_DATA_FREE;
-}
-
-static u32 esp32s3_acm_tx_fifo_cnt(struct uart_port *port)
-{
-	u32 status = esp32s3_acm_read(port, USB_SERIAL_JTAG_IN_EP1_ST_REG);
-
-	return FIELD_GET(USB_SERIAL_JTAG_IN_EP1_WR_ADDR, status);
-}
-
-static u32 esp32s3_acm_rx_fifo_cnt(struct uart_port *port)
-{
-	u32 status = esp32s3_acm_read(port, USB_SERIAL_JTAG_OUT_EP1_ST_REG);
-
-	return FIELD_GET(USB_SERIAL_JTAG_OUT_EP1_REC_DATA_CNT, status);
-}
-
-/* return TIOCSER_TEMT when transmitter is not busy */
-static unsigned int esp32s3_acm_tx_empty(struct uart_port *port)
-{
-	return esp32s3_acm_tx_fifo_cnt(port) == 0 ? TIOCSER_TEMT : 0;
-}
-
-static void esp32s3_acm_set_mctrl(struct uart_port *port, unsigned int mctrl)
-{
-}
-
-static unsigned int esp32s3_acm_get_mctrl(struct uart_port *port)
-{
-	return TIOCM_CAR;
-}
-
-static void esp32s3_acm_stop_tx(struct uart_port *port)
-{
-	u32 int_ena;
-
-	int_ena = esp32s3_acm_read(port, USB_SERIAL_JTAG_INT_ENA_REG);
-	int_ena &= ~USB_SERIAL_JTAG_SERIAL_IN_EMPTY_INT_ENA;
-	esp32s3_acm_write(port, USB_SERIAL_JTAG_INT_ENA_REG, int_ena);
-}
-
-static void esp32s3_acm_rxint(struct uart_port *port)
-{
-	struct tty_port *tty_port = &port->state->port;
-	u32 rx_fifo_cnt = esp32s3_acm_rx_fifo_cnt(port);
-	unsigned long flags;
-	u32 i;
-
-	if (!rx_fifo_cnt)
-		return;
-
-	spin_lock_irqsave(&port->lock, flags);
-
-	for (i = 0; i < rx_fifo_cnt; ++i) {
-		u32 rx = esp32s3_acm_read(port, USB_SERIAL_JTAG_EP1_REG);
-
-		++port->icount.rx;
-		tty_insert_flip_char(tty_port, rx, TTY_NORMAL);
-	}
-	spin_unlock_irqrestore(&port->lock, flags);
-
-	tty_flip_buffer_push(tty_port);
-}
-
-static void esp32s3_acm_push(struct uart_port *port)
-{
-	if (esp32s3_acm_tx_fifo_free(port))
-		esp32s3_acm_write(port, USB_SERIAL_JTAG_EP1_CONF_REG,
-				  USB_SERIAL_JTAG_WR_DONE);
-}
-
-static void esp32s3_acm_put_char(struct uart_port *port, u8 c)
-{
-	esp32s3_acm_write(port, USB_SERIAL_JTAG_EP1_REG, c);
-}
-
-static void esp32s3_acm_put_char_sync(struct uart_port *port, u8 c)
-{
-	unsigned long timeout = jiffies + HZ;
-
-	while (!esp32s3_acm_tx_fifo_free(port)) {
-		if (time_after(jiffies, timeout)) {
-			dev_warn(port->dev, "timeout waiting for TX FIFO\n");
-			return;
-		}
-		cpu_relax();
-	}
-	esp32s3_acm_put_char(port, c);
-	esp32s3_acm_push(port);
-}
-
-static void esp32s3_acm_transmit_buffer(struct uart_port *port)
-{
-	u32 tx_fifo_used;
-	unsigned int pending;
-	u8 ch;
-
-	if (!esp32s3_acm_tx_fifo_free(port))
-		return;
-
-	tx_fifo_used = esp32s3_acm_tx_fifo_cnt(port);
-	pending = uart_port_tx_limited(port, ch,
-				       ESP32S3_ACM_TX_FIFO_SIZE - tx_fifo_used,
-				       true, esp32s3_acm_put_char(port, ch),
-				       ({}));
-	if (pending) {
-		u32 int_ena;
-
-		int_ena = esp32s3_acm_read(port, USB_SERIAL_JTAG_INT_ENA_REG);
-		int_ena |= USB_SERIAL_JTAG_SERIAL_IN_EMPTY_INT_ENA;
-		esp32s3_acm_write(port, USB_SERIAL_JTAG_INT_ENA_REG, int_ena);
-	}
-	esp32s3_acm_push(port);
-}
-
-static void esp32s3_acm_txint(struct uart_port *port)
-{
-	esp32s3_acm_transmit_buffer(port);
-}
-
-static irqreturn_t esp32s3_acm_int(int irq, void *dev_id)
-{
-	struct uart_port *port = dev_id;
-	u32 status;
-
-	status = esp32s3_acm_read(port, USB_SERIAL_JTAG_INT_ST_REG);
-	esp32s3_acm_write(port, USB_SERIAL_JTAG_INT_CLR_REG, status);
-
-	if (status & USB_SERIAL_JTAG_SERIAL_OUT_RECV_PKT_INT_ST)
-		esp32s3_acm_rxint(port);
-	if (status & USB_SERIAL_JTAG_SERIAL_IN_EMPTY_INT_ST)
-		esp32s3_acm_txint(port);
-
-	return IRQ_RETVAL(status);
-}
-
-static void esp32s3_acm_start_tx(struct uart_port *port)
-{
-	esp32s3_acm_transmit_buffer(port);
-}
-
-static void esp32s3_acm_stop_rx(struct uart_port *port)
-{
-	u32 int_ena;
-
-	int_ena = esp32s3_acm_read(port, USB_SERIAL_JTAG_INT_ENA_REG);
-	int_ena &= ~USB_SERIAL_JTAG_SERIAL_OUT_RECV_PKT_INT_ENA;
-	esp32s3_acm_write(port, USB_SERIAL_JTAG_INT_ENA_REG, int_ena);
-}
-
-static int esp32s3_acm_startup(struct uart_port *port)
-{
-	int ret;
-
-	ret = request_irq(port->irq, esp32s3_acm_int, 0, DRIVER_NAME, port);
-	if (ret)
-		return ret;
-	esp32s3_acm_write(port, USB_SERIAL_JTAG_INT_ENA_REG,
-			  USB_SERIAL_JTAG_SERIAL_OUT_RECV_PKT_INT_ENA);
-
-	return 0;
-}
-
-static void esp32s3_acm_shutdown(struct uart_port *port)
-{
-	esp32s3_acm_write(port, USB_SERIAL_JTAG_INT_ENA_REG, 0);
-	free_irq(port->irq, port);
-}
-
-static void esp32s3_acm_set_termios(struct uart_port *port,
-				    struct ktermios *termios,
-				    const struct ktermios *old)
-{
-}
-
-static const char *esp32s3_acm_type(struct uart_port *port)
-{
-	return "ESP32S3 ACM";
-}
-
-/* configure/auto-configure the port */
-static void esp32s3_acm_config_port(struct uart_port *port, int flags)
-{
-	if (flags & UART_CONFIG_TYPE)
-		port->type = PORT_GENERIC;
-}
-
-#ifdef CONFIG_CONSOLE_POLL
-static void esp32s3_acm_poll_put_char(struct uart_port *port, unsigned char c)
-{
-	esp32s3_acm_put_char_sync(port, c);
-}
-
-static int esp32s3_acm_poll_get_char(struct uart_port *port)
-{
-	if (esp32s3_acm_rx_fifo_cnt(port))
-		return esp32s3_acm_read(port, USB_SERIAL_JTAG_EP1_REG);
-	else
-		return NO_POLL_CHAR;
-}
-#endif
-
-static const struct uart_ops esp32s3_acm_pops = {
-	.tx_empty	= esp32s3_acm_tx_empty,
-	.set_mctrl	= esp32s3_acm_set_mctrl,
-	.get_mctrl	= esp32s3_acm_get_mctrl,
-	.stop_tx	= esp32s3_acm_stop_tx,
-	.start_tx	= esp32s3_acm_start_tx,
-	.stop_rx	= esp32s3_acm_stop_rx,
-	.startup	= esp32s3_acm_startup,
-	.shutdown	= esp32s3_acm_shutdown,
-	.set_termios	= esp32s3_acm_set_termios,
-	.type		= esp32s3_acm_type,
-	.config_port	= esp32s3_acm_config_port,
-#ifdef CONFIG_CONSOLE_POLL
-	.poll_put_char	= esp32s3_acm_poll_put_char,
-	.poll_get_char	= esp32s3_acm_poll_get_char,
-#endif
-};
-
-static void esp32s3_acm_string_write(struct uart_port *port, const char *s,
-				     unsigned int count)
-{
-	uart_console_write(port, s, count, esp32s3_acm_put_char_sync);
-}
-
-static void
-esp32s3_acm_console_write(struct console *co, const char *s, unsigned int count)
-{
-	struct uart_port *port = esp32s3_acm_ports[co->index];
-	unsigned long flags;
-	bool locked = true;
-
-	if (port->sysrq)
-		locked = false;
-	else if (oops_in_progress)
-		locked = spin_trylock_irqsave(&port->lock, flags);
-	else
-		spin_lock_irqsave(&port->lock, flags);
-
-	esp32s3_acm_string_write(port, s, count);
-
-	if (locked)
-		spin_unlock_irqrestore(&port->lock, flags);
-}
-
-static struct uart_driver esp32s3_acm_reg;
-static struct console esp32s3_acm_console = {
-	.name		= DEV_NAME,
-	.write		= esp32s3_acm_console_write,
-	.device		= uart_console_device,
-	.flags		= CON_PRINTBUFFER,
-	.index		= -1,
-	.data		= &esp32s3_acm_reg,
-};
-
-static void esp32s3_acm_earlycon_write(struct console *con, const char *s,
-				      unsigned int n)
-{
-	struct earlycon_device *dev = con->data;
-
-	uart_console_write(&dev->port, s, n, esp32s3_acm_put_char_sync);
-}
-
-#ifdef CONFIG_CONSOLE_POLL
-static int esp32s3_acm_earlycon_read(struct console *con, char *s, unsigned int n)
-{
-	struct earlycon_device *dev = con->data;
-	unsigned int num_read = 0;
-
-	while (num_read < n) {
-		int c = esp32s3_acm_poll_get_char(&dev->port);
-
-		if (c == NO_POLL_CHAR)
-			break;
-		s[num_read++] = c;
-	}
-	return num_read;
-}
-#endif
-
-static int __init esp32s3_acm_early_console_setup(struct earlycon_device *device,
-						   const char *options)
-{
-	if (!device->port.membase)
-		return -ENODEV;
-
-	device->con->write = esp32s3_acm_earlycon_write;
-#ifdef CONFIG_CONSOLE_POLL
-	device->con->read = esp32s3_acm_earlycon_read;
-#endif
-	return 0;
-}
-
-OF_EARLYCON_DECLARE(esp32s3acm, "esp,esp32s3-acm",
-		    esp32s3_acm_early_console_setup);
-
-static struct uart_driver esp32s3_acm_reg = {
-	.owner		= THIS_MODULE,
-	.driver_name	= DRIVER_NAME,
-	.dev_name	= DEV_NAME,
-	.nr		= ARRAY_SIZE(esp32s3_acm_ports),
-	.cons		= &esp32s3_acm_console,
-};
-
-static int esp32s3_acm_probe(struct platform_device *pdev)
-{
-	struct device_node *np = pdev->dev.of_node;
-	struct uart_port *port;
-	struct resource *res;
-	int ret;
-
-	port = devm_kzalloc(&pdev->dev, sizeof(*port), GFP_KERNEL);
-	if (!port)
-		return -ENOMEM;
-
-	ret = of_alias_get_id(np, "serial");
-	if (ret < 0) {
-		dev_err(&pdev->dev, "failed to get alias id, errno %d\n", ret);
-		return ret;
-	}
-	if (ret >= UART_NR) {
-		dev_err(&pdev->dev, "driver limited to %d serial ports\n",
-			UART_NR);
-		return -ENOMEM;
-	}
-
-	port->line = ret;
-
-	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	if (!res)
-		return -ENODEV;
-
-	port->mapbase = res->start;
-	port->membase = devm_ioremap_resource(&pdev->dev, res);
-	if (IS_ERR(port->membase))
-		return PTR_ERR(port->membase);
-
-	port->dev = &pdev->dev;
-	port->type = PORT_GENERIC;
-	port->iotype = UPIO_MEM;
-	port->irq = platform_get_irq(pdev, 0);
-	port->ops = &esp32s3_acm_pops;
-	port->flags = UPF_BOOT_AUTOCONF;
-	port->has_sysrq = 1;
-	port->fifosize = ESP32S3_ACM_TX_FIFO_SIZE;
-
-	esp32s3_acm_ports[port->line] = port;
-
-	platform_set_drvdata(pdev, port);
-
-	return uart_add_one_port(&esp32s3_acm_reg, port);
-}
-
-static void esp32s3_acm_remove(struct platform_device *pdev)
-{
-	struct uart_port *port = platform_get_drvdata(pdev);
-
-	uart_remove_one_port(&esp32s3_acm_reg, port);
-}
-
-
-static struct platform_driver esp32s3_acm_driver = {
-	.probe		= esp32s3_acm_probe,
-	.remove		= esp32s3_acm_remove,
-	.driver		= {
-		.name	= DRIVER_NAME,
-		.of_match_table	= esp32s3_acm_dt_ids,
-	},
-};
-
-static int __init esp32s3_acm_init(void)
-{
-	int ret;
-
-	ret = uart_register_driver(&esp32s3_acm_reg);
-	if (ret)
-		return ret;
-
-	ret = platform_driver_register(&esp32s3_acm_driver);
-	if (ret)
-		uart_unregister_driver(&esp32s3_acm_reg);
-
-	return ret;
-}
-
-static void __exit esp32s3_acm_exit(void)
-{
-	platform_driver_unregister(&esp32s3_acm_driver);
-	uart_unregister_driver(&esp32s3_acm_reg);
-}
-
-module_init(esp32s3_acm_init);
-module_exit(esp32s3_acm_exit);
-
-MODULE_AUTHOR("Max Filippov <jcmvbkbc@gmail.com>");
-MODULE_DESCRIPTION("Espressif ESP32 USB ACM gadget support");
-MODULE_LICENSE("GPL");
diff --git a/drivers/tty/serial/esp32_uart.c b/drivers/tty/serial/esp32_uart.c
deleted file mode 100644
index 667c2198a03a..000000000000
--- a/drivers/tty/serial/esp32_uart.c
+++ /dev/null
@@ -1,779 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include <linux/bitfield.h>
-#include <linux/bits.h>
-#include <linux/clk.h>
-#include <linux/console.h>
-#include <linux/delay.h>
-#include <linux/io.h>
-#include <linux/irq.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/platform_device.h>
-#include <linux/property.h>
-#include <linux/serial_core.h>
-#include <linux/slab.h>
-#include <linux/tty_flip.h>
-#include <asm/serial.h>
-
-#define DRIVER_NAME	"esp32-uart"
-#define DEV_NAME	"ttyS"
-#define UART_NR		3
-
-#define ESP32_UART_TX_FIFO_SIZE	127
-#define ESP32_UART_RX_FIFO_SIZE	127
-
-#define UART_FIFO_REG			0x00
-#define UART_INT_RAW_REG		0x04
-#define UART_INT_ST_REG			0x08
-#define UART_INT_ENA_REG		0x0c
-#define UART_INT_CLR_REG		0x10
-#define UART_RXFIFO_FULL_INT			BIT(0)
-#define UART_TXFIFO_EMPTY_INT			BIT(1)
-#define UART_BRK_DET_INT			BIT(7)
-#define UART_CLKDIV_REG			0x14
-#define ESP32_UART_CLKDIV			GENMASK(19, 0)
-#define ESP32S3_UART_CLKDIV			GENMASK(11, 0)
-#define UART_CLKDIV_SHIFT			0
-#define UART_CLKDIV_FRAG			GENMASK(23, 20)
-#define UART_STATUS_REG			0x1c
-#define ESP32_UART_RXFIFO_CNT			GENMASK(7, 0)
-#define ESP32S3_UART_RXFIFO_CNT			GENMASK(9, 0)
-#define UART_RXFIFO_CNT_SHIFT			0
-#define UART_DSRN				BIT(13)
-#define UART_CTSN				BIT(14)
-#define ESP32_UART_TXFIFO_CNT			GENMASK(23, 16)
-#define ESP32S3_UART_TXFIFO_CNT			GENMASK(25, 16)
-#define UART_TXFIFO_CNT_SHIFT			16
-#define UART_CONF0_REG			0x20
-#define UART_PARITY				BIT(0)
-#define UART_PARITY_EN				BIT(1)
-#define UART_BIT_NUM				GENMASK(3, 2)
-#define UART_BIT_NUM_5				0
-#define UART_BIT_NUM_6				1
-#define UART_BIT_NUM_7				2
-#define UART_BIT_NUM_8				3
-#define UART_STOP_BIT_NUM			GENMASK(5, 4)
-#define UART_STOP_BIT_NUM_1			1
-#define UART_STOP_BIT_NUM_2			3
-#define UART_SW_RTS				BIT(6)
-#define UART_SW_DTR				BIT(7)
-#define UART_LOOPBACK				BIT(14)
-#define UART_TX_FLOW_EN				BIT(15)
-#define UART_RTS_INV				BIT(23)
-#define UART_DTR_INV				BIT(24)
-#define UART_CONF1_REG			0x24
-#define UART_RXFIFO_FULL_THRHD_SHIFT		0
-#define ESP32_UART_TXFIFO_EMPTY_THRHD_SHIFT	8
-#define ESP32S3_UART_TXFIFO_EMPTY_THRHD_SHIFT	10
-#define ESP32_UART_RX_FLOW_EN			BIT(23)
-#define ESP32S3_UART_RX_FLOW_EN			BIT(22)
-#define ESP32S3_UART_CLK_CONF_REG	0x78
-#define ESP32S3_UART_SCLK_DIV_B			GENMASK(5, 0)
-#define ESP32S3_UART_SCLK_DIV_A			GENMASK(11, 6)
-#define ESP32S3_UART_SCLK_DIV_NUM		GENMASK(19, 12)
-#define ESP32S3_UART_SCLK_SEL			GENMASK(21, 20)
-#define APB_CLK					1
-#define RC_FAST_CLK				2
-#define XTAL_CLK				3
-#define ESP32S3_UART_SCLK_EN			BIT(22)
-#define ESP32S3_UART_RST_CORE			BIT(23)
-#define ESP32S3_UART_TX_SCLK_EN			BIT(24)
-#define ESP32S3_UART_RX_SCLK_EN			BIT(25)
-#define ESP32S3_UART_TX_RST_CORE		BIT(26)
-#define ESP32S3_UART_RX_RST_CORE		BIT(27)
-
-#define ESP32S3_UART_CLK_CONF_DEFAULT \
-	(ESP32S3_UART_RX_SCLK_EN | \
-	 ESP32S3_UART_TX_SCLK_EN | \
-	 ESP32S3_UART_SCLK_EN | \
-	 FIELD_PREP(ESP32S3_UART_SCLK_SEL, XTAL_CLK))
-
-struct esp32_port {
-	struct uart_port port;
-	struct clk *clk;
-};
-
-struct esp32_uart_variant {
-	u32 clkdiv_mask;
-	u32 rxfifo_cnt_mask;
-	u32 txfifo_cnt_mask;
-	u32 txfifo_empty_thrhd_shift;
-	u32 rx_flow_en;
-	const char *type;
-	bool has_clkconf;
-};
-
-static const struct esp32_uart_variant esp32_variant = {
-	.clkdiv_mask = ESP32_UART_CLKDIV,
-	.rxfifo_cnt_mask = ESP32_UART_RXFIFO_CNT,
-	.txfifo_cnt_mask = ESP32_UART_TXFIFO_CNT,
-	.txfifo_empty_thrhd_shift = ESP32_UART_TXFIFO_EMPTY_THRHD_SHIFT,
-	.rx_flow_en = ESP32_UART_RX_FLOW_EN,
-	.type = "ESP32 UART",
-};
-
-static const struct esp32_uart_variant esp32s3_variant = {
-	.clkdiv_mask = ESP32S3_UART_CLKDIV,
-	.rxfifo_cnt_mask = ESP32S3_UART_RXFIFO_CNT,
-	.txfifo_cnt_mask = ESP32S3_UART_TXFIFO_CNT,
-	.txfifo_empty_thrhd_shift = ESP32S3_UART_TXFIFO_EMPTY_THRHD_SHIFT,
-	.rx_flow_en = ESP32S3_UART_RX_FLOW_EN,
-	.type = "ESP32S3 UART",
-	.has_clkconf = true,
-};
-
-static const struct of_device_id esp32_uart_dt_ids[] = {
-	{
-		.compatible = "esp,esp32-uart",
-		.data = &esp32_variant,
-	}, {
-		.compatible = "esp,esp32s3-uart",
-		.data = &esp32s3_variant,
-	}, { /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(of, esp32_uart_dt_ids);
-
-static struct esp32_port *esp32_uart_ports[UART_NR];
-
-static const struct esp32_uart_variant *port_variant(struct uart_port *port)
-{
-	return port->private_data;
-}
-
-static void esp32_uart_write(struct uart_port *port, unsigned long reg, u32 v)
-{
-	writel(v, port->membase + reg);
-}
-
-static u32 esp32_uart_read(struct uart_port *port, unsigned long reg)
-{
-	return readl(port->membase + reg);
-}
-
-static u32 esp32_uart_tx_fifo_cnt(struct uart_port *port)
-{
-	u32 status = esp32_uart_read(port, UART_STATUS_REG);
-
-	return (status & port_variant(port)->txfifo_cnt_mask) >> UART_TXFIFO_CNT_SHIFT;
-}
-
-static u32 esp32_uart_rx_fifo_cnt(struct uart_port *port)
-{
-	u32 status = esp32_uart_read(port, UART_STATUS_REG);
-
-	return (status & port_variant(port)->rxfifo_cnt_mask) >> UART_RXFIFO_CNT_SHIFT;
-}
-
-/* return TIOCSER_TEMT when transmitter is not busy */
-static unsigned int esp32_uart_tx_empty(struct uart_port *port)
-{
-	return esp32_uart_tx_fifo_cnt(port) ? 0 : TIOCSER_TEMT;
-}
-
-static void esp32_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
-{
-	u32 conf0 = esp32_uart_read(port, UART_CONF0_REG);
-
-	conf0 &= ~(UART_LOOPBACK |
-		   UART_SW_RTS | UART_RTS_INV |
-		   UART_SW_DTR | UART_DTR_INV);
-
-	if (mctrl & TIOCM_RTS)
-		conf0 |= UART_SW_RTS;
-	if (mctrl & TIOCM_DTR)
-		conf0 |= UART_SW_DTR;
-	if (mctrl & TIOCM_LOOP)
-		conf0 |= UART_LOOPBACK;
-
-	esp32_uart_write(port, UART_CONF0_REG, conf0);
-}
-
-static unsigned int esp32_uart_get_mctrl(struct uart_port *port)
-{
-	u32 status = esp32_uart_read(port, UART_STATUS_REG);
-	unsigned int ret = TIOCM_CAR;
-
-	if (status & UART_DSRN)
-		ret |= TIOCM_DSR;
-	if (status & UART_CTSN)
-		ret |= TIOCM_CTS;
-
-	return ret;
-}
-
-static void esp32_uart_stop_tx(struct uart_port *port)
-{
-	u32 int_ena;
-
-	int_ena = esp32_uart_read(port, UART_INT_ENA_REG);
-	int_ena &= ~UART_TXFIFO_EMPTY_INT;
-	esp32_uart_write(port, UART_INT_ENA_REG, int_ena);
-}
-
-static void esp32_uart_rxint(struct uart_port *port)
-{
-	struct tty_port *tty_port = &port->state->port;
-	u32 rx_fifo_cnt = esp32_uart_rx_fifo_cnt(port);
-	unsigned long flags;
-	u32 i;
-
-	if (!rx_fifo_cnt)
-		return;
-
-	spin_lock_irqsave(&port->lock, flags);
-
-	for (i = 0; i < rx_fifo_cnt; ++i) {
-		u32 rx = esp32_uart_read(port, UART_FIFO_REG);
-
-		if (!rx &&
-		    (esp32_uart_read(port, UART_INT_ST_REG) & UART_BRK_DET_INT)) {
-			esp32_uart_write(port, UART_INT_CLR_REG, UART_BRK_DET_INT);
-			++port->icount.brk;
-			uart_handle_break(port);
-		} else {
-			if (uart_handle_sysrq_char(port, (unsigned char)rx))
-				continue;
-			tty_insert_flip_char(tty_port, rx, TTY_NORMAL);
-			++port->icount.rx;
-		}
-	}
-	spin_unlock_irqrestore(&port->lock, flags);
-
-	tty_flip_buffer_push(tty_port);
-}
-
-static void esp32_uart_put_char(struct uart_port *port, u8 c)
-{
-	esp32_uart_write(port, UART_FIFO_REG, c);
-}
-
-static void esp32_uart_put_char_sync(struct uart_port *port, u8 c)
-{
-	unsigned long timeout = jiffies + HZ;
-
-	while (esp32_uart_tx_fifo_cnt(port) >= ESP32_UART_TX_FIFO_SIZE) {
-		if (time_after(jiffies, timeout)) {
-			dev_warn(port->dev, "timeout waiting for TX FIFO\n");
-			return;
-		}
-		cpu_relax();
-	}
-	esp32_uart_put_char(port, c);
-}
-
-static void esp32_uart_transmit_buffer(struct uart_port *port)
-{
-	u32 tx_fifo_used = esp32_uart_tx_fifo_cnt(port);
-	unsigned int pending;
-	u8 ch;
-
-	if (tx_fifo_used >= ESP32_UART_TX_FIFO_SIZE)
-		return;
-
-	pending = uart_port_tx_limited(port, ch,
-				       ESP32_UART_TX_FIFO_SIZE - tx_fifo_used,
-				       true, esp32_uart_put_char(port, ch),
-				       ({}));
-	if (pending) {
-		u32 int_ena;
-
-		int_ena = esp32_uart_read(port, UART_INT_ENA_REG);
-		int_ena |= UART_TXFIFO_EMPTY_INT;
-		esp32_uart_write(port, UART_INT_ENA_REG, int_ena);
-	}
-}
-
-static void esp32_uart_txint(struct uart_port *port)
-{
-	esp32_uart_transmit_buffer(port);
-}
-
-static irqreturn_t esp32_uart_int(int irq, void *dev_id)
-{
-	struct uart_port *port = dev_id;
-	u32 status;
-
-	status = esp32_uart_read(port, UART_INT_ST_REG);
-
-	if (status & (UART_RXFIFO_FULL_INT | UART_BRK_DET_INT))
-		esp32_uart_rxint(port);
-	if (status & UART_TXFIFO_EMPTY_INT)
-		esp32_uart_txint(port);
-
-	esp32_uart_write(port, UART_INT_CLR_REG, status);
-
-	return IRQ_RETVAL(status);
-}
-
-static void esp32_uart_start_tx(struct uart_port *port)
-{
-	esp32_uart_transmit_buffer(port);
-}
-
-static void esp32_uart_stop_rx(struct uart_port *port)
-{
-	u32 int_ena;
-
-	int_ena = esp32_uart_read(port, UART_INT_ENA_REG);
-	int_ena &= ~UART_RXFIFO_FULL_INT;
-	esp32_uart_write(port, UART_INT_ENA_REG, int_ena);
-}
-
-static int esp32_uart_startup(struct uart_port *port)
-{
-	int ret = 0;
-	unsigned long flags;
-	struct esp32_port *sport = container_of(port, struct esp32_port, port);
-
-	ret = clk_prepare_enable(sport->clk);
-	if (ret)
-		return ret;
-
-	ret = request_irq(port->irq, esp32_uart_int, 0, DRIVER_NAME, port);
-	if (ret) {
-		clk_disable_unprepare(sport->clk);
-		return ret;
-	}
-
-	spin_lock_irqsave(&port->lock, flags);
-	if (port_variant(port)->has_clkconf)
-		esp32_uart_write(port, ESP32S3_UART_CLK_CONF_REG,
-				 ESP32S3_UART_CLK_CONF_DEFAULT);
-	esp32_uart_write(port, UART_CONF1_REG,
-			 (1 << UART_RXFIFO_FULL_THRHD_SHIFT) |
-			 (1 << port_variant(port)->txfifo_empty_thrhd_shift));
-	esp32_uart_write(port, UART_INT_CLR_REG, UART_RXFIFO_FULL_INT | UART_BRK_DET_INT);
-	esp32_uart_write(port, UART_INT_ENA_REG, UART_RXFIFO_FULL_INT | UART_BRK_DET_INT);
-	spin_unlock_irqrestore(&port->lock, flags);
-
-	return ret;
-}
-
-static void esp32_uart_shutdown(struct uart_port *port)
-{
-	struct esp32_port *sport = container_of(port, struct esp32_port, port);
-
-	esp32_uart_write(port, UART_INT_ENA_REG, 0);
-	free_irq(port->irq, port);
-	clk_disable_unprepare(sport->clk);
-}
-
-static bool esp32_uart_set_baud(struct uart_port *port, u32 baud)
-{
-	u32 sclk = port->uartclk;
-	u32 div = sclk / baud;
-
-	if (port_variant(port)->has_clkconf) {
-		u32 sclk_div = div / port_variant(port)->clkdiv_mask;
-
-		if (div > port_variant(port)->clkdiv_mask) {
-			sclk /= (sclk_div + 1);
-			div = sclk / baud;
-		}
-		esp32_uart_write(port, ESP32S3_UART_CLK_CONF_REG,
-				 FIELD_PREP(ESP32S3_UART_SCLK_DIV_NUM, sclk_div) |
-				 ESP32S3_UART_CLK_CONF_DEFAULT);
-	}
-
-	if (div <= port_variant(port)->clkdiv_mask) {
-		u32 frag = (sclk * 16) / baud - div * 16;
-
-		esp32_uart_write(port, UART_CLKDIV_REG,
-				 div | FIELD_PREP(UART_CLKDIV_FRAG, frag));
-		return true;
-	}
-
-	return false;
-}
-
-static void esp32_uart_set_termios(struct uart_port *port,
-				   struct ktermios *termios,
-				   const struct ktermios *old)
-{
-	unsigned long flags;
-	u32 conf0, conf1;
-	u32 baud;
-	const u32 rx_flow_en = port_variant(port)->rx_flow_en;
-	u32 max_div = port_variant(port)->clkdiv_mask;
-
-	termios->c_cflag &= ~CMSPAR;
-
-	if (port_variant(port)->has_clkconf)
-		max_div *= FIELD_MAX(ESP32S3_UART_SCLK_DIV_NUM);
-
-	baud = uart_get_baud_rate(port, termios, old,
-				  port->uartclk / max_div,
-				  port->uartclk / 16);
-
-	spin_lock_irqsave(&port->lock, flags);
-
-	conf0 = esp32_uart_read(port, UART_CONF0_REG);
-	conf0 &= ~(UART_PARITY_EN | UART_PARITY | UART_BIT_NUM | UART_STOP_BIT_NUM);
-
-	conf1 = esp32_uart_read(port, UART_CONF1_REG);
-	conf1 &= ~rx_flow_en;
-
-	if (termios->c_cflag & PARENB) {
-		conf0 |= UART_PARITY_EN;
-		if (termios->c_cflag & PARODD)
-			conf0 |= UART_PARITY;
-	}
-
-	switch (termios->c_cflag & CSIZE) {
-	case CS5:
-		conf0 |= FIELD_PREP(UART_BIT_NUM, UART_BIT_NUM_5);
-		break;
-	case CS6:
-		conf0 |= FIELD_PREP(UART_BIT_NUM, UART_BIT_NUM_6);
-		break;
-	case CS7:
-		conf0 |= FIELD_PREP(UART_BIT_NUM, UART_BIT_NUM_7);
-		break;
-	case CS8:
-		conf0 |= FIELD_PREP(UART_BIT_NUM, UART_BIT_NUM_8);
-		break;
-	}
-
-	if (termios->c_cflag & CSTOPB)
-		conf0 |= FIELD_PREP(UART_STOP_BIT_NUM, UART_STOP_BIT_NUM_2);
-	else
-		conf0 |= FIELD_PREP(UART_STOP_BIT_NUM, UART_STOP_BIT_NUM_1);
-
-	if (termios->c_cflag & CRTSCTS)
-		conf1 |= rx_flow_en;
-
-	esp32_uart_write(port, UART_CONF0_REG, conf0);
-	esp32_uart_write(port, UART_CONF1_REG, conf1);
-
-	if (baud) {
-		esp32_uart_set_baud(port, baud);
-		uart_update_timeout(port, termios->c_cflag, baud);
-	} else {
-		if (esp32_uart_set_baud(port, 115200)) {
-			baud = 115200;
-			tty_termios_encode_baud_rate(termios, baud, baud);
-			uart_update_timeout(port, termios->c_cflag, baud);
-		} else {
-			dev_warn(port->dev,
-				 "unable to set speed to %d baud or the default 115200\n",
-				 baud);
-		}
-	}
-	spin_unlock_irqrestore(&port->lock, flags);
-}
-
-static const char *esp32_uart_type(struct uart_port *port)
-{
-	return port_variant(port)->type;
-}
-
-/* configure/auto-configure the port */
-static void esp32_uart_config_port(struct uart_port *port, int flags)
-{
-	if (flags & UART_CONFIG_TYPE)
-		port->type = PORT_GENERIC;
-}
-
-#ifdef CONFIG_CONSOLE_POLL
-static int esp32_uart_poll_init(struct uart_port *port)
-{
-	struct esp32_port *sport = container_of(port, struct esp32_port, port);
-
-	return clk_prepare_enable(sport->clk);
-}
-
-static void esp32_uart_poll_put_char(struct uart_port *port, unsigned char c)
-{
-	esp32_uart_put_char_sync(port, c);
-}
-
-static int esp32_uart_poll_get_char(struct uart_port *port)
-{
-	if (esp32_uart_rx_fifo_cnt(port))
-		return esp32_uart_read(port, UART_FIFO_REG);
-	else
-		return NO_POLL_CHAR;
-
-}
-#endif
-
-static const struct uart_ops esp32_uart_pops = {
-	.tx_empty	= esp32_uart_tx_empty,
-	.set_mctrl	= esp32_uart_set_mctrl,
-	.get_mctrl	= esp32_uart_get_mctrl,
-	.stop_tx	= esp32_uart_stop_tx,
-	.start_tx	= esp32_uart_start_tx,
-	.stop_rx	= esp32_uart_stop_rx,
-	.startup	= esp32_uart_startup,
-	.shutdown	= esp32_uart_shutdown,
-	.set_termios	= esp32_uart_set_termios,
-	.type		= esp32_uart_type,
-	.config_port	= esp32_uart_config_port,
-#ifdef CONFIG_CONSOLE_POLL
-	.poll_init	= esp32_uart_poll_init,
-	.poll_put_char	= esp32_uart_poll_put_char,
-	.poll_get_char	= esp32_uart_poll_get_char,
-#endif
-};
-
-static void esp32_uart_console_putchar(struct uart_port *port, u8 c)
-{
-	esp32_uart_put_char_sync(port, c);
-}
-
-static void esp32_uart_string_write(struct uart_port *port, const char *s,
-				    unsigned int count)
-{
-	uart_console_write(port, s, count, esp32_uart_console_putchar);
-}
-
-static void
-esp32_uart_console_write(struct console *co, const char *s, unsigned int count)
-{
-	struct esp32_port *sport = esp32_uart_ports[co->index];
-	struct uart_port *port = &sport->port;
-	unsigned long flags;
-	bool locked = true;
-
-	if (port->sysrq)
-		locked = false;
-	else if (oops_in_progress)
-		locked = spin_trylock_irqsave(&port->lock, flags);
-	else
-		spin_lock_irqsave(&port->lock, flags);
-
-	esp32_uart_string_write(port, s, count);
-
-	if (locked)
-		spin_unlock_irqrestore(&port->lock, flags);
-}
-
-static int __init esp32_uart_console_setup(struct console *co, char *options)
-{
-	struct esp32_port *sport;
-	int baud = 115200;
-	int bits = 8;
-	int parity = 'n';
-	int flow = 'n';
-	int ret;
-
-	/*
-	 * check whether an invalid uart number has been specified, and
-	 * if so, search for the first available port that does have
-	 * console support.
-	 */
-	if (co->index == -1 || co->index >= ARRAY_SIZE(esp32_uart_ports))
-		co->index = 0;
-
-	sport = esp32_uart_ports[co->index];
-	if (!sport)
-		return -ENODEV;
-
-	ret = clk_prepare_enable(sport->clk);
-	if (ret)
-		return ret;
-
-	if (options)
-		uart_parse_options(options, &baud, &parity, &bits, &flow);
-
-	return uart_set_options(&sport->port, co, baud, parity, bits, flow);
-}
-
-static int esp32_uart_console_exit(struct console *co)
-{
-	struct esp32_port *sport = esp32_uart_ports[co->index];
-
-	clk_disable_unprepare(sport->clk);
-	return 0;
-}
-
-static struct uart_driver esp32_uart_reg;
-static struct console esp32_uart_console = {
-	.name		= DEV_NAME,
-	.write		= esp32_uart_console_write,
-	.device		= uart_console_device,
-	.setup		= esp32_uart_console_setup,
-	.exit		= esp32_uart_console_exit,
-	.flags		= CON_PRINTBUFFER,
-	.index		= -1,
-	.data		= &esp32_uart_reg,
-};
-
-static void esp32_uart_earlycon_putchar(struct uart_port *port, u8 c)
-{
-	esp32_uart_put_char_sync(port, c);
-}
-
-static void esp32_uart_earlycon_write(struct console *con, const char *s,
-				      unsigned int n)
-{
-	struct earlycon_device *dev = con->data;
-
-	uart_console_write(&dev->port, s, n, esp32_uart_earlycon_putchar);
-}
-
-#ifdef CONFIG_CONSOLE_POLL
-static int esp32_uart_earlycon_read(struct console *con, char *s, unsigned int n)
-{
-	struct earlycon_device *dev = con->data;
-	unsigned int num_read = 0;
-
-	while (num_read < n) {
-		int c = esp32_uart_poll_get_char(&dev->port);
-
-		if (c == NO_POLL_CHAR)
-			break;
-		s[num_read++] = c;
-	}
-	return num_read;
-}
-#endif
-
-static int __init esp32xx_uart_early_console_setup(struct earlycon_device *device,
-						   const char *options)
-{
-	if (!device->port.membase)
-		return -ENODEV;
-
-	device->con->write = esp32_uart_earlycon_write;
-#ifdef CONFIG_CONSOLE_POLL
-	device->con->read = esp32_uart_earlycon_read;
-#endif
-	if (device->port.uartclk != BASE_BAUD * 16)
-		esp32_uart_set_baud(&device->port, device->baud);
-
-	return 0;
-}
-
-static int __init esp32_uart_early_console_setup(struct earlycon_device *device,
-						 const char *options)
-{
-	device->port.private_data = (void *)&esp32_variant;
-
-	return esp32xx_uart_early_console_setup(device, options);
-}
-
-OF_EARLYCON_DECLARE(esp32uart, "esp,esp32-uart",
-		    esp32_uart_early_console_setup);
-
-static int __init esp32s3_uart_early_console_setup(struct earlycon_device *device,
-						   const char *options)
-{
-	device->port.private_data = (void *)&esp32s3_variant;
-
-	return esp32xx_uart_early_console_setup(device, options);
-}
-
-OF_EARLYCON_DECLARE(esp32s3uart, "esp,esp32s3-uart",
-		    esp32s3_uart_early_console_setup);
-
-static struct uart_driver esp32_uart_reg = {
-	.owner		= THIS_MODULE,
-	.driver_name	= DRIVER_NAME,
-	.dev_name	= DEV_NAME,
-	.nr		= ARRAY_SIZE(esp32_uart_ports),
-	.cons		= &esp32_uart_console,
-};
-
-static int esp32_uart_probe(struct platform_device *pdev)
-{
-	struct device_node *np = pdev->dev.of_node;
-	struct uart_port *port;
-	struct esp32_port *sport;
-	struct resource *res;
-	int ret;
-
-	sport = devm_kzalloc(&pdev->dev, sizeof(*sport), GFP_KERNEL);
-	if (!sport)
-		return -ENOMEM;
-
-	port = &sport->port;
-
-	ret = of_alias_get_id(np, "serial");
-	if (ret < 0) {
-		dev_err(&pdev->dev, "failed to get alias id, errno %d\n", ret);
-		return ret;
-	}
-	if (ret >= UART_NR) {
-		dev_err(&pdev->dev, "driver limited to %d serial ports\n", UART_NR);
-		return -ENOMEM;
-	}
-
-	port->line = ret;
-
-	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	if (!res)
-		return -ENODEV;
-
-	port->mapbase = res->start;
-	port->membase = devm_ioremap_resource(&pdev->dev, res);
-	if (IS_ERR(port->membase))
-		return PTR_ERR(port->membase);
-
-	sport->clk = devm_clk_get(&pdev->dev, NULL);
-	if (IS_ERR(sport->clk))
-		return PTR_ERR(sport->clk);
-
-	port->uartclk = clk_get_rate(sport->clk);
-	port->dev = &pdev->dev;
-	port->type = PORT_GENERIC;
-	port->iotype = UPIO_MEM;
-	port->irq = platform_get_irq(pdev, 0);
-	port->ops = &esp32_uart_pops;
-	port->flags = UPF_BOOT_AUTOCONF;
-	port->has_sysrq = 1;
-	port->fifosize = ESP32_UART_TX_FIFO_SIZE;
-	port->private_data = (void *)device_get_match_data(&pdev->dev);
-
-	esp32_uart_ports[port->line] = sport;
-
-	platform_set_drvdata(pdev, port);
-
-	return uart_add_one_port(&esp32_uart_reg, port);
-}
-
-static void esp32_uart_remove(struct platform_device *pdev)
-{
-	struct uart_port *port = platform_get_drvdata(pdev);
-
-	uart_remove_one_port(&esp32_uart_reg, port);
-}
-
-
-static struct platform_driver esp32_uart_driver = {
-	.probe		= esp32_uart_probe,
-	.remove		= esp32_uart_remove,
-	.driver		= {
-		.name	= DRIVER_NAME,
-		.of_match_table	= esp32_uart_dt_ids,
-	},
-};
-
-static int __init esp32_uart_init(void)
-{
-	int ret;
-
-	ret = uart_register_driver(&esp32_uart_reg);
-	if (ret)
-		return ret;
-
-	ret = platform_driver_register(&esp32_uart_driver);
-	if (ret)
-		uart_unregister_driver(&esp32_uart_reg);
-
-	return ret;
-}
-
-static void __exit esp32_uart_exit(void)
-{
-	platform_driver_unregister(&esp32_uart_driver);
-	uart_unregister_driver(&esp32_uart_reg);
-}
-
-module_init(esp32_uart_init);
-module_exit(esp32_uart_exit);
-
-MODULE_AUTHOR("Max Filippov <jcmvbkbc@gmail.com>");
-MODULE_DESCRIPTION("Espressif ESP32 UART support");
-MODULE_LICENSE("GPL");
-- 
2.51.2


^ permalink raw reply related

* Re: [PATCH] tty: ipwireless: Fix use-after-free in tasklet during device removal
From: Qingfang Deng @ 2026-03-09 11:18 UTC (permalink / raw)
  To: David Sterba
  Cc: Jiri Slaby, Jiri Kosina, Greg KH, duoming, linux-serial,
	linux-kernel, dsterba, kuba, alexander.deucher, akpm, pkshih,
	tglx, mingo
In-Reply-To: <20260218152330.GI26902@suse.cz>

Hi, David

On Wed, 18 Feb 2026 16:23:30 +0100, David Sterba wrote:
> On Tue, Feb 17, 2026 at 09:03:25AM +0100, Jiri Slaby wrote:
> > Hi,
> > 
> > On 09. 02. 26, 11:21, David Sterba wrote:
> > > On Sun, Feb 08, 2026 at 06:25:38PM +0100, Jiri Kosina wrote:
> > >> On Sun, 8 Feb 2026, Greg KH wrote:
> > >>
> > >>>> I don't have the real hardware. In order to reproduce the bug, I simulate
> > >>>> the IPWireless PCMCIA card in the qemu by allocating and configuring the
> > >>>> necessary resources(I/O ports, memory regions, interrupts and so on) to
> > >>>> correspond with the hardware expected by the driver in the initialization
> > >>>> code of the virtual device.
> > >>>
> > >>> I wonder if this device even is still around, given that pcmcia is all
> > >>> but dead for a very long time.
> > >>
> > >> I doubt that this device is still around anywhere where reasonably new
> > >> kernels (including LTS) would matter.
> > >>
> > >> I don't think I've seen this device (which was back then donated to me by
> > >> T-Mobile CZ in order to get it supported in Linux, and I am not sure how
> > >> much global adoption it got afterwards) for, let's say, past 15 years :)
> > >>
> > >> I think (let's see what David, ho took the maintainership over for me
> > >> afterwards, has to say) we'd better deprecate and drop the whole thing,
> > >> rather than trying to pretend that it's still actively being taken care
> > >> of.
> > > 
> > > https://lore.kernel.org/all/20230223172403.GW10580@suse.cz/ last time
> > > the question of keeping the driver was asked (2023). Back then I was
> > > able to find the cards on second hand market but now I can't on a local
> > > market and there's exactly one hit on global eBay.
> > > 
> > > Local linux related or telco support forums seem to mention the driver
> > > until 2011 (root.cz, abclinuxu.cz, t-mobile.cz). It does not prove
> > > nobody is using it but I think the chances are quite low to justify
> > > keeping the driver.  It is simple enough to be built as an external
> > > module eventually, I can help with that in case somebody really needs
> > > that.
> > 
> > So, would you want to submit the removal? Or anyone else, if you don't 
> > want to lose time with this? (I can do that, if noone wants to.)
> 
> Let me do it after rc1 so I can finish the journey of this driver which
> was my first nontrivial contribution to linux kernel.

I found a memory leak in the driver:

- https://lore.kernel.org/linux-serial/20260306034058.386747-1-dqfext@gmail.com/T/

The bug has been present since the first version of this driver, but it
seems no one has noticed it until now. So I believe the driver has not
been actively used.

Regards,
Qingfang

^ permalink raw reply

* Re: [PATCH] drivers/tty/serial: move ESP32 UART drivers into staging
From: Max Filippov @ 2026-03-09  7:44 UTC (permalink / raw)
  To: Greg KH
  Cc: Julian Braha, rdunlap, ilpo.jarvinen, conor+dt,
	krzysztof.kozlowski+dt, robh+dt, jirislaby, linux-serial,
	devicetree, linux-kernel
In-Reply-To: <2026030845-chastise-briskness-3317@gregkh>

On Sun, Mar 8, 2026 at 7:16 AM Greg KH <gregkh@linuxfoundation.org> wrote:
> On Sun, Mar 08, 2026 at 01:14:12PM +0000, Julian Braha wrote:
> > These drivers were added about 3 years ago, and depend on the
> > XTENSA_PLATFORM_ESP32 config option which has never existed,
> > so no device can actually use them.
> > They can only be compiled with COMPILE_TEST.
> >
> > As you can see, this config option is still undefined
> > in the downstream tree:
> > https://github.com/search?q=repo%3Ajcmvbkbc%2Flinux-xtensa%20XTENSA_PLATFORM_ESP32&type=code
> >
> > I propose moving these drivers into staging
> > until there is a device that can boot the mainline kernel and use them.
>
> Why not just delete them?

I agree, it should go. I haven't had a chance to get back to it in 2 years,
and it doesn't look like I will any time soon.

-- 
Thanks.
-- Max

^ permalink raw reply

* Re: [PATCH] drivers/tty/serial: move ESP32 UART drivers into staging
From: Greg KH @ 2026-03-08 14:16 UTC (permalink / raw)
  To: Julian Braha
  Cc: jcmvbkbc, rdunlap, ilpo.jarvinen, conor+dt,
	krzysztof.kozlowski+dt, robh+dt, jirislaby, linux-serial,
	devicetree, linux-kernel
In-Reply-To: <20260308131412.1102749-1-julianbraha@gmail.com>

On Sun, Mar 08, 2026 at 01:14:12PM +0000, Julian Braha wrote:
> These drivers were added about 3 years ago, and depend on the
> XTENSA_PLATFORM_ESP32 config option which has never existed,
> so no device can actually use them.
> They can only be compiled with COMPILE_TEST.
> 
> As you can see, this config option is still undefined
> in the downstream tree:
> https://github.com/search?q=repo%3Ajcmvbkbc%2Flinux-xtensa%20XTENSA_PLATFORM_ESP32&type=code
> 
> I propose moving these drivers into staging
> until there is a device that can boot the mainline kernel and use them.

Why not just delete them?  What can staging do to help out here?  If
these can't be built, or used, don't drop them in staging, that's not
what that part of the kernel is for, sorry.

thanks,

greg k-h

^ permalink raw reply

* [PATCH] drivers/tty/serial: move ESP32 UART drivers into staging
From: Julian Braha @ 2026-03-08 13:14 UTC (permalink / raw)
  To: gregkh, jcmvbkbc
  Cc: rdunlap, ilpo.jarvinen, conor+dt, krzysztof.kozlowski+dt, robh+dt,
	jirislaby, linux-serial, devicetree, linux-kernel, Julian Braha

These drivers were added about 3 years ago, and depend on the
XTENSA_PLATFORM_ESP32 config option which has never existed,
so no device can actually use them.
They can only be compiled with COMPILE_TEST.

As you can see, this config option is still undefined
in the downstream tree:
https://github.com/search?q=repo%3Ajcmvbkbc%2Flinux-xtensa%20XTENSA_PLATFORM_ESP32&type=code

I propose moving these drivers into staging
until there is a device that can boot the mainline kernel and use them.

Signed-off-by: Julian Braha <julianbraha@gmail.com>
---
 drivers/staging/Kconfig                       |  2 ++
 drivers/staging/Makefile                      |  1 +
 drivers/staging/serial_esp32/Kconfig          | 28 +++++++++++++++++++
 drivers/staging/serial_esp32/Makefile         |  4 +++
 .../serial_esp32}/esp32_acm.c                 |  0
 .../serial_esp32}/esp32_uart.c                |  0
 drivers/tty/serial/Kconfig                    | 26 -----------------
 drivers/tty/serial/Makefile                   |  2 --
 8 files changed, 35 insertions(+), 28 deletions(-)
 create mode 100644 drivers/staging/serial_esp32/Kconfig
 create mode 100644 drivers/staging/serial_esp32/Makefile
 rename drivers/{tty/serial => staging/serial_esp32}/esp32_acm.c (100%)
 rename drivers/{tty/serial => staging/serial_esp32}/esp32_uart.c (100%)

diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index 2f92cd698bef..97593e31ca47 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -48,4 +48,6 @@ source "drivers/staging/axis-fifo/Kconfig"
 
 source "drivers/staging/vme_user/Kconfig"
 
+source "drivers/staging/serial_esp32/Kconfig"
+
 endif # STAGING
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index f5b8876aa536..78c2e4f49fe5 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -2,6 +2,7 @@
 # Makefile for staging directory
 
 obj-y				+= media/
+obj-y				+= serial_esp32/
 obj-$(CONFIG_RTL8723BS)		+= rtl8723bs/
 obj-$(CONFIG_OCTEON_ETHERNET)	+= octeon/
 obj-$(CONFIG_VME_BUS)		+= vme_user/
diff --git a/drivers/staging/serial_esp32/Kconfig b/drivers/staging/serial_esp32/Kconfig
new file mode 100644
index 000000000000..61c56bfe7764
--- /dev/null
+++ b/drivers/staging/serial_esp32/Kconfig
@@ -0,0 +1,28 @@
+# SPDX-License-Identifier: GPL-2.0
+config SERIAL_ESP32
+	tristate "Espressif ESP32 UART support"
+	depends on HAS_IOMEM
+	depends on XTENSA_PLATFORM_ESP32 || (COMPILE_TEST && OF)
+	select SERIAL_CORE
+	select SERIAL_CORE_CONSOLE
+	select SERIAL_EARLYCON
+	help
+	  Driver for the UART controllers of the Espressif ESP32xx SoCs.
+	  When earlycon option is enabled the following kernel command line
+	  snippets may be used:
+	    earlycon=esp32s3uart,mmio32,0x60000000,115200n8,40000000
+	    earlycon=esp32uart,mmio32,0x3ff40000,115200n8
+
+config SERIAL_ESP32_ACM
+	tristate "Espressif ESP32 USB ACM gadget support"
+	depends on HAS_IOMEM
+	depends on XTENSA_PLATFORM_ESP32 || (COMPILE_TEST && OF)
+	select SERIAL_CORE
+	select SERIAL_CORE_CONSOLE
+	select SERIAL_EARLYCON
+	help
+	  Driver for the CDC ACM gadget controller of the Espressif ESP32S3
+	  SoCs that share separate USB controller with the JTAG adapter.
+	  When earlycon option is enabled the following kernel command line
+	  snippet may be used:
+	    earlycon=esp32s3acm,mmio32,0x60038000
diff --git a/drivers/staging/serial_esp32/Makefile b/drivers/staging/serial_esp32/Makefile
new file mode 100644
index 000000000000..3c23321944e2
--- /dev/null
+++ b/drivers/staging/serial_esp32/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_SERIAL_ESP32)		+= esp32_uart.o
+obj-$(CONFIG_SERIAL_ESP32_ACM)		+= esp32_acm.o
diff --git a/drivers/tty/serial/esp32_acm.c b/drivers/staging/serial_esp32/esp32_acm.c
similarity index 100%
rename from drivers/tty/serial/esp32_acm.c
rename to drivers/staging/serial_esp32/esp32_acm.c
diff --git a/drivers/tty/serial/esp32_uart.c b/drivers/staging/serial_esp32/esp32_uart.c
similarity index 100%
rename from drivers/tty/serial/esp32_uart.c
rename to drivers/staging/serial_esp32/esp32_uart.c
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index f86775cfdcc9..686e7fb073b8 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -1593,32 +1593,6 @@ config SERIAL_NUVOTON_MA35D1_CONSOLE
 	  but you can alter that using a kernel command line option such as
 	  "console=ttyNVTx".
 
-config SERIAL_ESP32
-	tristate "Espressif ESP32 UART support"
-	depends on XTENSA_PLATFORM_ESP32 || (COMPILE_TEST && OF)
-	select SERIAL_CORE
-	select SERIAL_CORE_CONSOLE
-	select SERIAL_EARLYCON
-	help
-	  Driver for the UART controllers of the Espressif ESP32xx SoCs.
-	  When earlycon option is enabled the following kernel command line
-	  snippets may be used:
-	    earlycon=esp32s3uart,mmio32,0x60000000,115200n8,40000000
-	    earlycon=esp32uart,mmio32,0x3ff40000,115200n8
-
-config SERIAL_ESP32_ACM
-	tristate "Espressif ESP32 USB ACM gadget support"
-	depends on XTENSA_PLATFORM_ESP32 || (COMPILE_TEST && OF)
-	select SERIAL_CORE
-	select SERIAL_CORE_CONSOLE
-	select SERIAL_EARLYCON
-	help
-	  Driver for the CDC ACM gadget controller of the Espressif ESP32S3
-	  SoCs that share separate USB controller with the JTAG adapter.
-	  When earlycon option is enabled the following kernel command line
-	  snippet may be used:
-	    earlycon=esp32s3acm,mmio32,0x60038000
-
 endmenu
 
 config SERIAL_MCTRL_GPIO
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index a2ccbc508ec5..bba7b21a4a1d 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -37,8 +37,6 @@ obj-$(CONFIG_SERIAL_CLPS711X)		+= clps711x.o
 obj-$(CONFIG_SERIAL_CPM)		+= cpm_uart.o
 obj-$(CONFIG_SERIAL_CONEXANT_DIGICOLOR)	+= digicolor-usart.o
 obj-$(CONFIG_SERIAL_DZ)			+= dz.o
-obj-$(CONFIG_SERIAL_ESP32)		+= esp32_uart.o
-obj-$(CONFIG_SERIAL_ESP32_ACM)		+= esp32_acm.o
 obj-$(CONFIG_SERIAL_FSL_LINFLEXUART)	+= fsl_linflexuart.o
 obj-$(CONFIG_SERIAL_FSL_LPUART)		+= fsl_lpuart.o
 obj-$(CONFIG_SERIAL_ICOM)		+= icom.o
-- 
2.51.2


^ permalink raw reply related

* [PATCH] drivers/tty/serial: move ESP32 UART drivers into staging
From: Julian Braha @ 2026-03-08 13:11 UTC (permalink / raw)
  To: gregkg, jcmvbkbc
  Cc: rdunlap, ilpo.jarvinen, conor+dt, krzysztof.kozlowski+dt, robh+dt,
	jirislaby, linux-serial, devicetree, linux-kernel, Julian Braha

These drivers were added about 3 years ago, and depend on the
XTENSA_PLATFORM_ESP32 config option which has never existed,
so no device can actually use them.
They can only be compiled with COMPILE_TEST.

As you can see, this config option is still undefined
in the downstream tree:
https://github.com/search?q=repo%3Ajcmvbkbc%2Flinux-xtensa%20XTENSA_PLATFORM_ESP32&type=code

I propose moving these drivers into staging
until there is a device that can boot the mainline kernel and use them.

Signed-off-by: Julian Braha <julianbraha@gmail.com>
---
 drivers/staging/Kconfig                       |  2 ++
 drivers/staging/Makefile                      |  1 +
 drivers/staging/serial_esp32/Kconfig          | 28 +++++++++++++++++++
 drivers/staging/serial_esp32/Makefile         |  4 +++
 .../serial_esp32}/esp32_acm.c                 |  0
 .../serial_esp32}/esp32_uart.c                |  0
 drivers/tty/serial/Kconfig                    | 26 -----------------
 drivers/tty/serial/Makefile                   |  2 --
 8 files changed, 35 insertions(+), 28 deletions(-)
 create mode 100644 drivers/staging/serial_esp32/Kconfig
 create mode 100644 drivers/staging/serial_esp32/Makefile
 rename drivers/{tty/serial => staging/serial_esp32}/esp32_acm.c (100%)
 rename drivers/{tty/serial => staging/serial_esp32}/esp32_uart.c (100%)

diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index 2f92cd698bef..97593e31ca47 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -48,4 +48,6 @@ source "drivers/staging/axis-fifo/Kconfig"
 
 source "drivers/staging/vme_user/Kconfig"
 
+source "drivers/staging/serial_esp32/Kconfig"
+
 endif # STAGING
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index f5b8876aa536..78c2e4f49fe5 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -2,6 +2,7 @@
 # Makefile for staging directory
 
 obj-y				+= media/
+obj-y				+= serial_esp32/
 obj-$(CONFIG_RTL8723BS)		+= rtl8723bs/
 obj-$(CONFIG_OCTEON_ETHERNET)	+= octeon/
 obj-$(CONFIG_VME_BUS)		+= vme_user/
diff --git a/drivers/staging/serial_esp32/Kconfig b/drivers/staging/serial_esp32/Kconfig
new file mode 100644
index 000000000000..61c56bfe7764
--- /dev/null
+++ b/drivers/staging/serial_esp32/Kconfig
@@ -0,0 +1,28 @@
+# SPDX-License-Identifier: GPL-2.0
+config SERIAL_ESP32
+	tristate "Espressif ESP32 UART support"
+	depends on HAS_IOMEM
+	depends on XTENSA_PLATFORM_ESP32 || (COMPILE_TEST && OF)
+	select SERIAL_CORE
+	select SERIAL_CORE_CONSOLE
+	select SERIAL_EARLYCON
+	help
+	  Driver for the UART controllers of the Espressif ESP32xx SoCs.
+	  When earlycon option is enabled the following kernel command line
+	  snippets may be used:
+	    earlycon=esp32s3uart,mmio32,0x60000000,115200n8,40000000
+	    earlycon=esp32uart,mmio32,0x3ff40000,115200n8
+
+config SERIAL_ESP32_ACM
+	tristate "Espressif ESP32 USB ACM gadget support"
+	depends on HAS_IOMEM
+	depends on XTENSA_PLATFORM_ESP32 || (COMPILE_TEST && OF)
+	select SERIAL_CORE
+	select SERIAL_CORE_CONSOLE
+	select SERIAL_EARLYCON
+	help
+	  Driver for the CDC ACM gadget controller of the Espressif ESP32S3
+	  SoCs that share separate USB controller with the JTAG adapter.
+	  When earlycon option is enabled the following kernel command line
+	  snippet may be used:
+	    earlycon=esp32s3acm,mmio32,0x60038000
diff --git a/drivers/staging/serial_esp32/Makefile b/drivers/staging/serial_esp32/Makefile
new file mode 100644
index 000000000000..3c23321944e2
--- /dev/null
+++ b/drivers/staging/serial_esp32/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_SERIAL_ESP32)		+= esp32_uart.o
+obj-$(CONFIG_SERIAL_ESP32_ACM)		+= esp32_acm.o
diff --git a/drivers/tty/serial/esp32_acm.c b/drivers/staging/serial_esp32/esp32_acm.c
similarity index 100%
rename from drivers/tty/serial/esp32_acm.c
rename to drivers/staging/serial_esp32/esp32_acm.c
diff --git a/drivers/tty/serial/esp32_uart.c b/drivers/staging/serial_esp32/esp32_uart.c
similarity index 100%
rename from drivers/tty/serial/esp32_uart.c
rename to drivers/staging/serial_esp32/esp32_uart.c
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index f86775cfdcc9..686e7fb073b8 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -1593,32 +1593,6 @@ config SERIAL_NUVOTON_MA35D1_CONSOLE
 	  but you can alter that using a kernel command line option such as
 	  "console=ttyNVTx".
 
-config SERIAL_ESP32
-	tristate "Espressif ESP32 UART support"
-	depends on XTENSA_PLATFORM_ESP32 || (COMPILE_TEST && OF)
-	select SERIAL_CORE
-	select SERIAL_CORE_CONSOLE
-	select SERIAL_EARLYCON
-	help
-	  Driver for the UART controllers of the Espressif ESP32xx SoCs.
-	  When earlycon option is enabled the following kernel command line
-	  snippets may be used:
-	    earlycon=esp32s3uart,mmio32,0x60000000,115200n8,40000000
-	    earlycon=esp32uart,mmio32,0x3ff40000,115200n8
-
-config SERIAL_ESP32_ACM
-	tristate "Espressif ESP32 USB ACM gadget support"
-	depends on XTENSA_PLATFORM_ESP32 || (COMPILE_TEST && OF)
-	select SERIAL_CORE
-	select SERIAL_CORE_CONSOLE
-	select SERIAL_EARLYCON
-	help
-	  Driver for the CDC ACM gadget controller of the Espressif ESP32S3
-	  SoCs that share separate USB controller with the JTAG adapter.
-	  When earlycon option is enabled the following kernel command line
-	  snippet may be used:
-	    earlycon=esp32s3acm,mmio32,0x60038000
-
 endmenu
 
 config SERIAL_MCTRL_GPIO
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index a2ccbc508ec5..bba7b21a4a1d 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -37,8 +37,6 @@ obj-$(CONFIG_SERIAL_CLPS711X)		+= clps711x.o
 obj-$(CONFIG_SERIAL_CPM)		+= cpm_uart.o
 obj-$(CONFIG_SERIAL_CONEXANT_DIGICOLOR)	+= digicolor-usart.o
 obj-$(CONFIG_SERIAL_DZ)			+= dz.o
-obj-$(CONFIG_SERIAL_ESP32)		+= esp32_uart.o
-obj-$(CONFIG_SERIAL_ESP32_ACM)		+= esp32_acm.o
 obj-$(CONFIG_SERIAL_FSL_LINFLEXUART)	+= fsl_linflexuart.o
 obj-$(CONFIG_SERIAL_FSL_LPUART)		+= fsl_lpuart.o
 obj-$(CONFIG_SERIAL_ICOM)		+= icom.o
-- 
2.51.2


^ permalink raw reply related

* Re: [PATCH v2 3/4] rust: add basic serial device bus abstractions
From: Markus Probst @ 2026-03-06 20:46 UTC (permalink / raw)
  To: Danilo Krummrich
  Cc: Rob Herring, Greg Kroah-Hartman, Jiri Slaby, Miguel Ojeda,
	Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Alice Ryhl, Trevor Gross, Kari Argillander, Rafael J. Wysocki,
	Viresh Kumar, Boqun Feng, David Airlie, Simona Vetter,
	linux-serial, linux-kernel, rust-for-linux, linux-pm, driver-core,
	dri-devel
In-Reply-To: <DGVZNDKJ7RAG.A66CR0EV9T3P@kernel.org>

[-- Attachment #1: Type: text/plain, Size: 2283 bytes --]

On Fri, 2026-03-06 at 21:40 +0100, Danilo Krummrich wrote:
> On Fri Mar 6, 2026 at 8:35 PM CET, Markus Probst wrote:
> > +    extern "C" fn receive_buf_callback(
> > +        sdev: *mut bindings::serdev_device,
> > +        buf: *const u8,
> > +        length: usize,
> > +    ) -> usize {
> > +        // SAFETY: The serial device bus only ever calls the receive buf callback with a valid
> > +        // pointer to a `struct serdev_device`.
> > +        //
> > +        // INVARIANT: `sdev` is valid for the duration of `receive_buf_callback()`.
> > +        let sdev = unsafe { &*sdev.cast::<Device<device::CoreInternal>>() };
> > +
> > +        // SAFETY: `receive_buf_callback` is only ever called after a successful call to
> > +        // `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called
> > +        // and stored a `Pin<KBox<T>>`.
> > +        let data = unsafe { sdev.as_ref().drvdata_borrow::<T>() };
> > +
> > +        // SAFETY:
> > +        // - The serial device bus only ever calls the receive buf callback with a valid pointer to
> > +        //   a `struct serdev_device`.
> > +        // - `receive_buf_callback` is only ever called after a successful call to
> > +        //   `probe_callback`, hence it's guaranteed that `sdev.private_data` is a pointer
> > +        //   to a valid `PrivateData`.
> > +        let private_data = unsafe { &*(*sdev.as_raw()).private_data.cast::<PrivateData>() };
> > +
> > +        private_data.probe_complete.complete_all();
> 
> Will do a full review pass later on, but one quick question in advance:
> 
> What is this used for? It is completed here and in probe(), but I don't see it ever
> being used to actually wait.
Uh, thats a typo. It is supposed to wait here in receive_buf_callback.

Thanks
- Markus Probst

> 
> > +
> > +        // SAFETY: No one has exclusive access to `private_data.error`.
> > +        if unsafe { *private_data.error.get() } {
> > +            return length;
> > +        }
> > +
> > +        // SAFETY: `buf` is guaranteed to be non-null and has the size of `length`.
> > +        let buf = unsafe { core::slice::from_raw_parts(buf, length) };
> > +
> > +        T::receive(sdev, data, buf)
> > +    }
> > +}

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 870 bytes --]

^ permalink raw reply

* Re: [PATCH v2 3/4] rust: add basic serial device bus abstractions
From: Danilo Krummrich @ 2026-03-06 20:40 UTC (permalink / raw)
  To: Markus Probst
  Cc: Rob Herring, Greg Kroah-Hartman, Jiri Slaby, Miguel Ojeda,
	Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Alice Ryhl, Trevor Gross, Kari Argillander, Rafael J. Wysocki,
	Viresh Kumar, Boqun Feng, David Airlie, Simona Vetter,
	linux-serial, linux-kernel, rust-for-linux, linux-pm, driver-core,
	dri-devel
In-Reply-To: <20260306-rust_serdev-v2-3-e9b23b42b255@posteo.de>

On Fri Mar 6, 2026 at 8:35 PM CET, Markus Probst wrote:
> +    extern "C" fn receive_buf_callback(
> +        sdev: *mut bindings::serdev_device,
> +        buf: *const u8,
> +        length: usize,
> +    ) -> usize {
> +        // SAFETY: The serial device bus only ever calls the receive buf callback with a valid
> +        // pointer to a `struct serdev_device`.
> +        //
> +        // INVARIANT: `sdev` is valid for the duration of `receive_buf_callback()`.
> +        let sdev = unsafe { &*sdev.cast::<Device<device::CoreInternal>>() };
> +
> +        // SAFETY: `receive_buf_callback` is only ever called after a successful call to
> +        // `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called
> +        // and stored a `Pin<KBox<T>>`.
> +        let data = unsafe { sdev.as_ref().drvdata_borrow::<T>() };
> +
> +        // SAFETY:
> +        // - The serial device bus only ever calls the receive buf callback with a valid pointer to
> +        //   a `struct serdev_device`.
> +        // - `receive_buf_callback` is only ever called after a successful call to
> +        //   `probe_callback`, hence it's guaranteed that `sdev.private_data` is a pointer
> +        //   to a valid `PrivateData`.
> +        let private_data = unsafe { &*(*sdev.as_raw()).private_data.cast::<PrivateData>() };
> +
> +        private_data.probe_complete.complete_all();

Will do a full review pass later on, but one quick question in advance:

What is this used for? It is completed here and in probe(), but I don't see it ever
being used to actually wait.

> +
> +        // SAFETY: No one has exclusive access to `private_data.error`.
> +        if unsafe { *private_data.error.get() } {
> +            return length;
> +        }
> +
> +        // SAFETY: `buf` is guaranteed to be non-null and has the size of `length`.
> +        let buf = unsafe { core::slice::from_raw_parts(buf, length) };
> +
> +        T::receive(sdev, data, buf)
> +    }
> +}

^ permalink raw reply

* Re: [PATCH v2 3/4] rust: add basic serial device bus abstractions
From: Markus Probst @ 2026-03-06 20:36 UTC (permalink / raw)
  To: Rob Herring, Greg Kroah-Hartman, Jiri Slaby, Miguel Ojeda,
	Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Alice Ryhl, Trevor Gross, Danilo Krummrich, Kari Argillander,
	Rafael J. Wysocki, Viresh Kumar, Boqun Feng, David Airlie,
	Simona Vetter
  Cc: linux-serial, linux-kernel, rust-for-linux, linux-pm, driver-core,
	dri-devel
In-Reply-To: <20260306-rust_serdev-v2-3-e9b23b42b255@posteo.de>

[-- Attachment #1: Type: text/plain, Size: 24731 bytes --]

On Fri, 2026-03-06 at 19:35 +0000, Markus Probst wrote:
> Implement the basic serial device bus abstractions required to write a
> serial device bus device driver with or without the need for initial device
> data. This includes the following data structures:
> 
> The `serdev::Driver` trait represents the interface to the driver.
> 
> The `serdev::Device` abstraction represents a `struct serdev_device`.
> 
> In order to provide the Serdev specific parts to a generic
> `driver::Registration` the `driver::RegistrationOps` trait is
> implemented by `serdev::Adapter`.
> 
> Signed-off-by: Markus Probst <markus.probst@posteo.de>
> ---
>  rust/bindings/bindings_helper.h |   1 +
>  rust/helpers/helpers.c          |   1 +
>  rust/helpers/serdev.c           |  22 ++
>  rust/kernel/lib.rs              |   2 +
>  rust/kernel/serdev.rs           | 533 ++++++++++++++++++++++++++++++++++++++++
>  5 files changed, 559 insertions(+)
> 
> diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
> index 083cc44aa952..ab521ba42673 100644
> --- a/rust/bindings/bindings_helper.h
> +++ b/rust/bindings/bindings_helper.h
> @@ -80,6 +80,7 @@
>  #include <linux/regulator/consumer.h>
>  #include <linux/sched.h>
>  #include <linux/security.h>
> +#include <linux/serdev.h>
>  #include <linux/slab.h>
>  #include <linux/sys_soc.h>
>  #include <linux/task_work.h>
> diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
> index a3c42e51f00a..9b87e9591cfd 100644
> --- a/rust/helpers/helpers.c
> +++ b/rust/helpers/helpers.c
> @@ -53,6 +53,7 @@
>  #include "regulator.c"
>  #include "scatterlist.c"
>  #include "security.c"
> +#include "serdev.c"
>  #include "signal.c"
>  #include "slab.c"
>  #include "spinlock.c"
> diff --git a/rust/helpers/serdev.c b/rust/helpers/serdev.c
> new file mode 100644
> index 000000000000..c52b78ca3fc7
> --- /dev/null
> +++ b/rust/helpers/serdev.c
> @@ -0,0 +1,22 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +#include <linux/serdev.h>
> +
> +__rust_helper
> +void rust_helper_serdev_device_driver_unregister(struct serdev_device_driver *sdrv)
> +{
> +	serdev_device_driver_unregister(sdrv);
> +}
> +
> +__rust_helper
> +void rust_helper_serdev_device_put(struct serdev_device *serdev)
> +{
> +	serdev_device_put(serdev);
> +}
> +
> +__rust_helper
> +void rust_helper_serdev_device_set_client_ops(struct serdev_device *serdev,
> +					      const struct serdev_device_ops *ops)
> +{
> +	serdev_device_set_client_ops(serdev, ops);
> +}
> diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
> index 3da92f18f4ee..1d337e112922 100644
> --- a/rust/kernel/lib.rs
> +++ b/rust/kernel/lib.rs
> @@ -140,6 +140,8 @@
>  pub mod scatterlist;
>  pub mod security;
>  pub mod seq_file;
> +#[cfg(CONFIG_SERIAL_DEV_BUS)]
> +pub mod serdev;
>  pub mod sizes;
>  pub mod slice;
>  #[cfg(CONFIG_SOC_BUS)]
> diff --git a/rust/kernel/serdev.rs b/rust/kernel/serdev.rs
> new file mode 100644
> index 000000000000..cba5fb6245f7
> --- /dev/null
> +++ b/rust/kernel/serdev.rs
> @@ -0,0 +1,533 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +//! Abstractions for the serial device bus.
> +//!
> +//! C header: [`include/linux/serdev.h`](srctree/include/linux/serdev.h)
> +
> +use crate::{
> +    acpi,
> +    device,
> +    devres,
> +    driver,
> +    error::{from_result, to_result, VTABLE_DEFAULT_ERROR},
> +    of,
> +    prelude::*,
> +    sync::Completion,
> +    time::{msecs_to_jiffies, Jiffies, Msecs},
> +    types::{AlwaysRefCounted, ForeignOwnable, Opaque}, //
> +};
> +
> +use core::{
> +    cell::UnsafeCell,
> +    marker::PhantomData,
> +    mem::offset_of,
> +    num::NonZero,
> +    ptr::NonNull, //
> +};
> +
> +/// Parity bit to use with a serial device.
> +#[repr(u32)]
> +pub enum Parity {
> +    /// No parity bit.
> +    None = bindings::serdev_parity_SERDEV_PARITY_NONE,
> +    /// Even partiy.
> +    Even = bindings::serdev_parity_SERDEV_PARITY_EVEN,
> +    /// Odd parity.
> +    Odd = bindings::serdev_parity_SERDEV_PARITY_ODD,
> +}
> +
> +/// Timeout in Jiffies.
> +pub enum Timeout {
> +    /// Wait for a specific amount of [`Jiffies`].
> +    Jiffies(NonZero<Jiffies>),
> +    /// Wait for a specific amount of [`Msecs`].
> +    Milliseconds(NonZero<Msecs>),
> +    /// Wait as long as possible.
> +    ///
> +    /// This is equivalent to [`kernel::task::MAX_SCHEDULE_TIMEOUT`].
> +    Max,
> +}
> +
> +impl Timeout {
> +    fn into_jiffies(self) -> isize {
> +        match self {
> +            Self::Jiffies(value) => value.get().try_into().unwrap_or_default(),
> +            Self::Milliseconds(value) => {
> +                msecs_to_jiffies(value.get()).try_into().unwrap_or_default()
> +            }
> +            Self::Max => 0,
> +        }
> +    }
> +}
> +
> +/// An adapter for the registration of serial device bus device drivers.
> +pub struct Adapter<T: Driver>(T);
> +
> +// SAFETY:
> +// - `bindings::serdev_device_driver` is a C type declared as `repr(C)`.
> +// - `Drvdata<T>` is the type of the driver's device private data.
> +// - `struct serdev_device_driver` embeds a `struct device_driver`.
> +// - `DEVICE_DRIVER_OFFSET` is the correct byte offset to the embedded `struct device_driver`.
> +unsafe impl<T: Driver + 'static> driver::DriverLayout for Adapter<T> {
> +    type DriverType = bindings::serdev_device_driver;
> +    type DriverData = T;
> +    const DEVICE_DRIVER_OFFSET: usize = core::mem::offset_of!(Self::DriverType, driver);
> +}
> +
> +// SAFETY: A call to `unregister` for a given instance of `DriverType` is guaranteed to be valid if
> +// a preceding call to `register` has been successful.
> +unsafe impl<T: Driver + 'static> driver::RegistrationOps for Adapter<T> {
> +    unsafe fn register(
> +        sdrv: &Opaque<Self::DriverType>,
> +        name: &'static CStr,
> +        module: &'static ThisModule,
> +    ) -> Result {
> +        let of_table = match T::OF_ID_TABLE {
> +            Some(table) => table.as_ptr(),
> +            None => core::ptr::null(),
> +        };
> +
> +        let acpi_table = match T::ACPI_ID_TABLE {
> +            Some(table) => table.as_ptr(),
> +            None => core::ptr::null(),
> +        };
> +
> +        // SAFETY: It's safe to set the fields of `struct serdev_device_driver` on initialization.
> +        unsafe {
> +            (*sdrv.get()).driver.name = name.as_char_ptr();
> +            (*sdrv.get()).probe = Some(Self::probe_callback);
> +            (*sdrv.get()).remove = Some(Self::remove_callback);
> +            (*sdrv.get()).driver.of_match_table = of_table;
> +            (*sdrv.get()).driver.acpi_match_table = acpi_table;
> +        }
> +
> +        // SAFETY: `sdrv` is guaranteed to be a valid `DriverType`.
> +        to_result(unsafe { bindings::__serdev_device_driver_register(sdrv.get(), module.0) })
> +    }
> +
> +    unsafe fn unregister(sdrv: &Opaque<Self::DriverType>) {
> +        // SAFETY: `sdrv` is guaranteed to be a valid `DriverType`.
> +        unsafe { bindings::serdev_device_driver_unregister(sdrv.get()) };
> +    }
> +}
> +
> +#[pin_data]
> +struct PrivateData {
> +    #[pin]
> +    probe_complete: Completion,
> +    error: UnsafeCell<bool>,
> +}
> +
> +impl<T: Driver + 'static> Adapter<T> {
> +    const OPS: &'static bindings::serdev_device_ops = &bindings::serdev_device_ops {
> +        receive_buf: if T::HAS_RECEIVE {
> +            Some(Self::receive_buf_callback)
> +        } else {
> +            None
> +        },
> +        write_wakeup: Some(bindings::serdev_device_write_wakeup),
> +    };
> +
> +    extern "C" fn probe_callback(sdev: *mut bindings::serdev_device) -> kernel::ffi::c_int {
> +        // SAFETY: The serial device bus only ever calls the probe callback with a valid pointer to
> +        // a `struct serdev_device`.
> +        //
> +        // INVARIANT: `sdev` is valid for the duration of `probe_callback()`.
> +        let sdev = unsafe { &*sdev.cast::<Device<device::CoreInternal>>() };
> +        let id_info = <Self as driver::Adapter>::id_info(sdev.as_ref());
> +
> +        from_result(|| {
> +            let private_data = devres::register(
> +                sdev.as_ref(),
> +                try_pin_init!(PrivateData {
> +                    probe_complete <- Completion::new(),
> +                    error: false.into(),
> +                }),
> +                GFP_KERNEL,
> +            )?;
> +
> +            // SAFETY: `sdev.as_raw()` is guaranteed to be a valid pointer to `serdev_device`.
> +            unsafe {
> +                (*sdev.as_raw()).private_data =
> +                    (&raw const *private_data).cast::<c_void>().cast_mut()
> +            };
> +
> +            // SAFETY: `sdev.as_raw()` is guaranteed to be a valid pointer to `serdev_device`.
> +            unsafe { bindings::serdev_device_set_client_ops(sdev.as_raw(), Self::OPS) };
> +
> +            // SAFETY: The serial device bus only ever calls the probe callback with a valid pointer
> +            // to a `serdev_device`.
> +            to_result(unsafe {
> +                bindings::devm_serdev_device_open(sdev.as_ref().as_raw(), sdev.as_raw())
> +            })?;
> +
> +            let data = T::probe(sdev, id_info);
> +            let result = sdev.as_ref().set_drvdata(data);
> +
> +            // SAFETY: We have exclusive access to `private_data.error`.
> +            unsafe { *private_data.error.get() = result.is_err() };
> +
> +            private_data.probe_complete.complete_all();
> +
> +            result.map(|()| 0)
> +        })
> +    }
> +
> +    extern "C" fn remove_callback(sdev: *mut bindings::serdev_device) {
> +        // SAFETY: The serial device bus only ever calls the remove callback with a valid pointer
> +        // to a `struct serdev_device`.
> +        //
> +        // INVARIANT: `sdev` is valid for the duration of `remove_callback()`.
> +        let sdev = unsafe { &*sdev.cast::<Device<device::CoreInternal>>() };
> +
> +        // SAFETY: `remove_callback` is only ever called after a successful call to
> +        // `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called
> +        // and stored a `Pin<KBox<T>>`.
> +        let data = unsafe { sdev.as_ref().drvdata_borrow::<T>() };
> +
> +        T::unbind(sdev, data);
> +
> +        // SAFETY:
> +        // - `sdev.as_raw()` is guaranteed to be a pointer to a valid `serdev_device`.
> +        // - `remove_callback` is only ever called after a successful call to
> +        //   `probe_callback`, hence it's guaranteed that `private_data` is a valid pointer to
> +        //   `PrivateData` and is stored in a `Pin<KBox<PrivateData>>`.
> +        // - `private_data` will not be accessed after this function.
> +        drop(unsafe { <Pin<KBox<PrivateData>>>::from_foreign((*sdev.as_raw()).private_data) });
It seems I forgot to remove this drop statement. Please ignore it.

Thanks
- Markus Probst

> +    }
> +
> +    extern "C" fn receive_buf_callback(
> +        sdev: *mut bindings::serdev_device,
> +        buf: *const u8,
> +        length: usize,
> +    ) -> usize {
> +        // SAFETY: The serial device bus only ever calls the receive buf callback with a valid
> +        // pointer to a `struct serdev_device`.
> +        //
> +        // INVARIANT: `sdev` is valid for the duration of `receive_buf_callback()`.
> +        let sdev = unsafe { &*sdev.cast::<Device<device::CoreInternal>>() };
> +
> +        // SAFETY: `receive_buf_callback` is only ever called after a successful call to
> +        // `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called
> +        // and stored a `Pin<KBox<T>>`.
> +        let data = unsafe { sdev.as_ref().drvdata_borrow::<T>() };
> +
> +        // SAFETY:
> +        // - The serial device bus only ever calls the receive buf callback with a valid pointer to
> +        //   a `struct serdev_device`.
> +        // - `receive_buf_callback` is only ever called after a successful call to
> +        //   `probe_callback`, hence it's guaranteed that `sdev.private_data` is a pointer
> +        //   to a valid `PrivateData`.
> +        let private_data = unsafe { &*(*sdev.as_raw()).private_data.cast::<PrivateData>() };
> +
> +        private_data.probe_complete.complete_all();
> +
> +        // SAFETY: No one has exclusive access to `private_data.error`.
> +        if unsafe { *private_data.error.get() } {
> +            return length;
> +        }
> +
> +        // SAFETY: `buf` is guaranteed to be non-null and has the size of `length`.
> +        let buf = unsafe { core::slice::from_raw_parts(buf, length) };
> +
> +        T::receive(sdev, data, buf)
> +    }
> +}
> +
> +impl<T: Driver + 'static> driver::Adapter for Adapter<T> {
> +    type IdInfo = T::IdInfo;
> +
> +    fn of_id_table() -> Option<of::IdTable<Self::IdInfo>> {
> +        T::OF_ID_TABLE
> +    }
> +
> +    fn acpi_id_table() -> Option<acpi::IdTable<Self::IdInfo>> {
> +        T::ACPI_ID_TABLE
> +    }
> +}
> +
> +/// Declares a kernel module that exposes a single serial device bus device driver.
> +///
> +/// # Examples
> +///
> +/// ```ignore
> +/// kernel::module_serdev_device_driver! {
> +///     type: MyDriver,
> +///     name: "Module name",
> +///     authors: ["Author name"],
> +///     description: "Description",
> +///     license: "GPL v2",
> +/// }
> +/// ```
> +#[macro_export]
> +macro_rules! module_serdev_device_driver {
> +    ($($f:tt)*) => {
> +        $crate::module_driver!(<T>, $crate::serdev::Adapter<T>, { $($f)* });
> +    };
> +}
> +
> +/// The serial device bus device driver trait.
> +///
> +/// Drivers must implement this trait in order to get a serial device bus device driver registered.
> +///
> +/// # Examples
> +///
> +///```
> +/// # use kernel::{
> +///     acpi,
> +///     bindings,
> +///     device::{
> +///         Bound,
> +///         Core, //
> +///     },
> +///     of,
> +///     serdev, //
> +/// };
> +///
> +/// struct MyDriver;
> +///
> +/// kernel::of_device_table!(
> +///     OF_TABLE,
> +///     MODULE_OF_TABLE,
> +///     <MyDriver as serdev::Driver>::IdInfo,
> +///     [
> +///         (of::DeviceId::new(c"test,device"), ())
> +///     ]
> +/// );
> +///
> +/// kernel::acpi_device_table!(
> +///     ACPI_TABLE,
> +///     MODULE_ACPI_TABLE,
> +///     <MyDriver as serdev::Driver>::IdInfo,
> +///     [
> +///         (acpi::DeviceId::new(c"LNUXBEEF"), ())
> +///     ]
> +/// );
> +///
> +/// #[vtable]
> +/// impl serdev::Driver for MyDriver {
> +///     type IdInfo = ();
> +///     const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE);
> +///     const ACPI_ID_TABLE: Option<acpi::IdTable<Self::IdInfo>> = Some(&ACPI_TABLE);
> +///
> +///     fn probe(
> +///         sdev: &serdev::Device<Core>,
> +///         _id_info: Option<&Self::IdInfo>,
> +///     ) -> impl PinInit<Self, Error> {
> +///         sdev.set_baudrate(115200);
> +///         sdev.write_all(b"Hello\n", serdev::Timeout::Max)?;
> +///         Ok(MyDriver)
> +///     }
> +/// }
> +///```
> +#[vtable]
> +pub trait Driver: Send {
> +    /// The type holding driver private data about each device id supported by the driver.
> +    // TODO: Use associated_type_defaults once stabilized:
> +    //
> +    // ```
> +    // type IdInfo: 'static = ();
> +    // ```
> +    type IdInfo: 'static;
> +
> +    /// The table of OF device ids supported by the driver.
> +    const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = None;
> +
> +    /// The table of ACPI device ids supported by the driver.
> +    const ACPI_ID_TABLE: Option<acpi::IdTable<Self::IdInfo>> = None;
> +
> +    /// Serial device bus device driver probe.
> +    ///
> +    /// Called when a new serial device bus device is added or discovered.
> +    /// Implementers should attempt to initialize the device here.
> +    fn probe(
> +        sdev: &Device<device::Core>,
> +        id_info: Option<&Self::IdInfo>,
> +    ) -> impl PinInit<Self, Error>;
> +
> +    /// Serial device bus device driver unbind.
> +    ///
> +    /// Called when a [`Device`] is unbound from its bound [`Driver`]. Implementing this callback
> +    /// is optional.
> +    ///
> +    /// This callback serves as a place for drivers to perform teardown operations that require a
> +    /// `&Device<Core>` or `&Device<Bound>` reference. For instance.
> +    ///
> +    /// Otherwise, release operations for driver resources should be performed in `Self::drop`.
> +    fn unbind(sdev: &Device<device::Core>, this: Pin<&Self>) {
> +        let _ = (sdev, this);
> +    }
> +
> +    /// Serial device bus device data receive callback.
> +    ///
> +    /// Called when data got received from device.
> +    ///
> +    /// Returns the number of bytes accepted.
> +    fn receive(sdev: &Device<device::Bound>, this: Pin<&Self>, data: &[u8]) -> usize {
> +        let _ = (sdev, this, data);
> +        build_error!(VTABLE_DEFAULT_ERROR)
> +    }
> +}
> +
> +/// The serial device bus device representation.
> +///
> +/// This structure represents the Rust abstraction for a C `struct serdev_device`. The
> +/// implementation abstracts the usage of an already existing C `struct serdev_device` within Rust
> +/// code that we get passed from the C side.
> +///
> +/// # Invariants
> +///
> +/// A [`Device`] instance represents a valid `struct serdev_device` created by the C portion of
> +/// the kernel.
> +#[repr(transparent)]
> +pub struct Device<Ctx: device::DeviceContext = device::Normal>(
> +    Opaque<bindings::serdev_device>,
> +    PhantomData<Ctx>,
> +);
> +
> +impl<Ctx: device::DeviceContext> Device<Ctx> {
> +    fn as_raw(&self) -> *mut bindings::serdev_device {
> +        self.0.get()
> +    }
> +}
> +
> +impl Device<device::Bound> {
> +    /// Set the baudrate in bits per second.
> +    ///
> +    /// Common baudrates are 115200, 9600, 19200, 57600, 4800.
> +    ///
> +    /// Use [`Device::write_flush`] before calling this if you have written data prior to this call.
> +    pub fn set_baudrate(&self, speed: u32) -> Result<(), u32> {
> +        // SAFETY: `self.as_raw()` is guaranteed to be a pointer to a valid `serdev_device`.
> +        let ret = unsafe { bindings::serdev_device_set_baudrate(self.as_raw(), speed) };
> +        if ret == speed {
> +            Ok(())
> +        } else {
> +            Err(ret)
> +        }
> +    }
> +
> +    /// Set if flow control should be enabled.
> +    ///
> +    /// Use [`Device::write_flush`] before calling this if you have written data prior to this call.
> +    pub fn set_flow_control(&self, enable: bool) {
> +        // SAFETY: `self.as_raw()` is guaranteed to be a pointer to a valid `serdev_device`.
> +        unsafe { bindings::serdev_device_set_flow_control(self.as_raw(), enable) };
> +    }
> +
> +    /// Set parity to use.
> +    ///
> +    /// Use [`Device::write_flush`] before calling this if you have written data prior to this call.
> +    pub fn set_parity(&self, parity: Parity) -> Result {
> +        // SAFETY: `self.as_raw()` is guaranteed to be a pointer to a valid `serdev_device`.
> +        to_result(unsafe { bindings::serdev_device_set_parity(self.as_raw(), parity as u32) })
> +    }
> +
> +    /// Write data to the serial device until the controller has accepted all the data or has
> +    /// been interrupted by a timeout or signal.
> +    ///
> +    /// Note that any accepted data has only been buffered by the controller. Use
> +    /// [ Device::wait_until_sent`] to make sure the controller write buffer has actually been
> +    /// emptied.
> +    ///
> +    /// Returns the number of bytes written (less than `data.len()` if interrupted).
> +    /// [`kernel::error::code::ETIMEDOUT`] or [`kernel::error::code::ERESTARTSYS`] if interrupted
> +    /// before any bytes were written.
> +    pub fn write_all(&self, data: &[u8], timeout: Timeout) -> Result<usize> {
> +        // SAFETY:
> +        // - `self.as_raw()` is guaranteed to be a pointer to a valid `serdev_device`.
> +        // - `data.as_ptr()` is guaranteed to be a valid array pointer with the size of
> +        //   `data.len()`.
> +        let ret = unsafe {
> +            bindings::serdev_device_write(
> +                self.as_raw(),
> +                data.as_ptr(),
> +                data.len(),
> +                timeout.into_jiffies(),
> +            )
> +        };
> +        // CAST: negative return values are guaranteed to be between `-MAX_ERRNO` and `-1`,
> +        // which always fit into a `i32`.
> +        to_result(ret as i32).map(|()| ret.unsigned_abs())
> +    }
> +
> +    /// Write data to the serial device.
> +    ///
> +    /// If you want to write until the controller has accepted all the data, use
> +    /// [`Device::write_all`].
> +    ///
> +    /// Note that any accepted data has only been buffered by the controller. Use
> +    /// [ Device::wait_until_sent`] to make sure the controller write buffer has actually been
> +    /// emptied.
> +    ///
> +    /// Returns the number of bytes written (less than `data.len()` if not enough room in the
> +    /// write buffer).
> +    pub fn write(&self, data: &[u8]) -> Result<u32> {
> +        // SAFETY:
> +        // - `self.as_raw()` is guaranteed to be a pointer to a valid `serdev_device`.
> +        // - `data.as_ptr()` is guaranteed to be a valid array pointer with the size of
> +        //   `data.len()`.
> +        let ret =
> +            unsafe { bindings::serdev_device_write_buf(self.as_raw(), data.as_ptr(), data.len()) };
> +
> +        to_result(ret as i32).map(|()| ret.unsigned_abs())
> +    }
> +
> +    /// Send data to the serial device immediately.
> +    ///
> +    /// Note that this doesn't guarantee that the data has been transmitted.
> +    /// Use [`Device::wait_until_sent`] for this purpose.
> +    pub fn write_flush(&self) {
> +        // SAFETY: `self.as_raw()` is guaranteed to be a pointer to a valid `serdev_device`.
> +        unsafe { bindings::serdev_device_write_flush(self.as_raw()) };
> +    }
> +
> +    /// Wait for the data to be sent.
> +    ///
> +    /// After this function, the write buffer of the controller should be empty.
> +    pub fn wait_until_sent(&self, timeout: Timeout) {
> +        // SAFETY: `self.as_raw()` is guaranteed to be a pointer to a valid `serdev_device`.
> +        unsafe { bindings::serdev_device_wait_until_sent(self.as_raw(), timeout.into_jiffies()) };
> +    }
> +}
> +
> +// SAFETY: `serdev::Device` is a transparent wrapper of `struct serdev_device`.
> +// The offset is guaranteed to point to a valid device field inside `serdev::Device`.
> +unsafe impl<Ctx: device::DeviceContext> device::AsBusDevice<Ctx> for Device<Ctx> {
> +    const OFFSET: usize = offset_of!(bindings::serdev_device, dev);
> +}
> +
> +// SAFETY: `Device` is a transparent wrapper of a type that doesn't depend on `Device`'s generic
> +// argument.
> +kernel::impl_device_context_deref!(unsafe { Device });
> +kernel::impl_device_context_into_aref!(Device);
> +
> +// SAFETY: Instances of `Device` are always reference-counted.
> +unsafe impl AlwaysRefCounted for Device {
> +    fn inc_ref(&self) {
> +        self.as_ref().inc_ref();
> +    }
> +
> +    unsafe fn dec_ref(obj: NonNull<Self>) {
> +        // SAFETY: The safety requirements guarantee that the refcount is non-zero.
> +        unsafe { bindings::serdev_device_put(obj.cast().as_ptr()) }
> +    }
> +}
> +
> +impl<Ctx: device::DeviceContext> AsRef<device::Device<Ctx>> for Device<Ctx> {
> +    fn as_ref(&self) -> &device::Device<Ctx> {
> +        // SAFETY: By the type invariant of `Self`, `self.as_raw()` is a pointer to a valid
> +        // `struct serdev_device`.
> +        let dev = unsafe { &raw mut (*self.as_raw()).dev };
> +
> +        // SAFETY: `dev` points to a valid `struct device`.
> +        unsafe { device::Device::from_raw(dev) }
> +    }
> +}
> +
> +// SAFETY: A `Device` is always reference-counted and can be released from any thread.
> +unsafe impl Send for Device {}
> +
> +// SAFETY: `Device` can be shared among threads because all methods of `Device`
> +// (i.e. `Device<Normal>) are thread safe.
> +unsafe impl Sync for Device {}

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 870 bytes --]

^ permalink raw reply

* Re: [PATCH v2 2/4] serdev: add private data to serdev_device
From: Danilo Krummrich @ 2026-03-06 20:29 UTC (permalink / raw)
  To: Markus Probst
  Cc: Rob Herring, Greg Kroah-Hartman, Jiri Slaby, Miguel Ojeda,
	Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Alice Ryhl, Trevor Gross, Kari Argillander, Rafael J. Wysocki,
	Viresh Kumar, Boqun Feng, David Airlie, Simona Vetter,
	linux-serial, linux-kernel, rust-for-linux, linux-pm, driver-core,
	dri-devel
In-Reply-To: <20260306-rust_serdev-v2-2-e9b23b42b255@posteo.de>

On Fri Mar 6, 2026 at 8:35 PM CET, Markus Probst wrote:
> Add private data to `struct serdev_device`, as it is required by the
> rust abstraction added in the following commit
> (rust: add basic serial device bus abstractions).
>
> Signed-off-by: Markus Probst <markus.probst@posteo.de>
> ---
>  include/linux/serdev.h | 14 ++++++++------
>  1 file changed, 8 insertions(+), 6 deletions(-)
>
> diff --git a/include/linux/serdev.h b/include/linux/serdev.h
> index 5654c58eb73c..b591af23faf0 100644
> --- a/include/linux/serdev.h
> +++ b/include/linux/serdev.h
> @@ -33,12 +33,13 @@ struct serdev_device_ops {
>  
>  /**
>   * struct serdev_device - Basic representation of an serdev device
> - * @dev:	Driver model representation of the device.
> - * @nr:		Device number on serdev bus.
> - * @ctrl:	serdev controller managing this device.
> - * @ops:	Device operations.
> - * @write_comp	Completion used by serdev_device_write() internally
> - * @write_lock	Lock to serialize access when writing data
> + * @dev:	 Driver model representation of the device.
> + * @nr:		 Device number on serdev bus.
> + * @ctrl:	 serdev controller managing this device.
> + * @ops:	 Device operations.
> + * @write_comp	 Completion used by serdev_device_write() internally
> + * @write_lock	 Lock to serialize access when writing data
> + * @private_data Private data for the device driver.

I think this is a bit misleading, as the driver's device private data is stored
in the embedded struct device. This seems to be more about having a place to
store private data of the Rust abstraction.

So, you may want to name this something along the lines of rust_private_data.
Additionally, you should make it very clear that this field must not be used by
drivers directly (i.e. C drivers could easily be tempted to abuse this).

>   */
>  struct serdev_device {
>  	struct device dev;
> @@ -47,6 +48,7 @@ struct serdev_device {
>  	const struct serdev_device_ops *ops;
>  	struct completion write_comp;
>  	struct mutex write_lock;
> +	void *private_data;
>  };
>  
>  static inline struct serdev_device *to_serdev_device(struct device *d)
>
> -- 
> 2.52.0


^ permalink raw reply

* Re: [PATCH v2 2/4] serdev: add private data to serdev_device
From: Markus Probst @ 2026-03-06 20:14 UTC (permalink / raw)
  To: Randy Dunlap, Rob Herring, Greg Kroah-Hartman, Jiri Slaby,
	Miguel Ojeda, Gary Guo, Björn Roy Baron, Benno Lossin,
	Andreas Hindborg, Alice Ryhl, Trevor Gross, Danilo Krummrich,
	Kari Argillander, Rafael J. Wysocki, Viresh Kumar, Boqun Feng,
	David Airlie, Simona Vetter
  Cc: linux-serial, linux-kernel, rust-for-linux, linux-pm, driver-core,
	dri-devel
In-Reply-To: <934ca1e6-dfee-411e-890a-083dcfc9f6b1@infradead.org>

[-- Attachment #1: Type: text/plain, Size: 2412 bytes --]

On Fri, 2026-03-06 at 11:49 -0800, Randy Dunlap wrote:
> Hi--
> 
> On 3/6/26 11:35 AM, Markus Probst wrote:
> > Add private data to `struct serdev_device`, as it is required by the
> > rust abstraction added in the following commit
> > (rust: add basic serial device bus abstractions).
> > 
> > Signed-off-by: Markus Probst <markus.probst@posteo.de>
> > ---
> >  include/linux/serdev.h | 14 ++++++++------
> >  1 file changed, 8 insertions(+), 6 deletions(-)
> > 
> > diff --git a/include/linux/serdev.h b/include/linux/serdev.h
> > index 5654c58eb73c..b591af23faf0 100644
> > --- a/include/linux/serdev.h
> > +++ b/include/linux/serdev.h
> > @@ -33,12 +33,13 @@ struct serdev_device_ops {
> >  
> >  /**
> >   * struct serdev_device - Basic representation of an serdev device
> > - * @dev:	Driver model representation of the device.
> > - * @nr:		Device number on serdev bus.
> > - * @ctrl:	serdev controller managing this device.
> > - * @ops:	Device operations.
> > - * @write_comp	Completion used by serdev_device_write() internally
> > - * @write_lock	Lock to serialize access when writing data
> > + * @dev:	 Driver model representation of the device.
> > + * @nr:		 Device number on serdev bus.
> > + * @ctrl:	 serdev controller managing this device.
> > + * @ops:	 Device operations.
> > + * @write_comp	 Completion used by serdev_device_write() internally
> > + * @write_lock	 Lock to serialize access when writing data
> > + * @private_data Private data for the device driver.
> >   */
> 
> I don't quite get why each changed line has an extra tab added to it. ?
So all the fields have the same indentation.
Otherwise the description of private_data, because of its length, would
be 1 character more to the right.

> Also, struct member names in kernel-doc should with a colon  (':'), e.g.,
> 
>  * @private_data: Private data for the device driver.
> 
> Please correct that and the 2 lines above it also.
> And maybe test it to check for warnings.
I can do that.

> 
> >  struct serdev_device {
> >  	struct device dev;
> > @@ -47,6 +48,7 @@ struct serdev_device {
> >  	const struct serdev_device_ops *ops;
> >  	struct completion write_comp;
> >  	struct mutex write_lock;
> > +	void *private_data;
> >  };
> >  
> >  static inline struct serdev_device *to_serdev_device(struct device *d)
> > 
> 
> thanks.

Thanks
- Markus Probst


[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 870 bytes --]

^ permalink raw reply

* Re: [PATCH v2 2/4] serdev: add private data to serdev_device
From: Randy Dunlap @ 2026-03-06 19:49 UTC (permalink / raw)
  To: Markus Probst, Rob Herring, Greg Kroah-Hartman, Jiri Slaby,
	Miguel Ojeda, Gary Guo, Björn Roy Baron, Benno Lossin,
	Andreas Hindborg, Alice Ryhl, Trevor Gross, Danilo Krummrich,
	Kari Argillander, Rafael J. Wysocki, Viresh Kumar, Boqun Feng,
	David Airlie, Simona Vetter
  Cc: linux-serial, linux-kernel, rust-for-linux, linux-pm, driver-core,
	dri-devel
In-Reply-To: <20260306-rust_serdev-v2-2-e9b23b42b255@posteo.de>

Hi--

On 3/6/26 11:35 AM, Markus Probst wrote:
> Add private data to `struct serdev_device`, as it is required by the
> rust abstraction added in the following commit
> (rust: add basic serial device bus abstractions).
> 
> Signed-off-by: Markus Probst <markus.probst@posteo.de>
> ---
>  include/linux/serdev.h | 14 ++++++++------
>  1 file changed, 8 insertions(+), 6 deletions(-)
> 
> diff --git a/include/linux/serdev.h b/include/linux/serdev.h
> index 5654c58eb73c..b591af23faf0 100644
> --- a/include/linux/serdev.h
> +++ b/include/linux/serdev.h
> @@ -33,12 +33,13 @@ struct serdev_device_ops {
>  
>  /**
>   * struct serdev_device - Basic representation of an serdev device
> - * @dev:	Driver model representation of the device.
> - * @nr:		Device number on serdev bus.
> - * @ctrl:	serdev controller managing this device.
> - * @ops:	Device operations.
> - * @write_comp	Completion used by serdev_device_write() internally
> - * @write_lock	Lock to serialize access when writing data
> + * @dev:	 Driver model representation of the device.
> + * @nr:		 Device number on serdev bus.
> + * @ctrl:	 serdev controller managing this device.
> + * @ops:	 Device operations.
> + * @write_comp	 Completion used by serdev_device_write() internally
> + * @write_lock	 Lock to serialize access when writing data
> + * @private_data Private data for the device driver.
>   */

I don't quite get why each changed line has an extra tab added to it. ?
Also, struct member names in kernel-doc should with a colon  (':'), e.g.,

 * @private_data: Private data for the device driver.

Please correct that and the 2 lines above it also.
And maybe test it to check for warnings.

>  struct serdev_device {
>  	struct device dev;
> @@ -47,6 +48,7 @@ struct serdev_device {
>  	const struct serdev_device_ops *ops;
>  	struct completion write_comp;
>  	struct mutex write_lock;
> +	void *private_data;
>  };
>  
>  static inline struct serdev_device *to_serdev_device(struct device *d)
> 

thanks.
-- 
~Randy


^ permalink raw reply

* [PATCH v2 4/4] samples: rust: add Rust serial device bus sample device driver
From: Markus Probst @ 2026-03-06 19:35 UTC (permalink / raw)
  To: Rob Herring, Greg Kroah-Hartman, Jiri Slaby, Miguel Ojeda,
	Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Alice Ryhl, Trevor Gross, Danilo Krummrich, Kari Argillander,
	Rafael J. Wysocki, Viresh Kumar, Boqun Feng, David Airlie,
	Simona Vetter, Boqun Feng
  Cc: linux-serial, linux-kernel, rust-for-linux, linux-pm, driver-core,
	dri-devel, Markus Probst
In-Reply-To: <20260306-rust_serdev-v2-0-e9b23b42b255@posteo.de>

Add a sample Rust serial device bus device driver illustrating the usage
of the serial device bus abstractions.

This drivers probes through either a match of device / driver name or a
match within the OF ID table.

Signed-off-by: Markus Probst <markus.probst@posteo.de>
---
 samples/rust/Kconfig               | 11 +++++
 samples/rust/Makefile              |  1 +
 samples/rust/rust_driver_serdev.rs | 86 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 98 insertions(+)

diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig
index c49ab9106345..a421470d2c52 100644
--- a/samples/rust/Kconfig
+++ b/samples/rust/Kconfig
@@ -161,6 +161,17 @@ config SAMPLE_RUST_DRIVER_AUXILIARY
 
 	  If unsure, say N.
 
+config SAMPLE_RUST_DRIVER_SERDEV
+	tristate "Serial Device Bus Device Driver"
+	depends on SERIAL_DEV_BUS
+	help
+	  This option builds the Rust serial device bus driver sample.
+
+	  To compile this as a module, choose M here:
+	  the module will be called rust_driver_serdev.
+
+	  If unsure, say N.
+
 config SAMPLE_RUST_SOC
 	tristate "SoC Driver"
 	select SOC_BUS
diff --git a/samples/rust/Makefile b/samples/rust/Makefile
index 6c0aaa58cccc..b986b681cde5 100644
--- a/samples/rust/Makefile
+++ b/samples/rust/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_SAMPLE_RUST_DRIVER_PLATFORM)	+= rust_driver_platform.o
 obj-$(CONFIG_SAMPLE_RUST_DRIVER_USB)		+= rust_driver_usb.o
 obj-$(CONFIG_SAMPLE_RUST_DRIVER_FAUX)		+= rust_driver_faux.o
 obj-$(CONFIG_SAMPLE_RUST_DRIVER_AUXILIARY)	+= rust_driver_auxiliary.o
+obj-$(CONFIG_SAMPLE_RUST_DRIVER_SERDEV)		+= rust_driver_serdev.o
 obj-$(CONFIG_SAMPLE_RUST_CONFIGFS)		+= rust_configfs.o
 obj-$(CONFIG_SAMPLE_RUST_SOC)			+= rust_soc.o
 
diff --git a/samples/rust/rust_driver_serdev.rs b/samples/rust/rust_driver_serdev.rs
new file mode 100644
index 000000000000..8cf3fb451b22
--- /dev/null
+++ b/samples/rust/rust_driver_serdev.rs
@@ -0,0 +1,86 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Rust Serial device bus device driver sample.
+
+use kernel::{
+    acpi,
+    device::{
+        Bound,
+        Core, //
+    },
+    of,
+    prelude::*,
+    serdev,
+    sync::aref::ARef, //
+};
+
+struct SampleDriver {
+    sdev: ARef<serdev::Device>,
+}
+
+kernel::of_device_table!(
+    OF_TABLE,
+    MODULE_OF_TABLE,
+    <SampleDriver as serdev::Driver>::IdInfo,
+    [(of::DeviceId::new(c"test,rust_driver_serdev"), ())]
+);
+
+kernel::acpi_device_table!(
+    ACPI_TABLE,
+    MODULE_ACPI_TABLE,
+    <SampleDriver as serdev::Driver>::IdInfo,
+    [(acpi::DeviceId::new(c"LNUXBEEF"), ())]
+);
+
+#[vtable]
+impl serdev::Driver for SampleDriver {
+    type IdInfo = ();
+    const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE);
+    const ACPI_ID_TABLE: Option<acpi::IdTable<Self::IdInfo>> = Some(&ACPI_TABLE);
+
+    fn probe(
+        sdev: &serdev::Device<Core>,
+        _info: Option<&Self::IdInfo>,
+    ) -> impl PinInit<Self, Error> {
+        let dev = sdev.as_ref();
+
+        dev_dbg!(dev, "Probe Rust Serial device bus device driver sample.\n");
+
+        if sdev
+            .set_baudrate(
+                dev.fwnode()
+                    .and_then(|fwnode| fwnode.property_read(c"baudrate").optional())
+                    .unwrap_or(115200),
+            )
+            .is_err()
+        {
+            return Err(EINVAL);
+        }
+        sdev.set_flow_control(false);
+        sdev.set_parity(serdev::Parity::None)?;
+
+        Ok(Self { sdev: sdev.into() })
+    }
+
+    fn receive(sdev: &serdev::Device<Bound>, _this: Pin<&Self>, data: &[u8]) -> usize {
+        let _ = sdev.write_all(data, serdev::Timeout::Max);
+        data.len()
+    }
+}
+
+impl Drop for SampleDriver {
+    fn drop(&mut self) {
+        dev_dbg!(
+            self.sdev.as_ref(),
+            "Remove Rust Serial device bus device driver sample.\n"
+        );
+    }
+}
+
+kernel::module_serdev_device_driver! {
+    type: SampleDriver,
+    name: "rust_driver_serdev",
+    authors: ["Markus Probst"],
+    description: "Rust Serial device bus device driver",
+    license: "GPL v2",
+}

-- 
2.52.0


^ permalink raw reply related

* [PATCH v2 3/4] rust: add basic serial device bus abstractions
From: Markus Probst @ 2026-03-06 19:35 UTC (permalink / raw)
  To: Rob Herring, Greg Kroah-Hartman, Jiri Slaby, Miguel Ojeda,
	Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Alice Ryhl, Trevor Gross, Danilo Krummrich, Kari Argillander,
	Rafael J. Wysocki, Viresh Kumar, Boqun Feng, David Airlie,
	Simona Vetter, Boqun Feng
  Cc: linux-serial, linux-kernel, rust-for-linux, linux-pm, driver-core,
	dri-devel, Markus Probst
In-Reply-To: <20260306-rust_serdev-v2-0-e9b23b42b255@posteo.de>

Implement the basic serial device bus abstractions required to write a
serial device bus device driver with or without the need for initial device
data. This includes the following data structures:

The `serdev::Driver` trait represents the interface to the driver.

The `serdev::Device` abstraction represents a `struct serdev_device`.

In order to provide the Serdev specific parts to a generic
`driver::Registration` the `driver::RegistrationOps` trait is
implemented by `serdev::Adapter`.

Signed-off-by: Markus Probst <markus.probst@posteo.de>
---
 rust/bindings/bindings_helper.h |   1 +
 rust/helpers/helpers.c          |   1 +
 rust/helpers/serdev.c           |  22 ++
 rust/kernel/lib.rs              |   2 +
 rust/kernel/serdev.rs           | 533 ++++++++++++++++++++++++++++++++++++++++
 5 files changed, 559 insertions(+)

diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index 083cc44aa952..ab521ba42673 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -80,6 +80,7 @@
 #include <linux/regulator/consumer.h>
 #include <linux/sched.h>
 #include <linux/security.h>
+#include <linux/serdev.h>
 #include <linux/slab.h>
 #include <linux/sys_soc.h>
 #include <linux/task_work.h>
diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
index a3c42e51f00a..9b87e9591cfd 100644
--- a/rust/helpers/helpers.c
+++ b/rust/helpers/helpers.c
@@ -53,6 +53,7 @@
 #include "regulator.c"
 #include "scatterlist.c"
 #include "security.c"
+#include "serdev.c"
 #include "signal.c"
 #include "slab.c"
 #include "spinlock.c"
diff --git a/rust/helpers/serdev.c b/rust/helpers/serdev.c
new file mode 100644
index 000000000000..c52b78ca3fc7
--- /dev/null
+++ b/rust/helpers/serdev.c
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/serdev.h>
+
+__rust_helper
+void rust_helper_serdev_device_driver_unregister(struct serdev_device_driver *sdrv)
+{
+	serdev_device_driver_unregister(sdrv);
+}
+
+__rust_helper
+void rust_helper_serdev_device_put(struct serdev_device *serdev)
+{
+	serdev_device_put(serdev);
+}
+
+__rust_helper
+void rust_helper_serdev_device_set_client_ops(struct serdev_device *serdev,
+					      const struct serdev_device_ops *ops)
+{
+	serdev_device_set_client_ops(serdev, ops);
+}
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index 3da92f18f4ee..1d337e112922 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -140,6 +140,8 @@
 pub mod scatterlist;
 pub mod security;
 pub mod seq_file;
+#[cfg(CONFIG_SERIAL_DEV_BUS)]
+pub mod serdev;
 pub mod sizes;
 pub mod slice;
 #[cfg(CONFIG_SOC_BUS)]
diff --git a/rust/kernel/serdev.rs b/rust/kernel/serdev.rs
new file mode 100644
index 000000000000..cba5fb6245f7
--- /dev/null
+++ b/rust/kernel/serdev.rs
@@ -0,0 +1,533 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Abstractions for the serial device bus.
+//!
+//! C header: [`include/linux/serdev.h`](srctree/include/linux/serdev.h)
+
+use crate::{
+    acpi,
+    device,
+    devres,
+    driver,
+    error::{from_result, to_result, VTABLE_DEFAULT_ERROR},
+    of,
+    prelude::*,
+    sync::Completion,
+    time::{msecs_to_jiffies, Jiffies, Msecs},
+    types::{AlwaysRefCounted, ForeignOwnable, Opaque}, //
+};
+
+use core::{
+    cell::UnsafeCell,
+    marker::PhantomData,
+    mem::offset_of,
+    num::NonZero,
+    ptr::NonNull, //
+};
+
+/// Parity bit to use with a serial device.
+#[repr(u32)]
+pub enum Parity {
+    /// No parity bit.
+    None = bindings::serdev_parity_SERDEV_PARITY_NONE,
+    /// Even partiy.
+    Even = bindings::serdev_parity_SERDEV_PARITY_EVEN,
+    /// Odd parity.
+    Odd = bindings::serdev_parity_SERDEV_PARITY_ODD,
+}
+
+/// Timeout in Jiffies.
+pub enum Timeout {
+    /// Wait for a specific amount of [`Jiffies`].
+    Jiffies(NonZero<Jiffies>),
+    /// Wait for a specific amount of [`Msecs`].
+    Milliseconds(NonZero<Msecs>),
+    /// Wait as long as possible.
+    ///
+    /// This is equivalent to [`kernel::task::MAX_SCHEDULE_TIMEOUT`].
+    Max,
+}
+
+impl Timeout {
+    fn into_jiffies(self) -> isize {
+        match self {
+            Self::Jiffies(value) => value.get().try_into().unwrap_or_default(),
+            Self::Milliseconds(value) => {
+                msecs_to_jiffies(value.get()).try_into().unwrap_or_default()
+            }
+            Self::Max => 0,
+        }
+    }
+}
+
+/// An adapter for the registration of serial device bus device drivers.
+pub struct Adapter<T: Driver>(T);
+
+// SAFETY:
+// - `bindings::serdev_device_driver` is a C type declared as `repr(C)`.
+// - `Drvdata<T>` is the type of the driver's device private data.
+// - `struct serdev_device_driver` embeds a `struct device_driver`.
+// - `DEVICE_DRIVER_OFFSET` is the correct byte offset to the embedded `struct device_driver`.
+unsafe impl<T: Driver + 'static> driver::DriverLayout for Adapter<T> {
+    type DriverType = bindings::serdev_device_driver;
+    type DriverData = T;
+    const DEVICE_DRIVER_OFFSET: usize = core::mem::offset_of!(Self::DriverType, driver);
+}
+
+// SAFETY: A call to `unregister` for a given instance of `DriverType` is guaranteed to be valid if
+// a preceding call to `register` has been successful.
+unsafe impl<T: Driver + 'static> driver::RegistrationOps for Adapter<T> {
+    unsafe fn register(
+        sdrv: &Opaque<Self::DriverType>,
+        name: &'static CStr,
+        module: &'static ThisModule,
+    ) -> Result {
+        let of_table = match T::OF_ID_TABLE {
+            Some(table) => table.as_ptr(),
+            None => core::ptr::null(),
+        };
+
+        let acpi_table = match T::ACPI_ID_TABLE {
+            Some(table) => table.as_ptr(),
+            None => core::ptr::null(),
+        };
+
+        // SAFETY: It's safe to set the fields of `struct serdev_device_driver` on initialization.
+        unsafe {
+            (*sdrv.get()).driver.name = name.as_char_ptr();
+            (*sdrv.get()).probe = Some(Self::probe_callback);
+            (*sdrv.get()).remove = Some(Self::remove_callback);
+            (*sdrv.get()).driver.of_match_table = of_table;
+            (*sdrv.get()).driver.acpi_match_table = acpi_table;
+        }
+
+        // SAFETY: `sdrv` is guaranteed to be a valid `DriverType`.
+        to_result(unsafe { bindings::__serdev_device_driver_register(sdrv.get(), module.0) })
+    }
+
+    unsafe fn unregister(sdrv: &Opaque<Self::DriverType>) {
+        // SAFETY: `sdrv` is guaranteed to be a valid `DriverType`.
+        unsafe { bindings::serdev_device_driver_unregister(sdrv.get()) };
+    }
+}
+
+#[pin_data]
+struct PrivateData {
+    #[pin]
+    probe_complete: Completion,
+    error: UnsafeCell<bool>,
+}
+
+impl<T: Driver + 'static> Adapter<T> {
+    const OPS: &'static bindings::serdev_device_ops = &bindings::serdev_device_ops {
+        receive_buf: if T::HAS_RECEIVE {
+            Some(Self::receive_buf_callback)
+        } else {
+            None
+        },
+        write_wakeup: Some(bindings::serdev_device_write_wakeup),
+    };
+
+    extern "C" fn probe_callback(sdev: *mut bindings::serdev_device) -> kernel::ffi::c_int {
+        // SAFETY: The serial device bus only ever calls the probe callback with a valid pointer to
+        // a `struct serdev_device`.
+        //
+        // INVARIANT: `sdev` is valid for the duration of `probe_callback()`.
+        let sdev = unsafe { &*sdev.cast::<Device<device::CoreInternal>>() };
+        let id_info = <Self as driver::Adapter>::id_info(sdev.as_ref());
+
+        from_result(|| {
+            let private_data = devres::register(
+                sdev.as_ref(),
+                try_pin_init!(PrivateData {
+                    probe_complete <- Completion::new(),
+                    error: false.into(),
+                }),
+                GFP_KERNEL,
+            )?;
+
+            // SAFETY: `sdev.as_raw()` is guaranteed to be a valid pointer to `serdev_device`.
+            unsafe {
+                (*sdev.as_raw()).private_data =
+                    (&raw const *private_data).cast::<c_void>().cast_mut()
+            };
+
+            // SAFETY: `sdev.as_raw()` is guaranteed to be a valid pointer to `serdev_device`.
+            unsafe { bindings::serdev_device_set_client_ops(sdev.as_raw(), Self::OPS) };
+
+            // SAFETY: The serial device bus only ever calls the probe callback with a valid pointer
+            // to a `serdev_device`.
+            to_result(unsafe {
+                bindings::devm_serdev_device_open(sdev.as_ref().as_raw(), sdev.as_raw())
+            })?;
+
+            let data = T::probe(sdev, id_info);
+            let result = sdev.as_ref().set_drvdata(data);
+
+            // SAFETY: We have exclusive access to `private_data.error`.
+            unsafe { *private_data.error.get() = result.is_err() };
+
+            private_data.probe_complete.complete_all();
+
+            result.map(|()| 0)
+        })
+    }
+
+    extern "C" fn remove_callback(sdev: *mut bindings::serdev_device) {
+        // SAFETY: The serial device bus only ever calls the remove callback with a valid pointer
+        // to a `struct serdev_device`.
+        //
+        // INVARIANT: `sdev` is valid for the duration of `remove_callback()`.
+        let sdev = unsafe { &*sdev.cast::<Device<device::CoreInternal>>() };
+
+        // SAFETY: `remove_callback` is only ever called after a successful call to
+        // `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called
+        // and stored a `Pin<KBox<T>>`.
+        let data = unsafe { sdev.as_ref().drvdata_borrow::<T>() };
+
+        T::unbind(sdev, data);
+
+        // SAFETY:
+        // - `sdev.as_raw()` is guaranteed to be a pointer to a valid `serdev_device`.
+        // - `remove_callback` is only ever called after a successful call to
+        //   `probe_callback`, hence it's guaranteed that `private_data` is a valid pointer to
+        //   `PrivateData` and is stored in a `Pin<KBox<PrivateData>>`.
+        // - `private_data` will not be accessed after this function.
+        drop(unsafe { <Pin<KBox<PrivateData>>>::from_foreign((*sdev.as_raw()).private_data) });
+    }
+
+    extern "C" fn receive_buf_callback(
+        sdev: *mut bindings::serdev_device,
+        buf: *const u8,
+        length: usize,
+    ) -> usize {
+        // SAFETY: The serial device bus only ever calls the receive buf callback with a valid
+        // pointer to a `struct serdev_device`.
+        //
+        // INVARIANT: `sdev` is valid for the duration of `receive_buf_callback()`.
+        let sdev = unsafe { &*sdev.cast::<Device<device::CoreInternal>>() };
+
+        // SAFETY: `receive_buf_callback` is only ever called after a successful call to
+        // `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called
+        // and stored a `Pin<KBox<T>>`.
+        let data = unsafe { sdev.as_ref().drvdata_borrow::<T>() };
+
+        // SAFETY:
+        // - The serial device bus only ever calls the receive buf callback with a valid pointer to
+        //   a `struct serdev_device`.
+        // - `receive_buf_callback` is only ever called after a successful call to
+        //   `probe_callback`, hence it's guaranteed that `sdev.private_data` is a pointer
+        //   to a valid `PrivateData`.
+        let private_data = unsafe { &*(*sdev.as_raw()).private_data.cast::<PrivateData>() };
+
+        private_data.probe_complete.complete_all();
+
+        // SAFETY: No one has exclusive access to `private_data.error`.
+        if unsafe { *private_data.error.get() } {
+            return length;
+        }
+
+        // SAFETY: `buf` is guaranteed to be non-null and has the size of `length`.
+        let buf = unsafe { core::slice::from_raw_parts(buf, length) };
+
+        T::receive(sdev, data, buf)
+    }
+}
+
+impl<T: Driver + 'static> driver::Adapter for Adapter<T> {
+    type IdInfo = T::IdInfo;
+
+    fn of_id_table() -> Option<of::IdTable<Self::IdInfo>> {
+        T::OF_ID_TABLE
+    }
+
+    fn acpi_id_table() -> Option<acpi::IdTable<Self::IdInfo>> {
+        T::ACPI_ID_TABLE
+    }
+}
+
+/// Declares a kernel module that exposes a single serial device bus device driver.
+///
+/// # Examples
+///
+/// ```ignore
+/// kernel::module_serdev_device_driver! {
+///     type: MyDriver,
+///     name: "Module name",
+///     authors: ["Author name"],
+///     description: "Description",
+///     license: "GPL v2",
+/// }
+/// ```
+#[macro_export]
+macro_rules! module_serdev_device_driver {
+    ($($f:tt)*) => {
+        $crate::module_driver!(<T>, $crate::serdev::Adapter<T>, { $($f)* });
+    };
+}
+
+/// The serial device bus device driver trait.
+///
+/// Drivers must implement this trait in order to get a serial device bus device driver registered.
+///
+/// # Examples
+///
+///```
+/// # use kernel::{
+///     acpi,
+///     bindings,
+///     device::{
+///         Bound,
+///         Core, //
+///     },
+///     of,
+///     serdev, //
+/// };
+///
+/// struct MyDriver;
+///
+/// kernel::of_device_table!(
+///     OF_TABLE,
+///     MODULE_OF_TABLE,
+///     <MyDriver as serdev::Driver>::IdInfo,
+///     [
+///         (of::DeviceId::new(c"test,device"), ())
+///     ]
+/// );
+///
+/// kernel::acpi_device_table!(
+///     ACPI_TABLE,
+///     MODULE_ACPI_TABLE,
+///     <MyDriver as serdev::Driver>::IdInfo,
+///     [
+///         (acpi::DeviceId::new(c"LNUXBEEF"), ())
+///     ]
+/// );
+///
+/// #[vtable]
+/// impl serdev::Driver for MyDriver {
+///     type IdInfo = ();
+///     const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE);
+///     const ACPI_ID_TABLE: Option<acpi::IdTable<Self::IdInfo>> = Some(&ACPI_TABLE);
+///
+///     fn probe(
+///         sdev: &serdev::Device<Core>,
+///         _id_info: Option<&Self::IdInfo>,
+///     ) -> impl PinInit<Self, Error> {
+///         sdev.set_baudrate(115200);
+///         sdev.write_all(b"Hello\n", serdev::Timeout::Max)?;
+///         Ok(MyDriver)
+///     }
+/// }
+///```
+#[vtable]
+pub trait Driver: Send {
+    /// The type holding driver private data about each device id supported by the driver.
+    // TODO: Use associated_type_defaults once stabilized:
+    //
+    // ```
+    // type IdInfo: 'static = ();
+    // ```
+    type IdInfo: 'static;
+
+    /// The table of OF device ids supported by the driver.
+    const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = None;
+
+    /// The table of ACPI device ids supported by the driver.
+    const ACPI_ID_TABLE: Option<acpi::IdTable<Self::IdInfo>> = None;
+
+    /// Serial device bus device driver probe.
+    ///
+    /// Called when a new serial device bus device is added or discovered.
+    /// Implementers should attempt to initialize the device here.
+    fn probe(
+        sdev: &Device<device::Core>,
+        id_info: Option<&Self::IdInfo>,
+    ) -> impl PinInit<Self, Error>;
+
+    /// Serial device bus device driver unbind.
+    ///
+    /// Called when a [`Device`] is unbound from its bound [`Driver`]. Implementing this callback
+    /// is optional.
+    ///
+    /// This callback serves as a place for drivers to perform teardown operations that require a
+    /// `&Device<Core>` or `&Device<Bound>` reference. For instance.
+    ///
+    /// Otherwise, release operations for driver resources should be performed in `Self::drop`.
+    fn unbind(sdev: &Device<device::Core>, this: Pin<&Self>) {
+        let _ = (sdev, this);
+    }
+
+    /// Serial device bus device data receive callback.
+    ///
+    /// Called when data got received from device.
+    ///
+    /// Returns the number of bytes accepted.
+    fn receive(sdev: &Device<device::Bound>, this: Pin<&Self>, data: &[u8]) -> usize {
+        let _ = (sdev, this, data);
+        build_error!(VTABLE_DEFAULT_ERROR)
+    }
+}
+
+/// The serial device bus device representation.
+///
+/// This structure represents the Rust abstraction for a C `struct serdev_device`. The
+/// implementation abstracts the usage of an already existing C `struct serdev_device` within Rust
+/// code that we get passed from the C side.
+///
+/// # Invariants
+///
+/// A [`Device`] instance represents a valid `struct serdev_device` created by the C portion of
+/// the kernel.
+#[repr(transparent)]
+pub struct Device<Ctx: device::DeviceContext = device::Normal>(
+    Opaque<bindings::serdev_device>,
+    PhantomData<Ctx>,
+);
+
+impl<Ctx: device::DeviceContext> Device<Ctx> {
+    fn as_raw(&self) -> *mut bindings::serdev_device {
+        self.0.get()
+    }
+}
+
+impl Device<device::Bound> {
+    /// Set the baudrate in bits per second.
+    ///
+    /// Common baudrates are 115200, 9600, 19200, 57600, 4800.
+    ///
+    /// Use [`Device::write_flush`] before calling this if you have written data prior to this call.
+    pub fn set_baudrate(&self, speed: u32) -> Result<(), u32> {
+        // SAFETY: `self.as_raw()` is guaranteed to be a pointer to a valid `serdev_device`.
+        let ret = unsafe { bindings::serdev_device_set_baudrate(self.as_raw(), speed) };
+        if ret == speed {
+            Ok(())
+        } else {
+            Err(ret)
+        }
+    }
+
+    /// Set if flow control should be enabled.
+    ///
+    /// Use [`Device::write_flush`] before calling this if you have written data prior to this call.
+    pub fn set_flow_control(&self, enable: bool) {
+        // SAFETY: `self.as_raw()` is guaranteed to be a pointer to a valid `serdev_device`.
+        unsafe { bindings::serdev_device_set_flow_control(self.as_raw(), enable) };
+    }
+
+    /// Set parity to use.
+    ///
+    /// Use [`Device::write_flush`] before calling this if you have written data prior to this call.
+    pub fn set_parity(&self, parity: Parity) -> Result {
+        // SAFETY: `self.as_raw()` is guaranteed to be a pointer to a valid `serdev_device`.
+        to_result(unsafe { bindings::serdev_device_set_parity(self.as_raw(), parity as u32) })
+    }
+
+    /// Write data to the serial device until the controller has accepted all the data or has
+    /// been interrupted by a timeout or signal.
+    ///
+    /// Note that any accepted data has only been buffered by the controller. Use
+    /// [ Device::wait_until_sent`] to make sure the controller write buffer has actually been
+    /// emptied.
+    ///
+    /// Returns the number of bytes written (less than `data.len()` if interrupted).
+    /// [`kernel::error::code::ETIMEDOUT`] or [`kernel::error::code::ERESTARTSYS`] if interrupted
+    /// before any bytes were written.
+    pub fn write_all(&self, data: &[u8], timeout: Timeout) -> Result<usize> {
+        // SAFETY:
+        // - `self.as_raw()` is guaranteed to be a pointer to a valid `serdev_device`.
+        // - `data.as_ptr()` is guaranteed to be a valid array pointer with the size of
+        //   `data.len()`.
+        let ret = unsafe {
+            bindings::serdev_device_write(
+                self.as_raw(),
+                data.as_ptr(),
+                data.len(),
+                timeout.into_jiffies(),
+            )
+        };
+        // CAST: negative return values are guaranteed to be between `-MAX_ERRNO` and `-1`,
+        // which always fit into a `i32`.
+        to_result(ret as i32).map(|()| ret.unsigned_abs())
+    }
+
+    /// Write data to the serial device.
+    ///
+    /// If you want to write until the controller has accepted all the data, use
+    /// [`Device::write_all`].
+    ///
+    /// Note that any accepted data has only been buffered by the controller. Use
+    /// [ Device::wait_until_sent`] to make sure the controller write buffer has actually been
+    /// emptied.
+    ///
+    /// Returns the number of bytes written (less than `data.len()` if not enough room in the
+    /// write buffer).
+    pub fn write(&self, data: &[u8]) -> Result<u32> {
+        // SAFETY:
+        // - `self.as_raw()` is guaranteed to be a pointer to a valid `serdev_device`.
+        // - `data.as_ptr()` is guaranteed to be a valid array pointer with the size of
+        //   `data.len()`.
+        let ret =
+            unsafe { bindings::serdev_device_write_buf(self.as_raw(), data.as_ptr(), data.len()) };
+
+        to_result(ret as i32).map(|()| ret.unsigned_abs())
+    }
+
+    /// Send data to the serial device immediately.
+    ///
+    /// Note that this doesn't guarantee that the data has been transmitted.
+    /// Use [`Device::wait_until_sent`] for this purpose.
+    pub fn write_flush(&self) {
+        // SAFETY: `self.as_raw()` is guaranteed to be a pointer to a valid `serdev_device`.
+        unsafe { bindings::serdev_device_write_flush(self.as_raw()) };
+    }
+
+    /// Wait for the data to be sent.
+    ///
+    /// After this function, the write buffer of the controller should be empty.
+    pub fn wait_until_sent(&self, timeout: Timeout) {
+        // SAFETY: `self.as_raw()` is guaranteed to be a pointer to a valid `serdev_device`.
+        unsafe { bindings::serdev_device_wait_until_sent(self.as_raw(), timeout.into_jiffies()) };
+    }
+}
+
+// SAFETY: `serdev::Device` is a transparent wrapper of `struct serdev_device`.
+// The offset is guaranteed to point to a valid device field inside `serdev::Device`.
+unsafe impl<Ctx: device::DeviceContext> device::AsBusDevice<Ctx> for Device<Ctx> {
+    const OFFSET: usize = offset_of!(bindings::serdev_device, dev);
+}
+
+// SAFETY: `Device` is a transparent wrapper of a type that doesn't depend on `Device`'s generic
+// argument.
+kernel::impl_device_context_deref!(unsafe { Device });
+kernel::impl_device_context_into_aref!(Device);
+
+// SAFETY: Instances of `Device` are always reference-counted.
+unsafe impl AlwaysRefCounted for Device {
+    fn inc_ref(&self) {
+        self.as_ref().inc_ref();
+    }
+
+    unsafe fn dec_ref(obj: NonNull<Self>) {
+        // SAFETY: The safety requirements guarantee that the refcount is non-zero.
+        unsafe { bindings::serdev_device_put(obj.cast().as_ptr()) }
+    }
+}
+
+impl<Ctx: device::DeviceContext> AsRef<device::Device<Ctx>> for Device<Ctx> {
+    fn as_ref(&self) -> &device::Device<Ctx> {
+        // SAFETY: By the type invariant of `Self`, `self.as_raw()` is a pointer to a valid
+        // `struct serdev_device`.
+        let dev = unsafe { &raw mut (*self.as_raw()).dev };
+
+        // SAFETY: `dev` points to a valid `struct device`.
+        unsafe { device::Device::from_raw(dev) }
+    }
+}
+
+// SAFETY: A `Device` is always reference-counted and can be released from any thread.
+unsafe impl Send for Device {}
+
+// SAFETY: `Device` can be shared among threads because all methods of `Device`
+// (i.e. `Device<Normal>) are thread safe.
+unsafe impl Sync for Device {}

-- 
2.52.0


^ permalink raw reply related

* [PATCH v2 1/4] rust: devres: return reference in `devres::register`
From: Markus Probst @ 2026-03-06 19:35 UTC (permalink / raw)
  To: Rob Herring, Greg Kroah-Hartman, Jiri Slaby, Miguel Ojeda,
	Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Alice Ryhl, Trevor Gross, Danilo Krummrich, Kari Argillander,
	Rafael J. Wysocki, Viresh Kumar, Boqun Feng, David Airlie,
	Simona Vetter, Boqun Feng
  Cc: linux-serial, linux-kernel, rust-for-linux, linux-pm, driver-core,
	dri-devel, Markus Probst
In-Reply-To: <20260306-rust_serdev-v2-0-e9b23b42b255@posteo.de>

Return the reference to the initialized data in the `devres::register`
function.

This is needed in a following commit (rust: add basic serial device bus
abstractions).

Signed-off-by: Markus Probst <markus.probst@posteo.de>
---
 rust/kernel/cpufreq.rs    |  3 ++-
 rust/kernel/devres.rs     | 15 +++++++++++++--
 rust/kernel/drm/driver.rs |  3 ++-
 3 files changed, 17 insertions(+), 4 deletions(-)

diff --git a/rust/kernel/cpufreq.rs b/rust/kernel/cpufreq.rs
index 76faa1ac8501..8cf86bb8e0f4 100644
--- a/rust/kernel/cpufreq.rs
+++ b/rust/kernel/cpufreq.rs
@@ -1051,7 +1051,8 @@ pub fn new_foreign_owned(dev: &Device<Bound>) -> Result
     where
         T: 'static,
     {
-        devres::register(dev, Self::new()?, GFP_KERNEL)
+        devres::register(dev, Self::new()?, GFP_KERNEL)?;
+        Ok(())
     }
 }
 
diff --git a/rust/kernel/devres.rs b/rust/kernel/devres.rs
index 6afe196be42c..f882bace8601 100644
--- a/rust/kernel/devres.rs
+++ b/rust/kernel/devres.rs
@@ -326,15 +326,26 @@ fn register_foreign<P>(dev: &Device<Bound>, data: P) -> Result
 /// }
 ///
 /// fn from_bound_context(dev: &Device<Bound>) -> Result {
-///     devres::register(dev, Registration::new(), GFP_KERNEL)
+///     devres::register(dev, Registration::new(), GFP_KERNEL)?;
+///     Ok(())
 /// }
 /// ```
-pub fn register<T, E>(dev: &Device<Bound>, data: impl PinInit<T, E>, flags: Flags) -> Result
+pub fn register<'a, T, E>(
+    dev: &'a Device<Bound>,
+    data: impl PinInit<T, E>,
+    flags: Flags,
+) -> Result<&'a T>
 where
     T: Send + 'static,
     Error: From<E>,
 {
     let data = KBox::pin_init(data, flags)?;
 
+    let data_ptr = &raw const *data;
+
     register_foreign(dev, data)
+        // SAFETY: `dev` is valid for the lifetime of 'a. As long as there is a reference to
+        // `Device<Bound>`, it is guaranteed that the device is not unbound and data has not been
+        // dropped. Thus `data_ptr` is also valid for the lifetime of 'a.
+        .map(|()| unsafe { &*data_ptr })
 }
diff --git a/rust/kernel/drm/driver.rs b/rust/kernel/drm/driver.rs
index e09f977b5b51..51e0c7e30cc2 100644
--- a/rust/kernel/drm/driver.rs
+++ b/rust/kernel/drm/driver.rs
@@ -145,7 +145,8 @@ pub fn new_foreign_owned(
 
         let reg = Registration::<T>::new(drm, flags)?;
 
-        devres::register(dev, reg, GFP_KERNEL)
+        devres::register(dev, reg, GFP_KERNEL)?;
+        Ok(())
     }
 
     /// Returns a reference to the `Device` instance for this registration.

-- 
2.52.0


^ permalink raw reply related

* [PATCH v2 2/4] serdev: add private data to serdev_device
From: Markus Probst @ 2026-03-06 19:35 UTC (permalink / raw)
  To: Rob Herring, Greg Kroah-Hartman, Jiri Slaby, Miguel Ojeda,
	Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Alice Ryhl, Trevor Gross, Danilo Krummrich, Kari Argillander,
	Rafael J. Wysocki, Viresh Kumar, Boqun Feng, David Airlie,
	Simona Vetter, Boqun Feng
  Cc: linux-serial, linux-kernel, rust-for-linux, linux-pm, driver-core,
	dri-devel, Markus Probst
In-Reply-To: <20260306-rust_serdev-v2-0-e9b23b42b255@posteo.de>

Add private data to `struct serdev_device`, as it is required by the
rust abstraction added in the following commit
(rust: add basic serial device bus abstractions).

Signed-off-by: Markus Probst <markus.probst@posteo.de>
---
 include/linux/serdev.h | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/include/linux/serdev.h b/include/linux/serdev.h
index 5654c58eb73c..b591af23faf0 100644
--- a/include/linux/serdev.h
+++ b/include/linux/serdev.h
@@ -33,12 +33,13 @@ struct serdev_device_ops {
 
 /**
  * struct serdev_device - Basic representation of an serdev device
- * @dev:	Driver model representation of the device.
- * @nr:		Device number on serdev bus.
- * @ctrl:	serdev controller managing this device.
- * @ops:	Device operations.
- * @write_comp	Completion used by serdev_device_write() internally
- * @write_lock	Lock to serialize access when writing data
+ * @dev:	 Driver model representation of the device.
+ * @nr:		 Device number on serdev bus.
+ * @ctrl:	 serdev controller managing this device.
+ * @ops:	 Device operations.
+ * @write_comp	 Completion used by serdev_device_write() internally
+ * @write_lock	 Lock to serialize access when writing data
+ * @private_data Private data for the device driver.
  */
 struct serdev_device {
 	struct device dev;
@@ -47,6 +48,7 @@ struct serdev_device {
 	const struct serdev_device_ops *ops;
 	struct completion write_comp;
 	struct mutex write_lock;
+	void *private_data;
 };
 
 static inline struct serdev_device *to_serdev_device(struct device *d)

-- 
2.52.0


^ permalink raw reply related

* [PATCH v2 0/4] rust: add basic serial device bus abstractions
From: Markus Probst @ 2026-03-06 19:35 UTC (permalink / raw)
  To: Rob Herring, Greg Kroah-Hartman, Jiri Slaby, Miguel Ojeda,
	Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Alice Ryhl, Trevor Gross, Danilo Krummrich, Kari Argillander,
	Rafael J. Wysocki, Viresh Kumar, Boqun Feng, David Airlie,
	Simona Vetter, Boqun Feng
  Cc: linux-serial, linux-kernel, rust-for-linux, linux-pm, driver-core,
	dri-devel, Markus Probst

This patch series adds the serdev device bus rust abstraction into the
kernel.

This abstraction will be used by a driver,
which targets the MCU devices in Synology devices.

Kari Argillander also messaged me, stating that he wants to write a
watchdog driver with this abstraction (needing initial device data).

@Rob: Are you willing to maintain these rust abstractions yourself,
as you are the expert on this subsystem, otherwise I would take care of
it with a "SERIAL DEVICE BUS [RUST]" section in the MAINTAINERS file. In
the second case, I assume you are going to pick those patches as-is into
your tree, after they have been reviewed?

Signed-off-by: Markus Probst <markus.probst@posteo.de>
---
Changes in v2:
- fix documentation in `serdev::Driver::write` and
  `serdev::Driver::write_all`
- remove use of `dev_info` in probe from the sample
- remove `properties_parse` from the sample
- add optional `baudrate` property to the sample
- remove 1. patch
- remove `TryFrom<&device::Device<Ctx>> for &serdev::Device<Ctx>`
  implementation
- fix import style
- add patch to return reference in `devres::register` to fix safety
  issue
- add patch to add private data to serdev_device, to fix
  `Device.drvdata()` from failing
- simplify abstraction by removing ability to receive the initial
  transmission. It may be added later in a separate patch series if
  needed.
- Link to v1: https://lore.kernel.org/r/20251220-rust_serdev-v1-0-e44645767621@posteo.de

---
Markus Probst (4):
      rust: devres: return reference in `devres::register`
      serdev: add private data to serdev_device
      rust: add basic serial device bus abstractions
      samples: rust: add Rust serial device bus sample device driver

 include/linux/serdev.h             |  14 +-
 rust/bindings/bindings_helper.h    |   1 +
 rust/helpers/helpers.c             |   1 +
 rust/helpers/serdev.c              |  22 ++
 rust/kernel/cpufreq.rs             |   3 +-
 rust/kernel/devres.rs              |  15 +-
 rust/kernel/drm/driver.rs          |   3 +-
 rust/kernel/lib.rs                 |   2 +
 rust/kernel/serdev.rs              | 533 +++++++++++++++++++++++++++++++++++++
 samples/rust/Kconfig               |  11 +
 samples/rust/Makefile              |   1 +
 samples/rust/rust_driver_serdev.rs |  86 ++++++
 12 files changed, 682 insertions(+), 10 deletions(-)
---
base-commit: 11439c4635edd669ae435eec308f4ab8a0804808
change-id: 20251217-rust_serdev-ee5481e9085c


^ permalink raw reply

* Re: [PATCH 0/3] vt: add modifier support to cursor and navigation keys
From: Nicolas Pitre @ 2026-03-06 18:26 UTC (permalink / raw)
  To: Greg Kroah-Hartman; +Cc: Jiri Slaby, Alexey Gladkov, linux-serial, linux-kernel
In-Reply-To: <2026021006-reenact-suave-c86c@gregkh>

On Tue, 10 Feb 2026, Greg Kroah-Hartman wrote:

> On Sun, Feb 08, 2026 at 11:22:27AM -0500, Nicolas Pitre wrote:
> > On Sun, 8 Feb 2026, Greg Kroah-Hartman wrote:
> > 
> > > Argh, sorry, I saw this patch series too late for this merge window.
> > > I'll review it after -rc1 is out.
> > 
> > Too bad.
> > 
> > But please at least consider  this one now
> > https://lkml.org/lkml/2026/1/27/1886
> > and queue it for the stable tree as well.
> 
> Sorry, that slipped through as well, I'll grab it after -rc1 is out and
> get it backported to stable kernels.

Ping.  ;-)


Nicolas

^ permalink raw reply

* Re: [RFC PATCH 3/7] drivers/tty/serial/serial_core: ratelimit uart_wait_until_sent
From: jim.cromie @ 2026-03-06 14:02 UTC (permalink / raw)
  To: Jiri Slaby
  Cc: linux-kernel, dri-devel, amd-gfx, intel-gvt-dev, intel-gfx,
	Greg Kroah-Hartman, Petr Mladek, Ilpo Järvinen,
	Dr. David Alan Gilbert, Joseph Tilahun, linux-serial
In-Reply-To: <51fed793-869b-4a5b-b90f-2ba80c13d773@kernel.org>

On Thu, Mar 5, 2026 at 11:32 PM Jiri Slaby <jirislaby@kernel.org> wrote:
>
> On 06. 03. 26, 2:50, Jim Cromie wrote:
> > Ratelimiting these pr_debug()s can reduce the console flood during
> > bulk dynamic-debug activation, in environments where a serial console
> > is used.
> >
> > Signed-off-by: Jim Cromie <jim.cromie@gmail.com>
> > ---
> >   drivers/tty/serial/serial_core.c | 4 ++--
> >   1 file changed, 2 insertions(+), 2 deletions(-)
> >
> > diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
> > index 487756947a96..6db465619c70 100644
> > --- a/drivers/tty/serial/serial_core.c
> > +++ b/drivers/tty/serial/serial_core.c
> > @@ -1790,8 +1790,8 @@ static void uart_wait_until_sent(struct tty_struct *tty, int timeout)
> >
> >       expire = jiffies + timeout;
> >
> > -     pr_debug("uart_wait_until_sent(%u), jiffies=%lu, expire=%lu...\n",
> > -             port->line, jiffies, expire);
> > +     pr_debug_ratelimited("waiting on (%u) jiffies=%lu, expire=%lu...\n",
> > +                          port->line, jiffies, expire);
>
> The changed message does not make any sense.
>

Ackn.  Given the narrow rate-limiting purpose,
I should have ignored the checkpatch warning
and kept the message as is.

In any case, my test setup didnt actually use serio,
and didnt get flooded by it, so I dont know that this
single change would be enough to fix it.

I withdraw this particular patch.

>
> --
> js
> suse labs

^ 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