From 3386c50d66759e4e6ddedabc178db3db33836aa6 Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Fri, 3 Jul 2026 10:47:14 +0200 Subject: [PATCH 01/51] goldfish: remove unused gf_write_dma_addr() The last user was removed in 2020 by commit c869eaa617e4 ("drivers: staging: retire drivers/staging/goldfish"). Drop it. Signed-off-by: Jiri Slaby (SUSE) Link: https://patch.msgid.link/20260703084717.176442-1-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman --- include/linux/goldfish.h | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/include/linux/goldfish.h b/include/linux/goldfish.h index bcc17f95b906..40a059e03d78 100644 --- a/include/linux/goldfish.h +++ b/include/linux/goldfish.h @@ -26,15 +26,4 @@ static inline void gf_write_ptr(const void *ptr, void __iomem *portl, #endif } -static inline void gf_write_dma_addr(const dma_addr_t addr, - void __iomem *portl, - void __iomem *porth) -{ - gf_iowrite32(lower_32_bits(addr), portl); -#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT - gf_iowrite32(upper_32_bits(addr), porth); -#endif -} - - #endif /* __LINUX_GOLDFISH_H */ From e2dcf364de2c5453d1f594712651a0275b0c1fe5 Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Fri, 3 Jul 2026 10:47:15 +0200 Subject: [PATCH 02/51] tty: goldfish: drop unused goldfish_tty::opencount The field was never used. Signed-off-by: Jiri Slaby (SUSE) Link: https://patch.msgid.link/20260703084717.176442-2-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/tty/goldfish.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/tty/goldfish.c b/drivers/tty/goldfish.c index 12d08de59095..ba060ce4e9b5 100644 --- a/drivers/tty/goldfish.c +++ b/drivers/tty/goldfish.c @@ -38,7 +38,6 @@ struct goldfish_tty { spinlock_t lock; void __iomem *base; u32 irq; - int opencount; struct console console; u32 version; struct device *dev; From e31bd02f19ddb01c1e1fb6d79b72ace8f014cb27 Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Fri, 3 Jul 2026 10:47:16 +0200 Subject: [PATCH 03/51] tty: goldfish: move gf_write_ptr() to tty/goldfish.c tty/goldfish.c is the only user of gf_write_ptr(). Move it there, drop the unneeded casts, and name it appropriately. FTR, the last non-tty user was removed in 2018 by 4ae0fe70a097 ("Delete the goldfish_nand driver."). Signed-off-by: Jiri Slaby (SUSE) Link: https://patch.msgid.link/20260703084717.176442-3-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/tty/goldfish.c | 13 +++++++++++-- include/linux/goldfish.h | 13 ------------- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/drivers/tty/goldfish.c b/drivers/tty/goldfish.c index ba060ce4e9b5..fb135bf5996c 100644 --- a/drivers/tty/goldfish.c +++ b/drivers/tty/goldfish.c @@ -18,6 +18,7 @@ #include #include #include +#include /* Goldfish tty register's offsets */ #define GOLDFISH_TTY_REG_BYTES_READY 0x04 @@ -49,6 +50,14 @@ static u32 goldfish_tty_line_count = 8; static u32 goldfish_tty_current_line_count; static struct goldfish_tty *goldfish_ttys; +static inline void gf_write_addr(unsigned long addr, void __iomem *portl, void __iomem *porth) +{ + gf_iowrite32(lower_32_bits(addr), portl); +#ifdef CONFIG_64BIT + gf_iowrite32(upper_32_bits(addr), porth); +#endif +} + static void do_rw_io(struct goldfish_tty *qtty, unsigned long address, size_t count, bool is_write) { @@ -56,8 +65,8 @@ static void do_rw_io(struct goldfish_tty *qtty, unsigned long address, void __iomem *base = qtty->base; spin_lock_irqsave(&qtty->lock, irq_flags); - gf_write_ptr((void *)address, base + GOLDFISH_TTY_REG_DATA_PTR, - base + GOLDFISH_TTY_REG_DATA_PTR_HIGH); + gf_write_addr(address, base + GOLDFISH_TTY_REG_DATA_PTR, + base + GOLDFISH_TTY_REG_DATA_PTR_HIGH); gf_iowrite32(count, base + GOLDFISH_TTY_REG_DATA_LEN); if (is_write) diff --git a/include/linux/goldfish.h b/include/linux/goldfish.h index 40a059e03d78..98a4719c6776 100644 --- a/include/linux/goldfish.h +++ b/include/linux/goldfish.h @@ -2,8 +2,6 @@ #ifndef __LINUX_GOLDFISH_H #define __LINUX_GOLDFISH_H -#include -#include #include /* Helpers for Goldfish virtual platform */ @@ -15,15 +13,4 @@ #define gf_iowrite32 iowrite32 #endif -static inline void gf_write_ptr(const void *ptr, void __iomem *portl, - void __iomem *porth) -{ - const unsigned long addr = (unsigned long)ptr; - - gf_iowrite32(lower_32_bits(addr), portl); -#ifdef CONFIG_64BIT - gf_iowrite32(upper_32_bits(addr), porth); -#endif -} - #endif /* __LINUX_GOLDFISH_H */ From e508a176d86f5ca0916ac1caf80806f9ad3d91ef Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Fri, 3 Jul 2026 10:47:17 +0200 Subject: [PATCH 04/51] tty: goldfish: use guard() for locks Using guard()s is cleaner and safer. goldfish_tty_probe() is omitted due to the crossing err_unmap goto-label. Using scoped_guard() does not look that nice there. Perhaps if someone refactored the locked part into a separate function... Signed-off-by: Jiri Slaby (SUSE) Link: https://patch.msgid.link/20260703084717.176442-4-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/tty/goldfish.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/tty/goldfish.c b/drivers/tty/goldfish.c index fb135bf5996c..aace18e5f847 100644 --- a/drivers/tty/goldfish.c +++ b/drivers/tty/goldfish.c @@ -61,10 +61,10 @@ static inline void gf_write_addr(unsigned long addr, void __iomem *portl, void _ static void do_rw_io(struct goldfish_tty *qtty, unsigned long address, size_t count, bool is_write) { - unsigned long irq_flags; void __iomem *base = qtty->base; - spin_lock_irqsave(&qtty->lock, irq_flags); + guard(spinlock_irqsave)(&qtty->lock); + gf_write_addr(address, base + GOLDFISH_TTY_REG_DATA_PTR, base + GOLDFISH_TTY_REG_DATA_PTR_HIGH); gf_iowrite32(count, base + GOLDFISH_TTY_REG_DATA_LEN); @@ -75,8 +75,6 @@ static void do_rw_io(struct goldfish_tty *qtty, unsigned long address, else gf_iowrite32(GOLDFISH_TTY_CMD_READ_BUFFER, base + GOLDFISH_TTY_REG_CMD); - - spin_unlock_irqrestore(&qtty->lock, irq_flags); } static void goldfish_tty_rw(struct goldfish_tty *qtty, unsigned long addr, @@ -418,7 +416,7 @@ static void goldfish_tty_remove(struct platform_device *pdev) { struct goldfish_tty *qtty = platform_get_drvdata(pdev); - mutex_lock(&goldfish_tty_lock); + guard(mutex)(&goldfish_tty_lock); unregister_console(&qtty->console); tty_unregister_device(goldfish_tty_driver, qtty->console.index); @@ -429,7 +427,6 @@ static void goldfish_tty_remove(struct platform_device *pdev) goldfish_tty_current_line_count--; if (goldfish_tty_current_line_count == 0) goldfish_tty_delete_driver(); - mutex_unlock(&goldfish_tty_lock); } #ifdef CONFIG_GOLDFISH_TTY_EARLY_CONSOLE From 0f09902b05062749e4e5a7e4112f35f70b3f2feb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Fri, 22 May 2026 16:01:03 +0200 Subject: [PATCH 05/51] serial: 8250_exar: Consistently define pci_device_ids using named initializers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .driver_data member of the struct pci_device_id array were initialized by list expressions. This isn't easily readable if you're not into PCI, still more given that it's hidden in macros. Using named initializers is more explicit and thus easier to parse for a human. And it's also more robust against changes to the struct definition. The mentioned robustness is relevant for a planned change to struct pci_device_id that replaces .driver_data by an anonymous union. Also drop a few explicit zeros that are not needed and improve indention. This change doesn't introduce changes to the compiled pci_device_id array. Tested on x86 and arm64. Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://patch.msgid.link/20260522140103.769262-2-u.kleine-koenig@baylibre.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_exar.c | 30 ++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index c682c0d0dffa..f9a14eaa13cb 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -1642,14 +1642,14 @@ static const struct exar8250_board pbn_exar_XR17V8358 = { .exit = pci_xr17v35x_exit, }; -#define CTI_EXAR_DEVICE(devid, bd) { \ - PCI_DEVICE_SUB( \ - PCI_VENDOR_ID_EXAR, \ - PCI_DEVICE_ID_EXAR_##devid, \ - PCI_SUBVENDOR_ID_CONNECT_TECH, \ - PCI_ANY_ID), 0, 0, \ - (kernel_ulong_t)&bd \ - } +#define CTI_EXAR_DEVICE(devid, bd) { \ + PCI_DEVICE_SUB( \ + PCI_VENDOR_ID_EXAR, \ + PCI_DEVICE_ID_EXAR_##devid, \ + PCI_SUBVENDOR_ID_CONNECT_TECH, \ + PCI_ANY_ID), \ + .driver_data = (kernel_ulong_t)&bd \ +} #define EXAR_DEVICE(vend, devid, bd) { PCI_DEVICE_DATA(vend, devid, &bd) } @@ -1658,18 +1658,18 @@ static const struct exar8250_board pbn_exar_XR17V8358 = { PCI_VENDOR_ID_EXAR, \ PCI_DEVICE_ID_EXAR_##devid, \ PCI_SUBVENDOR_ID_IBM, \ - PCI_SUBDEVICE_ID_IBM_##sdevid), 0, 0, \ - (kernel_ulong_t)&bd \ - } + PCI_SUBDEVICE_ID_IBM_##sdevid), \ + .driver_data = (kernel_ulong_t)&bd \ +} #define USR_DEVICE(devid, sdevid, bd) { \ PCI_DEVICE_SUB( \ PCI_VENDOR_ID_USR, \ PCI_DEVICE_ID_EXAR_##devid, \ PCI_VENDOR_ID_EXAR, \ - PCI_SUBDEVICE_ID_USR_##sdevid), 0, 0, \ - (kernel_ulong_t)&bd \ - } + PCI_SUBDEVICE_ID_USR_##sdevid), \ + .driver_data = (kernel_ulong_t)&bd \ +} static const struct pci_device_id exar_pci_tbl[] = { EXAR_DEVICE(ACCESSIO, COM_2S, pbn_exar_XR17C15x), @@ -1726,7 +1726,7 @@ static const struct pci_device_id exar_pci_tbl[] = { EXAR_DEVICE(COMMTECH, 4224PCI335, pbn_fastcom335_4), EXAR_DEVICE(COMMTECH, 2324PCI335, pbn_fastcom335_4), EXAR_DEVICE(COMMTECH, 2328PCI335, pbn_fastcom335_8), - { 0, } + { } }; MODULE_DEVICE_TABLE(pci, exar_pci_tbl); From addee0be2481e46a94b21742d2520b8c788e037a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Sun, 24 May 2026 15:19:05 +0200 Subject: [PATCH 06/51] tty: serial: rp2: Use named initializer for pci_device_id::driver_data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .driver_data member of the struct pci_device_id array were initialized by list expressions relying on hidden assignment of .class and .class_mask in PCI_VDEVICE(). Make the initialization more robust by using a named initializer. This robustness is relevant for a planned change to struct pci_device_id that replaces .driver_data by an anonymous union. This change doesn't introduce changes to the compiled pci_device_id array. Tested on x86 and arm64. Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://patch.msgid.link/20260524131905.871222-2-u.kleine-koenig@baylibre.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/rp2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/rp2.c b/drivers/tty/serial/rp2.c index 6d99a02dd439..51e81ec0ffdb 100644 --- a/drivers/tty/serial/rp2.c +++ b/drivers/tty/serial/rp2.c @@ -197,7 +197,7 @@ struct rp2_card { }; #define RP_ID(prod) PCI_VDEVICE(RP, (prod)) -#define RP_CAP(ports, smpte) (((ports) << 8) | ((smpte) << 0)) +#define RP_CAP(ports, smpte) .driver_data = (((ports) << 8) | ((smpte) << 0)) static inline void rp2_decode_cap(const struct pci_device_id *id, int *ports, int *smpte) From 120fc59a20135d0fba20c522997da38e1a8490a6 Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Thu, 28 May 2026 13:24:17 +0300 Subject: [PATCH 07/51] serial: pch: replace __get_free_page() with kmalloc() pch_uart_init_port() allocates a staging buffer for non-DMA receive path using __get_free_page(). This buffer can be allocated with kmalloc() as there's nothing special about it to go directly to the page allocator. kmalloc() provides a better API that does not require ugly casts and kfree() does not need to know the size of the freed object. Replace use of __get_free_page() with kmalloc() and free_page() with kfree(). Link: https://lore.kernel.org/all/635405e4-9423-4a25-a6e7-e03c8ea0bcbe@redhat.com Signed-off-by: Mike Rapoport (Microsoft) Link: https://patch.msgid.link/20260528-b4-tty-v1-1-9da9f7aec5f2@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/pch_uart.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c index 80e31c4d9536..5f7f073f285e 100644 --- a/drivers/tty/serial/pch_uart.c +++ b/drivers/tty/serial/pch_uart.c @@ -1662,7 +1662,7 @@ static struct eg20t_port *pch_uart_init_port(struct pci_dev *pdev, if (priv == NULL) goto init_port_alloc_err; - rxbuf = (unsigned char *)__get_free_page(GFP_KERNEL); + rxbuf = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!rxbuf) goto init_port_free_txbuf; @@ -1735,7 +1735,7 @@ init_port_hal_free: #ifdef CONFIG_SERIAL_PCH_UART_CONSOLE pch_uart_ports[board->line_no] = NULL; #endif - free_page((unsigned long)rxbuf); + kfree(rxbuf); init_port_free_txbuf: kfree(priv); init_port_alloc_err: @@ -1750,7 +1750,7 @@ static void pch_uart_exit_port(struct eg20t_port *priv) snprintf(name, sizeof(name), "uart%d_regs", priv->port.line); debugfs_lookup_and_remove(name, NULL); uart_remove_one_port(&pch_uart_driver, &priv->port); - free_page((unsigned long)priv->rxbuf.buf); + kfree(priv->rxbuf.buf); } static void pch_uart_pci_remove(struct pci_dev *pdev) From d26ed502d0c7c05c273b6e45a0b7569f55720af6 Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Thu, 28 May 2026 13:24:18 +0300 Subject: [PATCH 08/51] tty: amiserial: replace get_zeroed_page() with kzalloc() rs_startup() allocates a transmit ring buffer that is used to buffer reads and writes from/to serial data register. This buffer can be allocated with kmalloc() as there's nothing special about it to go directly to the page allocator. kmalloc() provides a better API that does not require ugly casts and kfree() does not need to know the size of the freed object. Replace use of get_zeroed_page() with kzalloc() and free_page() with kfree(). Link: https://lore.kernel.org/all/635405e4-9423-4a25-a6e7-e03c8ea0bcbe@redhat.com Signed-off-by: Mike Rapoport (Microsoft) Link: https://patch.msgid.link/20260528-b4-tty-v1-2-9da9f7aec5f2@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/tty/amiserial.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c index 81eaca751541..28af0fd98181 100644 --- a/drivers/tty/amiserial.c +++ b/drivers/tty/amiserial.c @@ -443,23 +443,23 @@ static int rs_startup(struct tty_struct *tty, struct serial_state *info) struct tty_port *port = &info->tport; unsigned long flags; int retval=0; - unsigned long page; + void *buffer; - page = get_zeroed_page(GFP_KERNEL); - if (!page) + buffer = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!buffer) return -ENOMEM; local_irq_save(flags); if (tty_port_initialized(port)) { - free_page(page); + kfree(buffer); goto errout; } if (info->xmit.buf) - free_page(page); + kfree(buffer); else - info->xmit.buf = (unsigned char *) page; + info->xmit.buf = buffer; #ifdef SERIAL_DEBUG_OPEN printk("starting up ttys%d ...", info->line); @@ -537,7 +537,7 @@ static void rs_shutdown(struct tty_struct *tty, struct serial_state *info) */ free_irq(IRQ_AMIGA_VERTB, info); - free_page((unsigned long)info->xmit.buf); + kfree(info->xmit.buf); info->xmit.buf = NULL; info->IER = 0; From 274afb49eeb3ac974d74104a55050990caae6faa Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Thu, 28 May 2026 13:24:19 +0300 Subject: [PATCH 09/51] tty: serial: men_z135_uart: replace __get_free_page() with kmalloc() men_z135_probe() allocates a receive staging buffer filled by the CPU via memcpy_fromio() from the device MMIO region. This buffer can be allocated with kmalloc() as there's nothing special about it to go directly to the page allocator. kmalloc() provides a better API that does not require ugly casts and kfree() does not need to know the size of the freed object. Replace use of __get_free_page() with kmalloc() and free_page() with kfree(). Link: https://lore.kernel.org/all/635405e4-9423-4a25-a6e7-e03c8ea0bcbe@redhat.com Signed-off-by: Mike Rapoport (Microsoft) Link: https://patch.msgid.link/20260528-b4-tty-v1-3-9da9f7aec5f2@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/men_z135_uart.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/tty/serial/men_z135_uart.c b/drivers/tty/serial/men_z135_uart.c index 6fad57fee912..9138fa29d301 100644 --- a/drivers/tty/serial/men_z135_uart.c +++ b/drivers/tty/serial/men_z135_uart.c @@ -16,6 +16,7 @@ #include #include #include +#include #define MEN_Z135_MAX_PORTS 12 #define MEN_Z135_BASECLK 29491200 @@ -811,7 +812,7 @@ static int men_z135_probe(struct mcb_device *mdev, if (!uart) return -ENOMEM; - uart->rxbuf = (unsigned char *)__get_free_page(GFP_KERNEL); + uart->rxbuf = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!uart->rxbuf) return -ENOMEM; @@ -841,7 +842,7 @@ static int men_z135_probe(struct mcb_device *mdev, return 0; err: - free_page((unsigned long) uart->rxbuf); + kfree(uart->rxbuf); dev_err(dev, "Failed to add UART: %d\n", err); return err; @@ -858,7 +859,7 @@ static void men_z135_remove(struct mcb_device *mdev) line--; uart_remove_one_port(&men_z135_driver, &uart->port); - free_page((unsigned long) uart->rxbuf); + kfree(uart->rxbuf); } static const struct mcb_device_id men_z135_ids[] = { From de96e8b27b82dc2c773ca3f53bae7bad6e233055 Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Thu, 28 May 2026 13:24:20 +0300 Subject: [PATCH 10/51] vc_screen: replace __get_free_pages() with kmalloc() vcs_read() and vcs_write() allocate staging buffers with __get_free_pages(). These buffers can be allocated with kmalloc() as there's nothing special about them to go directly to the page allocator. kmalloc() provides a better API that does not require ugly casts and it's a modern way of saying "I need a page-sized buffer" Replace use of __get_free_page() with kmalloc() and drop unused now DEFINE_FREE(free_page_ptr ...) Link: https://lore.kernel.org/all/700c5a5f-3128-4671-99aa-827ca73f5cdf@kernel.org Link: https://lore.kernel.org/all/635405e4-9423-4a25-a6e7-e03c8ea0bcbe@redhat.com Signed-off-by: Mike Rapoport (Microsoft) Reviewed-by: Jiri Slaby Link: https://patch.msgid.link/20260528-b4-tty-v1-4-9da9f7aec5f2@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vc_screen.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/tty/vt/vc_screen.c b/drivers/tty/vt/vc_screen.c index 7d40eacc21b3..bf1502fd5bd4 100644 --- a/drivers/tty/vt/vc_screen.c +++ b/drivers/tty/vt/vc_screen.c @@ -53,8 +53,6 @@ #define HEADER_SIZE 4u #define CON_BUF_SIZE (IS_ENABLED(CONFIG_BASE_SMALL) ? 256 : PAGE_SIZE) -DEFINE_FREE(free_page_ptr, void *, if (_T) free_page((unsigned long)_T)); - /* * Our minor space: * @@ -371,7 +369,7 @@ vcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) loff_t pos; bool viewed, attr, uni_mode; - char *con_buf __free(free_page_ptr) = (char *)__get_free_page(GFP_KERNEL); + char *con_buf __free(kfree) = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!con_buf) return -ENOMEM; @@ -596,7 +594,7 @@ vcs_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) if (use_unicode(inode)) return -EOPNOTSUPP; - char *con_buf __free(free_page_ptr) = (char *)__get_free_page(GFP_KERNEL); + char *con_buf __free(kfree) = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!con_buf) return -ENOMEM; From 25b51d1fd3268a219e43608b165098fff7cd9dcd Mon Sep 17 00:00:00 2001 From: Tapio Reijonen Date: Mon, 15 Jun 2026 10:27:35 +0000 Subject: [PATCH 11/51] serial: max310x: register GPIO controller before adding UART ports The MAX310x exposes four GPIOs per UART port via an in-driver gpio_chip. devm_gpiochip_add_data() used to run after the per-port uart_add_one_port() loop, so a device-tree consumer referencing one of the chip's own GPIOs (for example rs485-term-gpios = <&max310x 0 ...>) could not resolve it during port registration: the GPIO provider it waits for is the very driver still trying to register, and the lookup returns -EPROBE_DEFER on its own provider, deferring probe forever. Split the per-port setup into two passes around the gpio_chip registration: 1. Initialise per-port state - port struct fields, regmap binding, IRQ disable, work queues. The gpio_chip callbacks dereference s->p[i].regmap via to_max310x_port() and become callable as soon as the chip is visible to gpiolib, so every entry must be populated first. 2. devm_gpiochip_add_data() - register the gpio_chip. 3. Allocate a line, uart_add_one_port(), set_bit(), max310x_power(). Keeping line allocation, registration and set_bit() together preserves the existing "bit set <=> port registered" rollback invariant that out_uart relies on. Signed-off-by: Tapio Reijonen Link: https://patch.msgid.link/20260615-b4-max310x-rs485-dt-v3-1-7e79f064bdd7@vaisala.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/max310x.c | 54 ++++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index e28e3065c99d..3e26bdf8806b 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -1390,17 +1390,12 @@ static int max310x_probe(struct device *dev, const struct max310x_devtype *devty dev_dbg(dev, "Reference clock set to %i Hz\n", uartclk); + /* + * Set up each port's state before registering the gpiochip, + * since the gpiochip callbacks will read s->p[i].regmap as + * soon as gpiolib exposes the controller. + */ for (i = 0; i < devtype->nr; i++) { - unsigned int line; - - line = find_first_zero_bit(max310x_lines, MAX310X_UART_NRMAX); - if (line == MAX310X_UART_NRMAX) { - ret = -ERANGE; - goto out_uart; - } - - /* Initialize port data */ - s->p[i].port.line = line; s->p[i].port.dev = dev; s->p[i].port.irq = irq; s->p[i].port.type = PORT_MAX310X; @@ -1430,20 +1425,16 @@ static int max310x_probe(struct device *dev, const struct max310x_devtype *devty INIT_WORK(&s->p[i].md_work, max310x_md_proc); /* Initialize queue for changing RS485 mode */ INIT_WORK(&s->p[i].rs_work, max310x_rs_proc); - - /* Register port */ - ret = uart_add_one_port(&max310x_uart, &s->p[i].port); - if (ret) - goto out_uart; - - set_bit(line, max310x_lines); - - /* Go to suspend mode */ - max310x_power(&s->p[i].port, 0); } #ifdef CONFIG_GPIOLIB - /* Setup GPIO controller */ + /* + * Register the GPIO controller before adding the UART ports so + * that consumers referencing the chip's own GPIOs from device + * tree (for example rs485-term-gpios = <&max310x ...>) can + * resolve them at uart_add_one_port() time instead of receiving + * -EPROBE_DEFER from their own provider. + */ s->gpio.owner = THIS_MODULE; s->gpio.parent = dev; s->gpio.label = devtype->name; @@ -1460,6 +1451,27 @@ static int max310x_probe(struct device *dev, const struct max310x_devtype *devty goto out_uart; #endif + for (i = 0; i < devtype->nr; i++) { + unsigned int line; + + line = find_first_zero_bit(max310x_lines, MAX310X_UART_NRMAX); + if (line == MAX310X_UART_NRMAX) { + ret = -ERANGE; + goto out_uart; + } + s->p[i].port.line = line; + + /* Register port */ + ret = uart_add_one_port(&max310x_uart, &s->p[i].port); + if (ret) + goto out_uart; + + set_bit(line, max310x_lines); + + /* Go to suspend mode */ + max310x_power(&s->p[i].port, 0); + } + /* Setup interrupt */ ret = devm_request_threaded_irq(dev, irq, NULL, max310x_ist, IRQF_ONESHOT | IRQF_SHARED, dev_name(dev), s); From f3fd73bf208b622be36b923346e1fa7a69d88a1b Mon Sep 17 00:00:00 2001 From: Tapio Reijonen Date: Mon, 15 Jun 2026 10:27:36 +0000 Subject: [PATCH 12/51] dt-bindings: serial: maxim,max310x: describe per-channel rs485 subnodes The MAX310x is a family of one- (max3107, max3108), two- (max3109) and four-channel (max14830) UARTs. The binding pulls in /schemas/serial/rs485.yaml at the chip level, describing a single set of RS-485 properties - enough for the single-channel parts, but a multi-channel chip can wire RS-485 differently on each channel. Split the binding per compatible: - single-channel parts (max3107, max3108): the chip node is itself the serial port and carries the RS-485 properties, as before; - multi-channel parts (max3109, max14830): the chip node is only a container and is no longer a serial node; each channel is a "serial@N" subnode that carries the standard serial.yaml/rs485.yaml properties (and may host a serial slave device). max3109 has channels 0-1, max14830 has 0-3. This avoids a chip node that is simultaneously a serial node and the parent of serial nodes. The driver still reads chip-level RS-485 for single-channel and legacy device trees, so existing users are unaffected. Signed-off-by: Tapio Reijonen Reviewed-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260615-b4-max310x-rs485-dt-v3-2-7e79f064bdd7@vaisala.com Signed-off-by: Greg Kroah-Hartman --- .../bindings/serial/maxim,max310x.yaml | 92 ++++++++++++++++++- 1 file changed, 90 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/serial/maxim,max310x.yaml b/Documentation/devicetree/bindings/serial/maxim,max310x.yaml index 889eeaca64a0..e598dda4d13f 100644 --- a/Documentation/devicetree/bindings/serial/maxim,max310x.yaml +++ b/Documentation/devicetree/bindings/serial/maxim,max310x.yaml @@ -9,6 +9,13 @@ title: Maxim MAX310X Advanced Universal Asynchronous Receiver-Transmitter (UART) maintainers: - Hugo Villeneuve +description: + The MAX310X is a family of SPI/I2C UARTs with one (max3107, max3108), + two (max3109) or four (max14830) channels. Single-channel parts are + described as a serial node with RS-485 properties on the chip node; + multi-channel parts use one "serial@N" child node per channel, each + carrying its own serial/RS-485 properties. + properties: compatible: enum: @@ -49,8 +56,55 @@ required: allOf: - $ref: /schemas/spi/spi-peripheral-props.yaml# - - $ref: /schemas/serial/serial.yaml# - - $ref: /schemas/serial/rs485.yaml# + + - if: + properties: + compatible: + contains: + enum: + - maxim,max3107 + - maxim,max3108 + then: + allOf: + - $ref: /schemas/serial/serial.yaml# + - $ref: /schemas/serial/rs485.yaml# + + - if: + properties: + compatible: + contains: + enum: + - maxim,max3109 + - maxim,max14830 + then: + properties: + "#address-cells": + const: 1 + "#size-cells": + const: 0 + patternProperties: + "^serial@[0-3]$": + type: object + description: A single UART channel of the chip. + allOf: + - $ref: /schemas/serial/serial.yaml# + - $ref: /schemas/serial/rs485.yaml# + properties: + reg: + description: UART channel number on the chip. + maximum: 3 + required: + - reg + unevaluatedProperties: false + + - if: + properties: + compatible: + contains: + const: maxim,max3109 + then: + patternProperties: + "^serial@[23]$": false unevaluatedProperties: false @@ -70,5 +124,39 @@ examples: interrupts = <7 IRQ_TYPE_LEVEL_LOW>; gpio-controller; #gpio-cells = <2>; + rs485-rts-active-low; + linux,rs485-enabled-at-boot-time; + }; + }; + + - | + #include + spi { + #address-cells = <1>; + #size-cells = <0>; + + serial@0 { + compatible = "maxim,max14830"; + reg = <0>; + spi-max-frequency = <26000000>; + clocks = <&xtal4m>; + clock-names = "xtal"; + interrupt-parent = <&gpio3>; + interrupts = <7 IRQ_TYPE_LEVEL_LOW>; + gpio-controller; + #gpio-cells = <2>; + #address-cells = <1>; + #size-cells = <0>; + + serial@0 { + reg = <0>; + rs485-rts-active-low; + linux,rs485-enabled-at-boot-time; + }; + + serial@2 { + reg = <2>; + rs485-rts-active-low; + }; }; }; From bed094602ffb5c90fe761977c44653968809ebf3 Mon Sep 17 00:00:00 2001 From: Tapio Reijonen Date: Mon, 15 Jun 2026 10:27:37 +0000 Subject: [PATCH 13/51] serial: max310x: honour rs485 properties from per-channel DT subnode The MAX310x DT binding pulls in /schemas/serial/rs485.yaml via its allOf list, advertising the rs485-* properties defined there - none of which were honoured at runtime, because the driver never called uart_get_rs485_mode(). All channels share the parent SPI/I2C device, so uart_get_rs485_mode() called directly on each port would read the same chip-level fwnode for every call. Walk dev->of_node's children for the "serial@N" subnode with matching reg, and temporarily retarget the parent device's fwnode while uart_get_rs485_mode() runs, so each channel picks up its own subnode's properties. Probe is serialised, so the swap is safe. For single-channel variants (max3107, max3108), fall back to the chip's own fwnode when no subnode is present, so existing DTs that declare rs485 properties at the top level keep working. Signed-off-by: Tapio Reijonen Link: https://patch.msgid.link/20260615-b4-max310x-rs485-dt-v3-3-7e79f064bdd7@vaisala.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/max310x.c | 37 ++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index 3e26bdf8806b..022502986c5f 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -1452,6 +1452,9 @@ static int max310x_probe(struct device *dev, const struct max310x_devtype *devty #endif for (i = 0; i < devtype->nr; i++) { + struct fwnode_handle *saved_fwnode = dev_fwnode(dev); + struct device_node *port_np = NULL; + struct device_node *child; unsigned int line; line = find_first_zero_bit(max310x_lines, MAX310X_UART_NRMAX); @@ -1461,6 +1464,40 @@ static int max310x_probe(struct device *dev, const struct max310x_devtype *devty } s->p[i].port.line = line; + /* Locate the matching "serial@i" DT subnode, if any. */ + for_each_available_child_of_node(dev->of_node, child) { + u32 reg; + + if (!of_node_name_eq(child, "serial")) + continue; + if (of_property_read_u32(child, "reg", ®)) + continue; + if (reg == i) { + port_np = child; + break; + } + } + + /* + * Temporarily retarget dev's fwnode to the per-port subnode + * so uart_get_rs485_mode() picks up the per-port properties. + * For single-port variants, fall back to the chip's own + * fwnode so legacy DTs that declare rs485 properties at the + * top level keep working. + */ + if (port_np) { + device_set_node(dev, of_fwnode_handle(port_np)); + ret = uart_get_rs485_mode(&s->p[i].port); + device_set_node(dev, saved_fwnode); + of_node_put(port_np); + if (ret) + goto out_uart; + } else if (devtype->nr == 1) { + ret = uart_get_rs485_mode(&s->p[i].port); + if (ret) + goto out_uart; + } + /* Register port */ ret = uart_add_one_port(&max310x_uart, &s->p[i].port); if (ret) From 7a52545d37eb805eb1f3c7e03ff336b12c12f5af Mon Sep 17 00:00:00 2001 From: John de la Garza Date: Tue, 30 Jun 2026 20:27:17 -0700 Subject: [PATCH 14/51] tty: tty_jobctrl: use guard()s in tiocspgrp() Convert the manual spin_lock_irq()/rcu_read_lock() nesting in tiocspgrp() to scoped guard()s. This drops the out_unlock and out_unlock_ctrl labels and lets the error paths return directly. No functional change intended. Signed-off-by: John de la Garza Reviewed-by: Jiri Slaby Link: https://patch.msgid.link/20260701030139.3189030-1-john@jjdev.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_jobctrl.c | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/drivers/tty/tty_jobctrl.c b/drivers/tty/tty_jobctrl.c index ef8741c3e662..583e2412a47b 100644 --- a/drivers/tty/tty_jobctrl.c +++ b/drivers/tty/tty_jobctrl.c @@ -506,29 +506,23 @@ static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t if (pgrp_nr < 0) return -EINVAL; - spin_lock_irq(&real_tty->ctrl.lock); + guard(spinlock_irq)(&real_tty->ctrl.lock); if (!current->signal->tty || (current->signal->tty != real_tty) || - (real_tty->ctrl.session != task_session(current))) { - retval = -ENOTTY; - goto out_unlock_ctrl; - } - rcu_read_lock(); + (real_tty->ctrl.session != task_session(current))) + return -ENOTTY; + + guard(rcu)(); pgrp = find_vpid(pgrp_nr); - retval = -ESRCH; if (!pgrp) - goto out_unlock; - retval = -EPERM; + return -ESRCH; if (session_of_pgrp(pgrp) != task_session(current)) - goto out_unlock; - retval = 0; + return -EPERM; + put_pid(real_tty->ctrl.pgrp); real_tty->ctrl.pgrp = get_pid(pgrp); -out_unlock: - rcu_read_unlock(); -out_unlock_ctrl: - spin_unlock_irq(&real_tty->ctrl.lock); - return retval; + + return 0; } /** From 9505146e885b1a842118aa6410f737290c4a5a32 Mon Sep 17 00:00:00 2001 From: John de la Garza Date: Wed, 1 Jul 2026 16:12:15 -0700 Subject: [PATCH 15/51] tty: tty_jobctrl: use guard() in tiocgsid() guard()s express more clearly what the lock protects and let the function return immediately instead of jumping to an unlock label. Signed-off-by: John de la Garza Reviewed-by: Jiri Slaby Link: https://patch.msgid.link/20260701231215.4092457-1-john@jjdev.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_jobctrl.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/drivers/tty/tty_jobctrl.c b/drivers/tty/tty_jobctrl.c index 583e2412a47b..37929fb56174 100644 --- a/drivers/tty/tty_jobctrl.c +++ b/drivers/tty/tty_jobctrl.c @@ -536,7 +536,6 @@ static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t */ static int tiocgsid(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p) { - unsigned long flags; pid_t sid; /* @@ -546,17 +545,13 @@ static int tiocgsid(struct tty_struct *tty, struct tty_struct *real_tty, pid_t _ if (tty == real_tty && current->signal->tty != real_tty) return -ENOTTY; - spin_lock_irqsave(&real_tty->ctrl.lock, flags); - if (!real_tty->ctrl.session) - goto err; - sid = pid_vnr(real_tty->ctrl.session); - spin_unlock_irqrestore(&real_tty->ctrl.lock, flags); + scoped_guard(spinlock_irqsave, &real_tty->ctrl.lock) { + if (!real_tty->ctrl.session) + return -ENOTTY; + sid = pid_vnr(real_tty->ctrl.session); + } return put_user(sid, p); - -err: - spin_unlock_irqrestore(&real_tty->ctrl.lock, flags); - return -ENOTTY; } /* From 4d105880666ab7f7914a75716d3e95b0d8b879dc Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Fri, 29 May 2026 23:10:25 -0700 Subject: [PATCH 16/51] tty: serial: mpc52xx_uart: add bounds check for psc_num array index psc_num is derived from port->mapbase bits 11:8, giving a range of 0-15, but the psc_mclk_clk and psc_ipg_clk arrays are sized to MPC52xx_PSC_MAXNUM (12 when CONFIG_PPC_MPC512x is set). A malformed device tree with bits 11:8 >= 12 would cause out-of-bounds writes in mpc512x_psc_alloc_clock() and out-of-bounds reads/writes in mpc512x_psc_relse_clock() and mpc512x_psc_endis_clock(). The same unchecked index also appears in mpc512x_psc_handle_irq(). Add ARRAY_SIZE() bounds checks to all four functions before using psc_num as an array index. Assisted-by: Opencode:big-pickle Signed-off-by: Rosen Penev Link: https://patch.msgid.link/20260530061025.11625-1-rosenp@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/mpc52xx_uart.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/tty/serial/mpc52xx_uart.c b/drivers/tty/serial/mpc52xx_uart.c index 37eb701b0b46..b566206f42a2 100644 --- a/drivers/tty/serial/mpc52xx_uart.c +++ b/drivers/tty/serial/mpc52xx_uart.c @@ -645,6 +645,8 @@ static irqreturn_t mpc512x_psc_handle_irq(struct uart_port *port) /* Check if it is an interrupt for this port */ psc_num = (port->mapbase & 0xf00) >> 8; + if (psc_num >= ARRAY_SIZE(psc_mclk_clk)) + return IRQ_NONE; if (test_bit(psc_num, &fifoc_int) || test_bit(psc_num + 16, &fifoc_int)) return mpc5xxx_uart_process_int(port); @@ -663,6 +665,8 @@ static int mpc512x_psc_alloc_clock(struct uart_port *port) int err; psc_num = (port->mapbase & 0xf00) >> 8; + if (psc_num >= ARRAY_SIZE(psc_mclk_clk)) + return -EINVAL; clk = devm_clk_get(port->dev, "mclk"); if (IS_ERR(clk)) { @@ -711,6 +715,8 @@ static void mpc512x_psc_relse_clock(struct uart_port *port) struct clk *clk; psc_num = (port->mapbase & 0xf00) >> 8; + if (psc_num >= ARRAY_SIZE(psc_mclk_clk)) + return; clk = psc_mclk_clk[psc_num]; if (clk) { clk_disable_unprepare(clk); @@ -733,6 +739,8 @@ static int mpc512x_psc_endis_clock(struct uart_port *port, int enable) return 0; psc_num = (port->mapbase & 0xf00) >> 8; + if (psc_num >= ARRAY_SIZE(psc_mclk_clk)) + return -ENODEV; psc_clk = psc_mclk_clk[psc_num]; if (!psc_clk) { dev_err(port->dev, "Failed to get PSC clock entry!\n"); From d338ab1d90603f875c4f7ed223406535378173a5 Mon Sep 17 00:00:00 2001 From: Fushuai Wang Date: Fri, 22 May 2026 18:10:42 +0800 Subject: [PATCH 17/51] serial: 8250: Clear CON_PRINTBUFFER on port re-registration When two PnP devices map to the same physical port, the serial8250 driver removes and re-registers the console structure for the same port. During re-registration, the console structure still has CON_PRINTBUFFER set from the initial registration, which causes console_init_seq() to set console->seq to syslog_seq. This results in re-printing the entire system log buffer, which may lead to RCU stall on slow serial consoles. Clear CON_PRINTBUFFER when re-registering a port to prevent duplicate log printing. Fixes: 835d844d1a28 ("8250_pnp: do pnp probe before legacy probe") Suggested-by: Greg Kroah-Hartman Signed-off-by: Fushuai Wang Link: https://patch.msgid.link/20260522101042.21976-1-fushuai.wang@linux.dev Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_core.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index f49862d90eeb..c0e8a4efbdcc 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -720,8 +720,12 @@ int serial8250_register_8250_port(const struct uart_8250_port *up) /* Preserve specified console flow control. */ cons_flow = uart_cons_flow_enabled(&uart->port); - if (uart->port.dev) + if (uart->port.dev) { + if (uart_console(&uart->port)) + uart->port.cons->flags &= ~CON_PRINTBUFFER; + uart_remove_one_port(&serial8250_reg, &uart->port); + } uart->port.ctrl_id = up->port.ctrl_id; uart->port.port_id = up->port.port_id; From 831603b3e8aa3892de229fda846b2f859c518699 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Sat, 6 Jun 2026 19:11:17 -0700 Subject: [PATCH 18/51] serial: cpm_uart: replace irq_of_parse_and_map with platform_get_irq platform_get_irq is a newer API for this that does not require irq_dispose_mapping(). Assisted-by: opencode:big-pickle Signed-off-by: Rosen Penev Link: https://patch.msgid.link/20260607021117.6325-1-rosenp@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/cpm_uart.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/tty/serial/cpm_uart.c b/drivers/tty/serial/cpm_uart.c index b778a20ec9b1..39f54bb7b485 100644 --- a/drivers/tty/serial/cpm_uart.c +++ b/drivers/tty/serial/cpm_uart.c @@ -27,7 +27,6 @@ #include #include #include -#include #include #include #include @@ -1530,16 +1529,14 @@ static int cpm_uart_probe(struct platform_device *ofdev) /* initialize the device pointer for the port */ pinfo->port.dev = &ofdev->dev; - pinfo->port.irq = irq_of_parse_and_map(ofdev->dev.of_node, 0); - if (!pinfo->port.irq) - return -EINVAL; + pinfo->port.irq = platform_get_irq(ofdev, 0); + if (pinfo->port.irq < 0) + return pinfo->port.irq; ret = cpm_uart_init_port(ofdev->dev.of_node, pinfo); if (!ret) return uart_add_one_port(&cpm_reg, &pinfo->port); - irq_dispose_mapping(pinfo->port.irq); - return ret; } From 31e11af34e2c7cc61e25533ab877b1f9cc3eb528 Mon Sep 17 00:00:00 2001 From: Rahul Bukte Date: Wed, 10 Jun 2026 14:41:30 +0900 Subject: [PATCH 19/51] serial: 8250: force synchronous probe for the ISA and PNP drivers On x86_64 defconfig, booting with driver_async_probe=serial hangs in early init. The 8250 PNP driver is put onto the async probe pool. serial8250_register_8250_port() runs in a kworker concurrently with the ISA registration done from the serial8250_init() initcall resulting in a deadlock or NULL dereference. - Deadlock: serial_core_register_port() holds port_mutex across serial_core_add_one_port() uart_configure_port() autoconfig_irq() probe_irq_on() async_synchronize_full(), which waits for the async probe pool to drain. The async PNP worker reaches the "port already in use" check and tries to unregister it. serial8250_register_8250_port() uart_remove_one_port() serial_core_unregister_port() This blocks on port_mutex. The init thread waits for the worker and the worker waits for the init thread. - NULL deref: when the worker instead observes a slot whose port.dev is set but whose port_dev has not yet been populated, it hits the null pointer on the call to serial_core_get_ctrl_dev() in serial_core_unregister_port(). Signed-off-by: Rahul Bukte Link: https://patch.msgid.link/20260610054130.2825182-1-rahul.bukte@sony.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_platform.c | 1 + drivers/tty/serial/8250/8250_pnp.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/tty/serial/8250/8250_platform.c b/drivers/tty/serial/8250/8250_platform.c index ad3a7bc31d6f..af946d12e764 100644 --- a/drivers/tty/serial/8250/8250_platform.c +++ b/drivers/tty/serial/8250/8250_platform.c @@ -284,6 +284,7 @@ static struct platform_driver serial8250_isa_driver = { .driver = { .name = "serial8250", .acpi_match_table = acpi_platform_serial_table, + .probe_type = PROBE_FORCE_SYNCHRONOUS, }, }; diff --git a/drivers/tty/serial/8250/8250_pnp.c b/drivers/tty/serial/8250/8250_pnp.c index 7a837fdf9df1..3f41a9d6cb27 100644 --- a/drivers/tty/serial/8250/8250_pnp.c +++ b/drivers/tty/serial/8250/8250_pnp.c @@ -521,6 +521,7 @@ static struct pnp_driver serial_pnp_driver = { .remove = serial_pnp_remove, .driver = { .pm = pm_sleep_ptr(&serial_pnp_pm_ops), + .probe_type = PROBE_FORCE_SYNCHRONOUS, }, .id_table = pnp_dev_table, }; From a9b2c446e36f60e540ec29f756bbb5a1de37811a Mon Sep 17 00:00:00 2001 From: Praveen Talari Date: Mon, 15 Jun 2026 19:46:52 +0530 Subject: [PATCH 20/51] serial: qcom-geni: trace: Drop redundant len field from geni_serial_data The dynamic array stored in the ring buffer already carries its own length in the array metadata. There is no need to also store it as a separate scalar field in the entry struct. Drop __field(unsigned int, len) and the corresponding __entry->len assignment, and use __get_dynamic_array_len(data) in the TP_printk for both the len=%u format argument and the __print_hex() size argument. This saves 4 bytes per event on the ring buffer. Signed-off-by: Praveen Talari Suggested-by: Steven Rostedt Reviewed-by: Konrad Dybcio Link: https://patch.msgid.link/20260615-add-tracepoints-for-qcom-geni-serial-v5-1-2efa4c97e0e2@oss.qualcomm.com Signed-off-by: Greg Kroah-Hartman --- include/trace/events/qcom_geni_serial.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/include/trace/events/qcom_geni_serial.h b/include/trace/events/qcom_geni_serial.h index 417ec01f9fc8..e1aa551d525e 100644 --- a/include/trace/events/qcom_geni_serial.h +++ b/include/trace/events/qcom_geni_serial.h @@ -97,18 +97,17 @@ DECLARE_EVENT_CLASS(geni_serial_data, TP_ARGS(dev, buf, len), TP_STRUCT__entry(__string(name, dev_name(dev)) - __field(unsigned int, len) __dynamic_array(u8, data, len) ), TP_fast_assign(__assign_str(name); - __entry->len = len; memcpy(__get_dynamic_array(data), buf, len); ), TP_printk("%s: len=%u data=%s", - __get_str(name), __entry->len, - __print_hex(__get_dynamic_array(data), __entry->len)) + __get_str(name), __get_dynamic_array_len(data), + __print_hex(__get_dynamic_array(data), + __get_dynamic_array_len(data))) ); DEFINE_EVENT(geni_serial_data, geni_serial_tx_data, From 970ede67de0e3f6a1e707ec99ae49d80bf3f2966 Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Tue, 23 Jun 2026 22:05:39 +0800 Subject: [PATCH 21/51] tty: serial: pch_uart: add check for pci_get_slot() Add check for pci_get_slot() to prevent a potetial null pointer dereference in pch_request_dma(). Signed-off-by: Haoxiang Li Reviewed-by: Andy Shevchenko Link: https://patch.msgid.link/20260623140539.2272473-1-haoxiang_li2024@163.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/pch_uart.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c index 5f7f073f285e..db5c62b0322f 100644 --- a/drivers/tty/serial/pch_uart.c +++ b/drivers/tty/serial/pch_uart.c @@ -678,6 +678,11 @@ static void pch_request_dma(struct uart_port *port) /* Get DMA's dev information */ dma_dev = pci_get_slot(priv->pdev->bus, PCI_DEVFN(PCI_SLOT(priv->pdev->devfn), 0)); + if (!dma_dev) { + dev_err(priv->port.dev, "%s: failed to get DMA device\n", + __func__); + return; + } /* Set Tx DMA */ param = &priv->param_tx; From 383f139f191e4d12343f7153880495c46042dd37 Mon Sep 17 00:00:00 2001 From: David Laight Date: Mon, 8 Jun 2026 10:54:54 +0100 Subject: [PATCH 22/51] drivers/tty/serial/kgdboc: Use strscpy() to copy strings into arrays Replacing strcpy() with strscpy() ensures that overflow of the target buffer cannot happen. Signed-off-by: David Laight Reviewed-by: Daniel Thompson (RISCstar) Link: https://patch.msgid.link/20260608095523.2606-10-david.laight.linux@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/kgdboc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/kgdboc.c b/drivers/tty/serial/kgdboc.c index 5a955c80a853..09648d643897 100644 --- a/drivers/tty/serial/kgdboc.c +++ b/drivers/tty/serial/kgdboc.c @@ -363,7 +363,7 @@ static int param_set_kgdboc_var(const char *kmessage, mutex_lock(&config_mutex); - strcpy(config, kmessage); + strscpy(config, kmessage); /* Chop out \n char as a result of echo */ if (len && config[len - 1] == '\n') config[len - 1] = '\0'; From 7a68b818d56e5c48b90232d59148ef8e716082ae Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 17 Jun 2026 11:25:15 +0200 Subject: [PATCH 23/51] serdev: acpi: Free resource list at appropriate time We do unneeded "double free" (emptying an empty list) in one case. This is not a critical issue at all, the fix just makes code robust against any possible future changes in the flow. Signed-off-by: Andy Shevchenko Acked-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260617092515.2649521-1-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serdev/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serdev/core.c b/drivers/tty/serdev/core.c index e9d044a331b0..7500efcdfc21 100644 --- a/drivers/tty/serdev/core.c +++ b/drivers/tty/serdev/core.c @@ -651,11 +651,11 @@ static int acpi_serdev_do_lookup(struct acpi_device *adev, INIT_LIST_HEAD(&resource_list); ret = acpi_dev_get_resources(adev, &resource_list, acpi_serdev_parse_resource, lookup); - acpi_dev_free_resource_list(&resource_list); - if (ret < 0) return -EINVAL; + acpi_dev_free_resource_list(&resource_list); + return 0; } From 4f643bef0984b504adfb4d979ba5068e4d63485a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Mon, 29 Jun 2026 19:04:09 +0200 Subject: [PATCH 24/51] serial: 8250_pnp: Use named initializers for pnp_device_id array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While being less compact, using named initializers allows to more easily see which members of the structs are assigned which value without having to lookup the declaration of the struct. And it's also more robust against changes to the struct definition. The mentioned robustness is relevant for a planned change to struct pnp_device_id that replaces .driver_data by an anonymous union. This patch doesn't modify the compiled array, only their representation in source form benefits. The former was confirmed with x86 and arm64 builds. Also simplify the list terminator and use a consistent and more common indention. Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://patch.msgid.link/20260629170409.3412413-2-u.kleine-koenig@baylibre.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_pnp.c | 308 ++++++++++++++--------------- 1 file changed, 154 insertions(+), 154 deletions(-) diff --git a/drivers/tty/serial/8250/8250_pnp.c b/drivers/tty/serial/8250/8250_pnp.c index 3f41a9d6cb27..6bfdeff5fe22 100644 --- a/drivers/tty/serial/8250/8250_pnp.c +++ b/drivers/tty/serial/8250/8250_pnp.c @@ -28,351 +28,351 @@ static const struct pnp_device_id pnp_dev_table[] = { /* Archtek America Corp. */ /* Archtek SmartLink Modem 3334BT Plug & Play */ - { "AAC000F", 0 }, + { .id = "AAC000F", .driver_data = 0 }, /* Anchor Datacomm BV */ /* SXPro 144 External Data Fax Modem Plug & Play */ - { "ADC0001", 0 }, + { .id = "ADC0001", .driver_data = 0 }, /* SXPro 288 External Data Fax Modem Plug & Play */ - { "ADC0002", 0 }, + { .id = "ADC0002", .driver_data = 0 }, /* PROLiNK 1456VH ISA PnP K56flex Fax Modem */ - { "AEI0250", 0 }, + { .id = "AEI0250", .driver_data = 0 }, /* Actiontec ISA PNP 56K X2 Fax Modem */ - { "AEI1240", 0 }, + { .id = "AEI1240", .driver_data = 0 }, /* Rockwell 56K ACF II Fax+Data+Voice Modem */ - { "AKY1021", 0 /*SPCI_FL_NO_SHIRQ*/ }, + { .id ="AKY1021", .driver_data = 0 /*SPCI_FL_NO_SHIRQ*/ }, /* * ALi Fast Infrared Controller * Native driver (ali-ircc) is broken so at least * it can be used with irtty-sir. */ - { "ALI5123", 0 }, + { .id = "ALI5123", .driver_data = 0 }, /* AZT3005 PnP SOUND DEVICE */ - { "AZT4001", 0 }, + { .id = "AZT4001", .driver_data = 0 }, /* Best Data Products Inc. Smart One 336F PnP Modem */ - { "BDP3336", 0 }, + { .id = "BDP3336", .driver_data = 0 }, /* Boca Research */ /* Boca Complete Ofc Communicator 14.4 Data-FAX */ - { "BRI0A49", 0 }, + { .id = "BRI0A49", .driver_data = 0 }, /* Boca Research 33,600 ACF Modem */ - { "BRI1400", 0 }, + { .id = "BRI1400", .driver_data = 0 }, /* Boca 33.6 Kbps Internal FD34FSVD */ - { "BRI3400", 0 }, + { .id = "BRI3400", .driver_data = 0 }, /* Computer Peripherals Inc */ /* EuroViVa CommCenter-33.6 SP PnP */ - { "CPI4050", 0 }, + { .id = "CPI4050", .driver_data = 0 }, /* Creative Labs */ /* Creative Labs Phone Blaster 28.8 DSVD PnP Voice */ - { "CTL3001", 0 }, + { .id = "CTL3001", .driver_data = 0 }, /* Creative Labs Modem Blaster 28.8 DSVD PnP Voice */ - { "CTL3011", 0 }, + { .id = "CTL3011", .driver_data = 0 }, /* Davicom ISA 33.6K Modem */ - { "DAV0336", 0 }, + { .id = "DAV0336", .driver_data = 0 }, /* Creative */ /* Creative Modem Blaster Flash56 DI5601-1 */ - { "DMB1032", 0 }, + { .id = "DMB1032", .driver_data = 0 }, /* Creative Modem Blaster V.90 DI5660 */ - { "DMB2001", 0 }, + { .id = "DMB2001", .driver_data = 0 }, /* E-Tech */ /* E-Tech CyberBULLET PC56RVP */ - { "ETT0002", 0 }, + { .id = "ETT0002", .driver_data = 0 }, /* FUJITSU */ /* Fujitsu 33600 PnP-I2 R Plug & Play */ - { "FUJ0202", 0 }, + { .id = "FUJ0202", .driver_data = 0 }, /* Fujitsu FMV-FX431 Plug & Play */ - { "FUJ0205", 0 }, + { .id = "FUJ0205", .driver_data = 0 }, /* Fujitsu 33600 PnP-I4 R Plug & Play */ - { "FUJ0206", 0 }, + { .id = "FUJ0206", .driver_data = 0 }, /* Fujitsu Fax Voice 33600 PNP-I5 R Plug & Play */ - { "FUJ0209", 0 }, + { .id = "FUJ0209", .driver_data = 0 }, /* Archtek America Corp. */ /* Archtek SmartLink Modem 3334BT Plug & Play */ - { "GVC000F", 0 }, + { .id = "GVC000F", .driver_data = 0 }, /* Archtek SmartLink Modem 3334BRV 33.6K Data Fax Voice */ - { "GVC0303", 0 }, + { .id = "GVC0303", .driver_data = 0 }, /* Hayes */ /* Hayes Optima 288 V.34-V.FC + FAX + Voice Plug & Play */ - { "HAY0001", 0 }, + { .id = "HAY0001", .driver_data = 0 }, /* Hayes Optima 336 V.34 + FAX + Voice PnP */ - { "HAY000C", 0 }, + { .id = "HAY000C", .driver_data = 0 }, /* Hayes Optima 336B V.34 + FAX + Voice PnP */ - { "HAY000D", 0 }, + { .id = "HAY000D", .driver_data = 0 }, /* Hayes Accura 56K Ext Fax Modem PnP */ - { "HAY5670", 0 }, + { .id = "HAY5670", .driver_data = 0 }, /* Hayes Accura 56K Ext Fax Modem PnP */ - { "HAY5674", 0 }, + { .id = "HAY5674", .driver_data = 0 }, /* Hayes Accura 56K Fax Modem PnP */ - { "HAY5675", 0 }, + { .id = "HAY5675", .driver_data = 0 }, /* Hayes 288, V.34 + FAX */ - { "HAYF000", 0 }, + { .id = "HAYF000", .driver_data = 0 }, /* Hayes Optima 288 V.34 + FAX + Voice, Plug & Play */ - { "HAYF001", 0 }, + { .id = "HAYF001", .driver_data = 0 }, /* IBM */ /* IBM Thinkpad 701 Internal Modem Voice */ - { "IBM0033", 0 }, + { .id = "IBM0033", .driver_data = 0 }, /* Intermec */ /* Intermec CV60 touchscreen port */ - { "PNP4972", 0 }, + { .id = "PNP4972", .driver_data = 0 }, /* Intertex */ /* Intertex 28k8 33k6 Voice EXT PnP */ - { "IXDC801", 0 }, + { .id = "IXDC801", .driver_data = 0 }, /* Intertex 33k6 56k Voice EXT PnP */ - { "IXDC901", 0 }, + { .id = "IXDC901", .driver_data = 0 }, /* Intertex 28k8 33k6 Voice SP EXT PnP */ - { "IXDD801", 0 }, + { .id = "IXDD801", .driver_data = 0 }, /* Intertex 33k6 56k Voice SP EXT PnP */ - { "IXDD901", 0 }, + { .id = "IXDD901", .driver_data = 0 }, /* Intertex 28k8 33k6 Voice SP INT PnP */ - { "IXDF401", 0 }, + { .id = "IXDF401", .driver_data = 0 }, /* Intertex 28k8 33k6 Voice SP EXT PnP */ - { "IXDF801", 0 }, + { .id = "IXDF801", .driver_data = 0 }, /* Intertex 33k6 56k Voice SP EXT PnP */ - { "IXDF901", 0 }, + { .id = "IXDF901", .driver_data = 0 }, /* Kortex International */ /* KORTEX 28800 Externe PnP */ - { "KOR4522", 0 }, + { .id = "KOR4522", .driver_data = 0 }, /* KXPro 33.6 Vocal ASVD PnP */ - { "KORF661", 0 }, + { .id = "KORF661", .driver_data = 0 }, /* Lasat */ /* LASAT Internet 33600 PnP */ - { "LAS4040", 0 }, + { .id = "LAS4040", .driver_data = 0 }, /* Lasat Safire 560 PnP */ - { "LAS4540", 0 }, + { .id = "LAS4540", .driver_data = 0 }, /* Lasat Safire 336 PnP */ - { "LAS5440", 0 }, + { .id = "LAS5440", .driver_data = 0 }, /* Microcom, Inc. */ /* Microcom TravelPorte FAST V.34 Plug & Play */ - { "MNP0281", 0 }, + { .id = "MNP0281", .driver_data = 0 }, /* Microcom DeskPorte V.34 FAST or FAST+ Plug & Play */ - { "MNP0336", 0 }, + { .id = "MNP0336", .driver_data = 0 }, /* Microcom DeskPorte FAST EP 28.8 Plug & Play */ - { "MNP0339", 0 }, + { .id = "MNP0339", .driver_data = 0 }, /* Microcom DeskPorte 28.8P Plug & Play */ - { "MNP0342", 0 }, + { .id = "MNP0342", .driver_data = 0 }, /* Microcom DeskPorte FAST ES 28.8 Plug & Play */ - { "MNP0500", 0 }, + { .id = "MNP0500", .driver_data = 0 }, /* Microcom DeskPorte FAST ES 28.8 Plug & Play */ - { "MNP0501", 0 }, + { .id = "MNP0501", .driver_data = 0 }, /* Microcom DeskPorte 28.8S Internal Plug & Play */ - { "MNP0502", 0 }, + { .id = "MNP0502", .driver_data = 0 }, /* Motorola */ /* Motorola BitSURFR Plug & Play */ - { "MOT1105", 0 }, + { .id = "MOT1105", .driver_data = 0 }, /* Motorola TA210 Plug & Play */ - { "MOT1111", 0 }, + { .id = "MOT1111", .driver_data = 0 }, /* Motorola HMTA 200 (ISDN) Plug & Play */ - { "MOT1114", 0 }, + { .id = "MOT1114", .driver_data = 0 }, /* Motorola BitSURFR Plug & Play */ - { "MOT1115", 0 }, + { .id = "MOT1115", .driver_data = 0 }, /* Motorola Lifestyle 28.8 Internal */ - { "MOT1190", 0 }, + { .id = "MOT1190", .driver_data = 0 }, /* Motorola V.3400 Plug & Play */ - { "MOT1501", 0 }, + { .id = "MOT1501", .driver_data = 0 }, /* Motorola Lifestyle 28.8 V.34 Plug & Play */ - { "MOT1502", 0 }, + { .id = "MOT1502", .driver_data = 0 }, /* Motorola Power 28.8 V.34 Plug & Play */ - { "MOT1505", 0 }, + { .id = "MOT1505", .driver_data = 0 }, /* Motorola ModemSURFR External 28.8 Plug & Play */ - { "MOT1509", 0 }, + { .id = "MOT1509", .driver_data = 0 }, /* Motorola Premier 33.6 Desktop Plug & Play */ - { "MOT150A", 0 }, + { .id = "MOT150A", .driver_data = 0 }, /* Motorola VoiceSURFR 56K External PnP */ - { "MOT150F", 0 }, + { .id = "MOT150F", .driver_data = 0 }, /* Motorola ModemSURFR 56K External PnP */ - { "MOT1510", 0 }, + { .id = "MOT1510", .driver_data = 0 }, /* Motorola ModemSURFR 56K Internal PnP */ - { "MOT1550", 0 }, + { .id = "MOT1550", .driver_data = 0 }, /* Motorola ModemSURFR Internal 28.8 Plug & Play */ - { "MOT1560", 0 }, + { .id = "MOT1560", .driver_data = 0 }, /* Motorola Premier 33.6 Internal Plug & Play */ - { "MOT1580", 0 }, + { .id = "MOT1580", .driver_data = 0 }, /* Motorola OnlineSURFR 28.8 Internal Plug & Play */ - { "MOT15B0", 0 }, + { .id = "MOT15B0", .driver_data = 0 }, /* Motorola VoiceSURFR 56K Internal PnP */ - { "MOT15F0", 0 }, + { .id = "MOT15F0", .driver_data = 0 }, /* Com 1 */ /* Deskline K56 Phone System PnP */ - { "MVX00A1", 0 }, + { .id = "MVX00A1", .driver_data = 0 }, /* PC Rider K56 Phone System PnP */ - { "MVX00F2", 0 }, + { .id = "MVX00F2", .driver_data = 0 }, /* NEC 98NOTE SPEAKER PHONE FAX MODEM(33600bps) */ - { "nEC8241", 0 }, + { .id = "nEC8241", .driver_data = 0 }, /* Pace 56 Voice Internal Plug & Play Modem */ - { "PMC2430", 0 }, + { .id = "PMC2430", .driver_data = 0 }, /* Generic */ /* Generic standard PC COM port */ - { "PNP0500", 0 }, + { .id = "PNP0500", .driver_data = 0 }, /* Generic 16550A-compatible COM port */ - { "PNP0501", 0 }, + { .id = "PNP0501", .driver_data = 0 }, /* Compaq 14400 Modem */ - { "PNPC000", 0 }, + { .id = "PNPC000", .driver_data = 0 }, /* Compaq 2400/9600 Modem */ - { "PNPC001", 0 }, + { .id = "PNPC001", .driver_data = 0 }, /* Dial-Up Networking Serial Cable between 2 PCs */ - { "PNPC031", 0 }, + { .id = "PNPC031", .driver_data = 0 }, /* Dial-Up Networking Parallel Cable between 2 PCs */ - { "PNPC032", 0 }, + { .id = "PNPC032", .driver_data = 0 }, /* Standard 9600 bps Modem */ - { "PNPC100", 0 }, + { .id = "PNPC100", .driver_data = 0 }, /* Standard 14400 bps Modem */ - { "PNPC101", 0 }, + { .id = "PNPC101", .driver_data = 0 }, /* Standard 28800 bps Modem*/ - { "PNPC102", 0 }, + { .id = "PNPC102", .driver_data = 0 }, /* Standard Modem*/ - { "PNPC103", 0 }, + { .id = "PNPC103", .driver_data = 0 }, /* Standard 9600 bps Modem*/ - { "PNPC104", 0 }, + { .id = "PNPC104", .driver_data = 0 }, /* Standard 14400 bps Modem*/ - { "PNPC105", 0 }, + { .id = "PNPC105", .driver_data = 0 }, /* Standard 28800 bps Modem*/ - { "PNPC106", 0 }, + { .id = "PNPC106", .driver_data = 0 }, /* Standard Modem */ - { "PNPC107", 0 }, + { .id = "PNPC107", .driver_data = 0 }, /* Standard 9600 bps Modem */ - { "PNPC108", 0 }, + { .id = "PNPC108", .driver_data = 0 }, /* Standard 14400 bps Modem */ - { "PNPC109", 0 }, + { .id = "PNPC109", .driver_data = 0 }, /* Standard 28800 bps Modem */ - { "PNPC10A", 0 }, + { .id = "PNPC10A", .driver_data = 0 }, /* Standard Modem */ - { "PNPC10B", 0 }, + { .id = "PNPC10B", .driver_data = 0 }, /* Standard 9600 bps Modem */ - { "PNPC10C", 0 }, + { .id = "PNPC10C", .driver_data = 0 }, /* Standard 14400 bps Modem */ - { "PNPC10D", 0 }, + { .id = "PNPC10D", .driver_data = 0 }, /* Standard 28800 bps Modem */ - { "PNPC10E", 0 }, + { .id = "PNPC10E", .driver_data = 0 }, /* Standard Modem */ - { "PNPC10F", 0 }, + { .id = "PNPC10F", .driver_data = 0 }, /* Standard PCMCIA Card Modem */ - { "PNP2000", 0 }, + { .id = "PNP2000", .driver_data = 0 }, /* Rockwell */ /* Modular Technology */ /* Rockwell 33.6 DPF Internal PnP */ /* Modular Technology 33.6 Internal PnP */ - { "ROK0030", 0 }, + { .id = "ROK0030", .driver_data = 0 }, /* Kortex International */ /* KORTEX 14400 Externe PnP */ - { "ROK0100", 0 }, + { .id = "ROK0100", .driver_data = 0 }, /* Rockwell 28.8 */ - { "ROK4120", 0 }, + { .id = "ROK4120", .driver_data = 0 }, /* Viking Components, Inc */ /* Viking 28.8 INTERNAL Fax+Data+Voice PnP */ - { "ROK4920", 0 }, + { .id = "ROK4920", .driver_data = 0 }, /* Rockwell */ /* British Telecom */ /* Modular Technology */ /* Rockwell 33.6 DPF External PnP */ /* BT Prologue 33.6 External PnP */ /* Modular Technology 33.6 External PnP */ - { "RSS00A0", 0 }, + { .id = "RSS00A0", .driver_data = 0 }, /* Viking 56K FAX INT */ - { "RSS0262", 0 }, + { .id = "RSS0262", .driver_data = 0 }, /* K56 par,VV,Voice,Speakphone,AudioSpan,PnP */ - { "RSS0250", 0 }, + { .id = "RSS0250", .driver_data = 0 }, /* SupraExpress 28.8 Data/Fax PnP modem */ - { "SUP1310", 0 }, + { .id = "SUP1310", .driver_data = 0 }, /* SupraExpress 336i PnP Voice Modem */ - { "SUP1381", 0 }, + { .id = "SUP1381", .driver_data = 0 }, /* SupraExpress 33.6 Data/Fax PnP modem */ - { "SUP1421", 0 }, + { .id = "SUP1421", .driver_data = 0 }, /* SupraExpress 33.6 Data/Fax PnP modem */ - { "SUP1590", 0 }, + { .id = "SUP1590", .driver_data = 0 }, /* SupraExpress 336i Sp ASVD */ - { "SUP1620", 0 }, + { .id = "SUP1620", .driver_data = 0 }, /* SupraExpress 33.6 Data/Fax PnP modem */ - { "SUP1760", 0 }, + { .id = "SUP1760", .driver_data = 0 }, /* SupraExpress 56i Sp Intl */ - { "SUP2171", 0 }, + { .id = "SUP2171", .driver_data = 0 }, /* Phoebe Micro */ /* Phoebe Micro 33.6 Data Fax 1433VQH Plug & Play */ - { "TEX0011", 0 }, + { .id = "TEX0011", .driver_data = 0 }, /* Archtek America Corp. */ /* Archtek SmartLink Modem 3334BT Plug & Play */ - { "UAC000F", 0 }, + { .id = "UAC000F", .driver_data = 0 }, /* 3Com Corp. */ /* Gateway Telepath IIvi 33.6 */ - { "USR0000", 0 }, + { .id = "USR0000", .driver_data = 0 }, /* U.S. Robotics Sporster 33.6K Fax INT PnP */ - { "USR0002", 0 }, + { .id = "USR0002", .driver_data = 0 }, /* Sportster Vi 14.4 PnP FAX Voicemail */ - { "USR0004", 0 }, + { .id = "USR0004", .driver_data = 0 }, /* U.S. Robotics 33.6K Voice INT PnP */ - { "USR0006", 0 }, + { .id = "USR0006", .driver_data = 0 }, /* U.S. Robotics 33.6K Voice EXT PnP */ - { "USR0007", 0 }, + { .id = "USR0007", .driver_data = 0 }, /* U.S. Robotics Courier V.Everything INT PnP */ - { "USR0009", 0 }, + { .id = "USR0009", .driver_data = 0 }, /* U.S. Robotics 33.6K Voice INT PnP */ - { "USR2002", 0 }, + { .id = "USR2002", .driver_data = 0 }, /* U.S. Robotics 56K Voice INT PnP */ - { "USR2070", 0 }, + { .id = "USR2070", .driver_data = 0 }, /* U.S. Robotics 56K Voice EXT PnP */ - { "USR2080", 0 }, + { .id = "USR2080", .driver_data = 0 }, /* U.S. Robotics 56K FAX INT */ - { "USR3031", 0 }, + { .id = "USR3031", .driver_data = 0 }, /* U.S. Robotics 56K FAX INT */ - { "USR3050", 0 }, + { .id = "USR3050", .driver_data = 0 }, /* U.S. Robotics 56K Voice INT PnP */ - { "USR3070", 0 }, + { .id = "USR3070", .driver_data = 0 }, /* U.S. Robotics 56K Voice EXT PnP */ - { "USR3080", 0 }, + { .id = "USR3080", .driver_data = 0 }, /* U.S. Robotics 56K Voice INT PnP */ - { "USR3090", 0 }, + { .id = "USR3090", .driver_data = 0 }, /* U.S. Robotics 56K Message */ - { "USR9100", 0 }, + { .id = "USR9100", .driver_data = 0 }, /* U.S. Robotics 56K FAX EXT PnP*/ - { "USR9160", 0 }, + { .id = "USR9160", .driver_data = 0 }, /* U.S. Robotics 56K FAX INT PnP*/ - { "USR9170", 0 }, + { .id = "USR9170", .driver_data = 0 }, /* U.S. Robotics 56K Voice EXT PnP*/ - { "USR9180", 0 }, + { .id = "USR9180", .driver_data = 0 }, /* U.S. Robotics 56K Voice INT PnP*/ - { "USR9190", 0 }, + { .id = "USR9190", .driver_data = 0 }, /* Wacom tablets */ - { "WACFXXX", 0 }, + { .id = "WACFXXX", .driver_data = 0 }, /* Compaq touchscreen */ - { "FPI2002", 0 }, + { .id = "FPI2002", .driver_data = 0 }, /* Fujitsu Stylistic touchscreens */ - { "FUJ02B2", 0 }, - { "FUJ02B3", 0 }, + { .id = "FUJ02B2", .driver_data = 0 }, + { .id = "FUJ02B3", .driver_data = 0 }, /* Fujitsu Stylistic LT touchscreens */ - { "FUJ02B4", 0 }, + { .id = "FUJ02B4", .driver_data = 0 }, /* Passive Fujitsu Stylistic touchscreens */ - { "FUJ02B6", 0 }, - { "FUJ02B7", 0 }, - { "FUJ02B8", 0 }, - { "FUJ02B9", 0 }, - { "FUJ02BC", 0 }, + { .id = "FUJ02B6", .driver_data = 0 }, + { .id = "FUJ02B7", .driver_data = 0 }, + { .id = "FUJ02B8", .driver_data = 0 }, + { .id = "FUJ02B9", .driver_data = 0 }, + { .id = "FUJ02BC", .driver_data = 0 }, /* Fujitsu Wacom Tablet PC device */ - { "FUJ02E5", 0 }, + { .id = "FUJ02E5", .driver_data = 0 }, /* Fujitsu P-series tablet PC device */ - { "FUJ02E6", 0 }, + { .id = "FUJ02E6", .driver_data = 0 }, /* Fujitsu Wacom 2FGT Tablet PC device */ - { "FUJ02E7", 0 }, + { .id = "FUJ02E7", .driver_data = 0 }, /* Fujitsu Wacom 1FGT Tablet PC device */ - { "FUJ02E9", 0 }, + { .id = "FUJ02E9", .driver_data = 0 }, /* * LG C1 EXPRESS DUAL (C1-PB11A3) touch screen (actually a FUJ02E6 * in disguise). */ - { "LTS0001", 0 }, + { .id = "LTS0001", .driver_data = 0 }, /* Rockwell's (PORALiNK) 33600 INT PNP */ - { "WCI0003", 0 }, + { .id = "WCI0003", .driver_data = 0 }, /* Unknown PnP modems */ - { "PNPCXXX", UNKNOWN_DEV }, + { .id = "PNPCXXX", .driver_data = UNKNOWN_DEV }, /* More unknown PnP modems */ - { "PNPDXXX", UNKNOWN_DEV }, + { .id = "PNPDXXX", .driver_data = UNKNOWN_DEV }, /* * Winbond CIR port, should not be probed. We should keep track of * it to prevent the legacy serial driver from probing it. */ - { "WEC1022", CIR_PORT }, + { .id = "WEC1022", .driver_data = CIR_PORT }, /* * SMSC IrCC SIR/FIR port, should not be probed by serial driver as * well so its own driver can bind to it. */ - { "SMCF010", CIR_PORT }, - { "", 0 } + { .id = "SMCF010", .driver_data = CIR_PORT }, + { } }; MODULE_DEVICE_TABLE(pnp, pnp_dev_table); From 8dfea56f350b3dc826f35711802ad6ae8fae0748 Mon Sep 17 00:00:00 2001 From: Yuho Choi Date: Tue, 30 Jun 2026 17:40:43 -0400 Subject: [PATCH 25/51] serial: ma35d1: Fix OF node reference leaks in console init ma35d1serial_console_init_port() stores matching UART device nodes in ma35d1serial_uart_nodes[] with an extra of_node_get() so that console setup can later read the "reg" property. However, the stored references are never released after console setup has finished using them. Drop the stored node reference after ma35d1serial_console_setup() reads the "reg" property, and clear the array slot to avoid leaving a stale pointer behind. Also release the iterator reference before breaking out of for_each_matching_node(), since the normal iterator advance will not run in that path. Fixes: 930cbf92db01 ("tty: serial: Add Nuvoton ma35d1 serial driver support") Signed-off-by: Yuho Choi Link: https://patch.msgid.link/20260630214043.1887351-1-dbgh9129@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/ma35d1_serial.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/ma35d1_serial.c b/drivers/tty/serial/ma35d1_serial.c index 285b0fe41a86..920fe7ff5083 100644 --- a/drivers/tty/serial/ma35d1_serial.c +++ b/drivers/tty/serial/ma35d1_serial.c @@ -608,8 +608,14 @@ static int __init ma35d1serial_console_setup(struct console *co, char *options) if (!np || !p) return -ENODEV; - if (of_property_read_u32_array(np, "reg", val32, ARRAY_SIZE(val32)) != 0) + if (of_property_read_u32_array(np, "reg", val32, ARRAY_SIZE(val32)) != 0) { + of_node_put(np); + ma35d1serial_uart_nodes[co->index] = NULL; return -EINVAL; + } + + of_node_put(np); + ma35d1serial_uart_nodes[co->index] = NULL; p->port.iobase = val32[1]; p->port.membase = ioremap(p->port.iobase, MA35_UART_REG_SIZE); @@ -648,8 +654,10 @@ static void ma35d1serial_console_init_port(void) of_node_get(np); ma35d1serial_uart_nodes[i] = np; i++; - if (i == MA35_UART_NR) + if (i == MA35_UART_NR) { + of_node_put(np); break; + } } } } From bafe277d6c5a8bc4b6b9ecafc35cc357c42e813d Mon Sep 17 00:00:00 2001 From: Praveen Talari Date: Wed, 1 Jul 2026 09:53:10 +0530 Subject: [PATCH 26/51] serial: qcom-geni: Pre-map RX DMA buffer at probe to avoid sleep-in-atomic geni_se_rx_dma_prep() calls dma_map_single() which can trigger IOMMU page table allocations under GFP_KERNEL. This is unsafe when called from qcom_geni_serial_start_rx_dma(), which runs in atomic context producing a "sleeping function called from invalid context" splat: __might_resched+0x15c/0x17c __alloc_pages_noprof+0xe4/0x4c8 qcom_io_pgtable_alloc_page+0x100/0x250 __arm_lpae_map+0x2d0/0x870 geni_se_rx_dma_prep+0xd8/0x158 qcom_geni_serial_start_rx_dma+0x84/0x16c qcom_geni_serial_startup+0x70/0x104 Fix this by mapping the RX DMA buffer once during probe, where sleeping is allowed, and keeping it mapped for the lifetime of the device. Replace the geni_se_rx_dma_prep() / geni_se_rx_dma_unprep() calls in the runtime paths with dma_sync_single_for_device() before initiating a transfer and dma_sync_single_for_cpu() on completion, using the persistent mapping. The buffer is unmapped in probe's error path and in remove(). Signed-off-by: Praveen Talari Link: https://patch.msgid.link/20260701-fix-sleep-in-atomic-context-during-rx-dma-setup-v1-1-95c208380c65@oss.qualcomm.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/qcom_geni_serial.c | 55 ++++++++++++++++----------- 1 file changed, 33 insertions(+), 22 deletions(-) diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c index 7ead87b4eb65..3e460b0358eb 100644 --- a/drivers/tty/serial/qcom_geni_serial.c +++ b/drivers/tty/serial/qcom_geni_serial.c @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -863,37 +864,31 @@ static void qcom_geni_serial_stop_rx_dma(struct uart_port *uport) uport->membase + SE_DMA_RX_IRQ_CLR); } - if (port->rx_dma_addr) { - geni_se_rx_dma_unprep(&port->se, port->rx_dma_addr, - DMA_RX_BUF_SIZE); - port->rx_dma_addr = 0; - } } static void qcom_geni_serial_start_rx_dma(struct uart_port *uport) { struct qcom_geni_serial_port *port = to_dev_port(uport); - int ret; if (qcom_geni_serial_secondary_active(uport)) qcom_geni_serial_stop_rx_dma(uport); geni_se_setup_s_cmd(&port->se, UART_START_READ, UART_PARAM_RFR_OPEN); - ret = geni_se_rx_dma_prep(&port->se, port->rx_buf, - DMA_RX_BUF_SIZE, - &port->rx_dma_addr); - if (ret) { - dev_err(uport->dev, "unable to start RX SE DMA: %d\n", ret); - qcom_geni_serial_stop_rx_dma(uport); + if (!port->rx_dma_addr) { + dev_err(uport->dev, "RX DMA buffer not mapped\n"); + return; } + + dma_sync_single_for_device(uport->dev->parent, port->rx_dma_addr, + DMA_RX_BUF_SIZE, DMA_FROM_DEVICE); + geni_se_rx_init_dma(&port->se, port->rx_dma_addr, DMA_RX_BUF_SIZE); } static void qcom_geni_serial_handle_rx_dma(struct uart_port *uport, bool drop) { struct qcom_geni_serial_port *port = to_dev_port(uport); u32 rx_in; - int ret; if (!qcom_geni_serial_secondary_active(uport)) return; @@ -901,8 +896,8 @@ static void qcom_geni_serial_handle_rx_dma(struct uart_port *uport, bool drop) if (!port->rx_dma_addr) return; - geni_se_rx_dma_unprep(&port->se, port->rx_dma_addr, DMA_RX_BUF_SIZE); - port->rx_dma_addr = 0; + dma_sync_single_for_cpu(uport->dev->parent, port->rx_dma_addr, + DMA_RX_BUF_SIZE, DMA_FROM_DEVICE); rx_in = readl(uport->membase + SE_DMA_RX_LEN_IN); if (!rx_in) @@ -910,13 +905,9 @@ static void qcom_geni_serial_handle_rx_dma(struct uart_port *uport, bool drop) else if (!drop) handle_rx_uart(uport, rx_in); - ret = geni_se_rx_dma_prep(&port->se, port->rx_buf, - DMA_RX_BUF_SIZE, - &port->rx_dma_addr); - if (ret) { - dev_err(uport->dev, "unable to start RX SE DMA: %d\n", ret); - qcom_geni_serial_stop_rx_dma(uport); - } + dma_sync_single_for_device(uport->dev->parent, port->rx_dma_addr, + DMA_RX_BUF_SIZE, DMA_FROM_DEVICE); + geni_se_rx_init_dma(&port->se, port->rx_dma_addr, DMA_RX_BUF_SIZE); } static void qcom_geni_serial_start_rx(struct uart_port *uport) @@ -1864,6 +1855,14 @@ static int qcom_geni_serial_probe(struct platform_device *pdev) ret = -ENOMEM; goto error; } + + port->rx_dma_addr = dma_map_single(pdev->dev.parent, port->rx_buf, + DMA_RX_BUF_SIZE, DMA_FROM_DEVICE); + if (dma_mapping_error(pdev->dev.parent, port->rx_dma_addr)) { + ret = -EIO; + dev_err(&pdev->dev, "Failed to map RX DMA buffer: %d\n", ret); + goto error; + } } port->name = devm_kasprintf(uport->dev, GFP_KERNEL, @@ -1928,6 +1927,11 @@ static int qcom_geni_serial_probe(struct platform_device *pdev) return 0; error: + if (port->rx_dma_addr) { + dma_unmap_single(pdev->dev.parent, port->rx_dma_addr, + DMA_RX_BUF_SIZE, DMA_FROM_DEVICE); + port->rx_dma_addr = 0; + } dev_pm_domain_detach_list(port->pd_list); return ret; } @@ -1942,6 +1946,13 @@ static void qcom_geni_serial_remove(struct platform_device *pdev) device_init_wakeup(&pdev->dev, false); ida_free(&port_ida, uport->line); uart_remove_one_port(drv, &port->uport); + + if (port->rx_dma_addr) { + dma_unmap_single(pdev->dev.parent, port->rx_dma_addr, + DMA_RX_BUF_SIZE, DMA_FROM_DEVICE); + port->rx_dma_addr = 0; + } + dev_pm_domain_detach_list(port->pd_list); } From d0cd9c8d0fd59bc7d140f3d60cf02e1d80376dab Mon Sep 17 00:00:00 2001 From: Praveen Talari Date: Thu, 2 Jul 2026 17:11:50 +0530 Subject: [PATCH 27/51] serial: qcom-geni: add force suspend/resume to system sleep callbacks During system sleep the hardware resources (clocks, interconnect) are not gated because the runtime-suspend callback is never invoked from the system sleep path. This prevents the platform from reaching its lowest idle state. The system sleep callbacks qcom_geni_serial_suspend() and qcom_geni_serial_resume() rely solely on uart_suspend_port() / uart_resume_port() to manage power. uart_suspend_port() drives the UART PM state machine to UART_PM_STATE_OFF, which in turn calls pm_runtime_put_sync() and eventually the runtime-suspend callback. However, if the runtime-PM usage count is still elevated at the time of system sleep (e.g. the port is held active by an open file descriptor), the runtime-suspend callback is never invoked and the hardware resources (clocks, interconnect) remain enabled across suspend, preventing the platform from reaching its lowest idle state. Fix this by calling pm_runtime_force_suspend() at the end of qcom_geni_serial_suspend() so that the runtime-suspend callback is always executed regardless of the usage count, and by calling pm_runtime_force_resume() at the start of qcom_geni_serial_resume() to restore those resources before uart_resume_port() re-opens the port. Signed-off-by: Praveen Talari Link: https://patch.msgid.link/20260702-add_force_suspend_resume_to_system_sleep_callbacks-v2-1-b79e254a7015@oss.qualcomm.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/qcom_geni_serial.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c index 3e460b0358eb..949e16ead7b5 100644 --- a/drivers/tty/serial/qcom_geni_serial.c +++ b/drivers/tty/serial/qcom_geni_serial.c @@ -1985,6 +1985,7 @@ static int qcom_geni_serial_suspend(struct device *dev) struct qcom_geni_serial_port *port = dev_get_drvdata(dev); struct uart_port *uport = &port->uport; struct qcom_geni_private_data *private_data = uport->private_data; + int ret; /* * This is done so we can hit the lowest possible state in suspend @@ -1994,7 +1995,19 @@ static int qcom_geni_serial_suspend(struct device *dev) geni_icc_set_tag(&port->se, QCOM_ICC_TAG_ACTIVE_ONLY); geni_icc_set_bw(&port->se); } - return uart_suspend_port(private_data->drv, uport); + + ret = uart_suspend_port(private_data->drv, uport); + if (ret) + return ret; + + /* + * When no_console_suspend is set the console must remain active + * across system sleep, so skip the force suspend path. + */ + if (!console_suspend_enabled && uart_console(uport)) + return 0; + + return pm_runtime_force_suspend(dev); } static int qcom_geni_serial_resume(struct device *dev) @@ -2004,6 +2017,10 @@ static int qcom_geni_serial_resume(struct device *dev) struct uart_port *uport = &port->uport; struct qcom_geni_private_data *private_data = uport->private_data; + ret = pm_runtime_force_resume(dev); + if (ret) + return ret; + ret = uart_resume_port(private_data->drv, uport); if (uart_console(uport)) { geni_icc_set_tag(&port->se, QCOM_ICC_TAG_ALWAYS); From acba38e675dd66f52925cb9edf9ed8db16335875 Mon Sep 17 00:00:00 2001 From: Aniket Randive Date: Mon, 6 Jul 2026 15:07:49 +0530 Subject: [PATCH 28/51] serial: qcom_geni: Disable closing_wait for console to prevent shutdown timeout During system power-off, systemd closes the console UART and blocks in tty_wait_until_sent() waiting for pending TX completion before uart_shutdown() can cancel pending TX operations. With the default closing_wait of 30s, this causes a watchdog reset when systemd is responsible for watchdog feeding. Set closing_wait to ASYNC_CLOSING_WAIT_NONE in startup() for console ports to bypass tty_wait_until_sent() on close, allowing uart_shutdown() to cancel TX commands cleanly. This change doesn't impact regular ports functionality. Signed-off-by: Aniket Randive Link: https://patch.msgid.link/20260706093749.836121-1-aniket.randive@oss.qualcomm.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/qcom_geni_serial.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c index 949e16ead7b5..ccbf6fb6b478 100644 --- a/drivers/tty/serial/qcom_geni_serial.c +++ b/drivers/tty/serial/qcom_geni_serial.c @@ -1236,6 +1236,7 @@ static int qcom_geni_serial_startup(struct uart_port *uport) { int ret; struct qcom_geni_serial_port *port = to_dev_port(uport); + struct tty_port *tport = &uport->state->port; if (!port->setup) { ret = qcom_geni_serial_port_setup(uport); @@ -1243,6 +1244,13 @@ static int qcom_geni_serial_startup(struct uart_port *uport) return ret; } + /* + * Skip the close-time transmit drain for console ports so that + * shutdown can proceed without waiting for pending TX completion. + */ + if (uart_console(uport)) + tport->closing_wait = ASYNC_CLOSING_WAIT_NONE; + uart_port_lock_irq(uport); qcom_geni_serial_start_rx(uport); uart_port_unlock_irq(uport); From 7ea38c49e7178960926657863299face6dc0e1b0 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Wed, 8 Jul 2026 21:17:26 +0800 Subject: [PATCH 29/51] serial: qcom-geni: do not advance stale DMA completions The qcom GENI serial DMA TX completion path advances the transmit fifo by the number of bytes recorded in port->tx_remaining. If uart_flush_buffer() runs after the hardware has completed a DMA transfer but before the DMA completion interrupt has been handled, the serial core resets the transmit fifo while port->tx_remaining still describes the old DMA transfer. A previous fix avoided advancing an empty fifo by checking that the fifo length is at least tx_remaining. That still does not distinguish the old DMA payload from new bytes written after the flush. If userspace writes new data before the stale DMA completion interrupt is handled, the fifo can again contain at least tx_remaining bytes and the stale completion can advance and discard those new bytes. Mark an in-flight DMA transfer stale when the transmit fifo is flushed. The later completion still unprepares the original DMA mapping using the saved length, but it no longer advances the transmit fifo. Fixes: 2aaa43c70778 ("tty: serial: qcom-geni-serial: add support for serial engine DMA") Signed-off-by: Guangshuo Li Link: https://patch.msgid.link/20260708131726.768692-1-lgs201920130244@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/qcom_geni_serial.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c index ccbf6fb6b478..e037af56a159 100644 --- a/drivers/tty/serial/qcom_geni_serial.c +++ b/drivers/tty/serial/qcom_geni_serial.c @@ -144,6 +144,7 @@ struct qcom_geni_serial_port { unsigned int tx_remaining; unsigned int tx_queued; + bool tx_dma_stale; int wakeup_irq; bool rx_tx_swap; bool cts_rts_swap; @@ -698,6 +699,7 @@ static void qcom_geni_serial_start_tx_dma(struct uart_port *uport) } port->tx_remaining = xmit_size; + port->tx_dma_stale = false; } static void qcom_geni_serial_start_tx_fifo(struct uart_port *uport) @@ -1020,6 +1022,7 @@ static void qcom_geni_serial_handle_tx_dma(struct uart_port *uport) struct qcom_geni_serial_port *port = to_dev_port(uport); struct tty_port *tport = &uport->state->port; unsigned int fifo_len = kfifo_len(&tport->xmit_fifo); + bool tx_dma_stale = port->tx_dma_stale; /* * Only advance the kfifo if it still contains the bytes that were @@ -1030,12 +1033,13 @@ static void qcom_geni_serial_handle_tx_dma(struct uart_port *uport) * kfifo->in, making kfifo_len() wrap to UART_XMIT_SIZE - tx_remaining * and triggering a spurious large DMA transfer of stale data. */ - if (fifo_len >= port->tx_remaining) + if (!tx_dma_stale && fifo_len >= port->tx_remaining) uart_xmit_advance(uport, port->tx_remaining); geni_se_tx_dma_unprep(&port->se, port->tx_dma_addr, port->tx_remaining); port->tx_dma_addr = 0; port->tx_remaining = 0; + port->tx_dma_stale = false; if (!kfifo_is_empty(&tport->xmit_fifo)) qcom_geni_serial_start_tx_dma(uport); @@ -1173,6 +1177,10 @@ static void qcom_geni_serial_shutdown(struct uart_port *uport) static void qcom_geni_serial_flush_buffer(struct uart_port *uport) { + struct qcom_geni_serial_port *port = to_dev_port(uport); + + if (port->tx_dma_addr) + port->tx_dma_stale = true; qcom_geni_serial_cancel_tx_cmd(uport); } From a76c010ec369ff3e3b3b0bf9224840caa8843a64 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 9 Jul 2026 21:21:08 +0200 Subject: [PATCH 30/51] serial: 8250: handle ixp4xx register endianness correctly Unlike modern SoCs that just work in both big-endian and little-endian mode using the readl()/writel() or readb()/writeb() accessors, the internal registers on ixp4xx behave like native-endian 32-bit registers in both modes, which requires adjusting the register address when using 8-bit access. The existing dts files are written for big-endian kernels and 8-bit access, which does not work with little-endian kernels. Add a quirk that makes the 8250 OF driver: 1. Mask off any hardcoded offset. 2. Add the += 3 offset if and only if we are running on big endian. This should work in all combinations of big-endian and little-endian kernels with either variant of the DTS file. Signed-off-by: Arnd Bergmann [linusw@kernel.org: Modified to just play with the offset] Signed-off-by: Linus Walleij Link: https://patch.msgid.link/20260709-ixp4xx-serial-hackfix-v2-1-465fc8e4c54c@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_early.c | 19 ++++++++++++++++++- drivers/tty/serial/8250/8250_of.c | 12 ++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/8250/8250_early.c b/drivers/tty/serial/8250/8250_early.c index dc0371857ecb..44ec209f37c4 100644 --- a/drivers/tty/serial/8250/8250_early.c +++ b/drivers/tty/serial/8250/8250_early.c @@ -23,6 +23,7 @@ * console=uart8250,mmio32,0xff5e0000,115200n8 */ +#include #include #include #include @@ -177,6 +178,23 @@ OF_EARLYCON_DECLARE(ns16550a, "ns16550a", early_serial8250_setup); OF_EARLYCON_DECLARE(uart, "nvidia,tegra20-uart", early_serial8250_setup); OF_EARLYCON_DECLARE(uart, "snps,dw-apb-uart", early_serial8250_setup); +static int __init early_serial8250_xscale_setup(struct earlycon_device *device, + const char *options) +{ + /* + * Adjust for BE32 register accesses: drop any hardcoded + * address for the big endian byte target, add it explicitly + * if running on BE32. + */ + device->port.membase = PTR_ALIGN_DOWN(device->port.membase, 4); + if (IS_ENABLED(CONFIG_CPU_ENDIAN_BE32)) + device->port.membase += 3; + device->port.regshift = 2; + + return early_serial8250_setup(device, options); +} +OF_EARLYCON_DECLARE(uart, "intel,xscale-uart", early_serial8250_xscale_setup); + static int __init early_serial8250_rs2_setup(struct earlycon_device *device, const char *options) { @@ -184,7 +202,6 @@ static int __init early_serial8250_rs2_setup(struct earlycon_device *device, return early_serial8250_setup(device, options); } -OF_EARLYCON_DECLARE(uart, "intel,xscale-uart", early_serial8250_rs2_setup); OF_EARLYCON_DECLARE(uart, "mrvl,mmp-uart", early_serial8250_rs2_setup); OF_EARLYCON_DECLARE(uart, "mrvl,pxa-uart", early_serial8250_rs2_setup); diff --git a/drivers/tty/serial/8250/8250_of.c b/drivers/tty/serial/8250/8250_of.c index 81644d40b09a..f0537fb6ef4f 100644 --- a/drivers/tty/serial/8250/8250_of.c +++ b/drivers/tty/serial/8250/8250_of.c @@ -5,6 +5,7 @@ * Copyright (C) 2006 Arnd Bergmann , IBM Corp. */ +#include #include #include #include @@ -122,6 +123,17 @@ static int of_platform_serial_setup(struct platform_device *ofdev, if (ret) goto err_pmruntime; + if (IS_ENABLED(CONFIG_CPU_XSCALE) && type == PORT_XSCALE) { + /* + * Adjust for BE32 register accesses: drop any hardcoded + * address for the big endian byte target, add it explicitly + * if running on BE32. + */ + port->mapbase = PTR_ALIGN_DOWN(port->mapbase, 4); + if (IS_ENABLED(CONFIG_CPU_ENDIAN_BE32)) + port->mapbase += 3; + } + /* Get clk rate through clk driver if present */ if (!port->uartclk) { struct clk *bus_clk; From 3d71f8d7eeb374d0eb84c64c6ffd68bdcc0d42d4 Mon Sep 17 00:00:00 2001 From: Praveen Talari Date: Fri, 10 Jul 2026 14:51:32 +0530 Subject: [PATCH 31/51] serial: qcom-geni: remove .pm callback, use runtime PM in startup/shutdown The driver currently relies on qcom_geni_serial_pm() through the uart_ops.pm callback to manage runtime PM references. However, the callback has a void return type, so failures from pm_runtime_resume_and_get() cannot be propagated to the caller. As a result, startup() may continue and access hardware even when the runtime PM resume operation failed, leading to register accesses while the device is not powered. Move runtime PM acquisition to qcom_geni_serial_startup() and release it to qcom_geni_serial_shutdown(). Since startup() can return an error, PM resume failures are now detected and propagated before any hardware initialization is performed. The startup/shutdown pair also provides a natural place to balance runtime PM references for normal port usage. During probe, uart_add_one_port() may configure the port before any user opens the TTY, meaning startup() has not yet been called. To keep the hardware powered during port registration, wrap uart_add_one_port() with PM_RUNTIME_ACQUIRE_IF_ENABLED() and PM_RUNTIME_ACQUIRE_ERR(). This ensures the device is resumed for the duration of registration and that the runtime PM reference is automatically released afterwards. By moving runtime PM handling out of uart_ops.pm, resume failures are no longer silently ignored and all hardware accesses are guaranteed to occur while the device is powered. Signed-off-by: Praveen Talari Link: https://patch.msgid.link/20260710-remove_uart_change_state-v1-1-8e8468da22a1@oss.qualcomm.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/qcom_geni_serial.c | 37 +++++++++++++-------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c index e037af56a159..53d221447557 100644 --- a/drivers/tty/serial/qcom_geni_serial.c +++ b/drivers/tty/serial/qcom_geni_serial.c @@ -1173,6 +1173,8 @@ static void qcom_geni_serial_shutdown(struct uart_port *uport) qcom_geni_serial_cancel_tx_cmd(uport); uart_port_unlock_irq(uport); + + pm_runtime_put_sync(uport->dev); } static void qcom_geni_serial_flush_buffer(struct uart_port *uport) @@ -1246,10 +1248,18 @@ static int qcom_geni_serial_startup(struct uart_port *uport) struct qcom_geni_serial_port *port = to_dev_port(uport); struct tty_port *tport = &uport->state->port; + ret = pm_runtime_resume_and_get(uport->dev); + if (ret < 0) { + dev_err(uport->dev, "Failed to resume and get %d\n", ret); + return ret; + } + if (!port->setup) { ret = qcom_geni_serial_port_setup(uport); - if (ret) + if (ret) { + pm_runtime_put_sync(uport->dev); return ret; + } } /* @@ -1731,22 +1741,6 @@ static int geni_serial_resource_init(struct uart_port *uport) return 0; } -static void qcom_geni_serial_pm(struct uart_port *uport, - unsigned int new_state, unsigned int old_state) -{ - - /* If we've never been called, treat it as off */ - if (old_state == UART_PM_STATE_UNDEFINED) - old_state = UART_PM_STATE_OFF; - - if (new_state == UART_PM_STATE_ON && old_state == UART_PM_STATE_OFF) - pm_runtime_resume_and_get(uport->dev); - else if (new_state == UART_PM_STATE_OFF && - old_state == UART_PM_STATE_ON) - pm_runtime_put_sync(uport->dev); - -} - /** * qcom_geni_rs485_config - Configure RS485 settings for the UART port * @uport: Pointer to the UART port structure @@ -1785,7 +1779,6 @@ static const struct uart_ops qcom_geni_console_pops = { .poll_put_char = qcom_geni_serial_poll_put_char, .poll_init = qcom_geni_serial_poll_init, #endif - .pm = qcom_geni_serial_pm, }; static const struct uart_ops qcom_geni_uart_pops = { @@ -1802,7 +1795,6 @@ static const struct uart_ops qcom_geni_uart_pops = { .type = qcom_geni_serial_get_type, .set_mctrl = qcom_geni_serial_set_mctrl, .get_mctrl = qcom_geni_serial_get_mctrl, - .pm = qcom_geni_serial_pm, }; static int qcom_geni_serial_probe(struct platform_device *pdev) @@ -1936,6 +1928,13 @@ static int qcom_geni_serial_probe(struct platform_device *pdev) devm_pm_runtime_enable(port->se.dev); + PM_RUNTIME_ACQUIRE_IF_ENABLED(uport->dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret < 0) { + dev_err(uport->dev, "Failed to resume and get %d\n", ret); + goto error; + } + ret = uart_add_one_port(drv, uport); if (ret) goto error; From 3d406299d8829747fe2e8692f4c29fe3dc1d101f Mon Sep 17 00:00:00 2001 From: Hugo Villeneuve Date: Thu, 21 May 2026 14:16:47 -0400 Subject: [PATCH 32/51] serial: 8250_hub6: add hub6_match_port() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the entire hub6 related match port check into its own function in 8250_hub6.c and add a stub for the case when hub6 code is not even built into kernel. Suggested-by: Ilpo Järvinen Signed-off-by: Hugo Villeneuve Link: https://patch.msgid.link/20260521-tty-upio-v3-1-bf74567994a0@dimonoff.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250.h | 7 +++++++ drivers/tty/serial/8250/8250_hub6.c | 6 ++++++ drivers/tty/serial/serial_core.c | 4 ++-- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h index 77fe0588fd6b..9d1068d0489d 100644 --- a/drivers/tty/serial/8250/8250.h +++ b/drivers/tty/serial/8250/8250.h @@ -334,6 +334,13 @@ int fintek_8250_probe(struct uart_8250_port *uart); static inline int fintek_8250_probe(struct uart_8250_port *uart) { return 0; } #endif +#ifdef CONFIG_SERIAL_8250_HUB6 +bool hub6_match_port(const struct uart_port *port1, const struct uart_port *port2); +#else +static inline bool hub6_match_port(const struct uart_port *port1, const struct uart_port *port2) +{ return false; } +#endif + #ifdef CONFIG_ARCH_OMAP1 #include static inline int is_omap1_8250(struct uart_8250_port *pt) diff --git a/drivers/tty/serial/8250/8250_hub6.c b/drivers/tty/serial/8250/8250_hub6.c index 273f59b9bca5..eae32c924e29 100644 --- a/drivers/tty/serial/8250/8250_hub6.c +++ b/drivers/tty/serial/8250/8250_hub6.c @@ -41,6 +41,12 @@ static struct platform_device hub6_device = { }, }; +bool hub6_match_port(const struct uart_port *port1, const struct uart_port *port2) +{ + return port1->iobase == port2->iobase && port1->hub6 == port2->hub6; +} +EXPORT_SYMBOL_GPL(hub6_match_port); + static int __init hub6_init(void) { return platform_device_register(&hub6_device); diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index a530ad372b43..965ba0335a36 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -33,6 +33,7 @@ #include #include "serial_base.h" +#include "8250/8250.h" /* For hub6_match_port() */ /* * This is used to lock changes in serial line configuration. @@ -3213,8 +3214,7 @@ bool uart_match_port(const struct uart_port *port1, case UPIO_PORT: return port1->iobase == port2->iobase; case UPIO_HUB6: - return port1->iobase == port2->iobase && - port1->hub6 == port2->hub6; + return hub6_match_port(port1, port2); case UPIO_MEM: case UPIO_MEM16: case UPIO_MEM32: From 6e85378a38cebf9b437bc2b5c22dc1ba58161bd2 Mon Sep 17 00:00:00 2001 From: Hugo Villeneuve Date: Thu, 21 May 2026 14:16:48 -0400 Subject: [PATCH 33/51] serial: core: add uart_iotype_mmio/io helper functions To help simplify code that check on the io type mode of the port. Signed-off-by: Hugo Villeneuve Link: https://patch.msgid.link/20260521-tty-upio-v3-2-bf74567994a0@dimonoff.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_core.c | 28 ++++++++++++++++++++++++++++ include/linux/serial_core.h | 4 ++++ 2 files changed, 32 insertions(+) diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 965ba0335a36..d82f3ff44532 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -1963,6 +1963,34 @@ static const char *uart_type(struct uart_port *port) return str; } +bool uart_iotype_mmio(enum uart_iotype iotype) +{ + switch (iotype) { + case UPIO_MEM: + case UPIO_MEM32: + case UPIO_AU: + case UPIO_TSI: + case UPIO_MEM32BE: + case UPIO_MEM16: + return true; + default: + return false; + } +} +EXPORT_SYMBOL_GPL(uart_iotype_mmio); + +bool uart_iotype_io(enum uart_iotype iotype) +{ + switch (iotype) { + case UPIO_PORT: + case UPIO_HUB6: + return true; + default: + return false; + } +} +EXPORT_SYMBOL_GPL(uart_iotype_io); + #ifdef CONFIG_PROC_FS static void uart_line_info(struct seq_file *m, struct uart_state *state) diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index bdc214386e4a..3cfde1af12fe 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -1338,4 +1338,8 @@ static inline int uart_handle_break(struct uart_port *port) !((cflag) & CLOCAL)) int uart_get_rs485_mode(struct uart_port *port); + +bool uart_iotype_mmio(enum uart_iotype iotype); +bool uart_iotype_io(enum uart_iotype iotype); + #endif /* LINUX_SERIAL_CORE_H */ From 11f1d49122ec2227b050f4596a7422606237347e Mon Sep 17 00:00:00 2001 From: Hugo Villeneuve Date: Thu, 21 May 2026 14:16:49 -0400 Subject: [PATCH 34/51] serial: core: use uart_iotype_*() to simplify uart_match_port() Make use of new functions uart_iotype_mmio() and uart_iotype_legacy_io() to simplify and improve code readability. Signed-off-by: Hugo Villeneuve Link: https://patch.msgid.link/20260521-tty-upio-v3-3-bf74567994a0@dimonoff.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_core.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index d82f3ff44532..6a8f27718648 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -3237,22 +3237,14 @@ bool uart_match_port(const struct uart_port *port1, { if (port1->iotype != port2->iotype) return false; - - switch (port1->iotype) { - case UPIO_PORT: + else if (port1->iotype == UPIO_PORT) return port1->iobase == port2->iobase; - case UPIO_HUB6: + else if (port1->iotype == UPIO_HUB6) return hub6_match_port(port1, port2); - case UPIO_MEM: - case UPIO_MEM16: - case UPIO_MEM32: - case UPIO_MEM32BE: - case UPIO_AU: - case UPIO_TSI: + else if (uart_iotype_mmio(port1->iotype)) return port1->mapbase == port2->mapbase; - default: + else return false; - } } EXPORT_SYMBOL(uart_match_port); From 86305190f307c05bc2e0660f4ce16b7aeca6711b Mon Sep 17 00:00:00 2001 From: Hugo Villeneuve Date: Thu, 21 May 2026 14:16:50 -0400 Subject: [PATCH 35/51] serial: uniformize serial port I/O infos display Uniformize serial port I/O infos display from three different functions that display mostly the same information, but with some variations, by adding a common function. This make use of new functions uart_iotype_mmio() and uart_iotype_legacy_io() to simplify and improve code readability. This will prevent displaying irrelevant information for future IO types (ex: UPIO_BUS), while also addressing the (eventually) invalid check for "iotype >= UPIO_MEM". This also allows us to remove the confusing cast to (unsigned long long) for iobase which is defined as an unsigned long, and use %pa to display the mapbase pointer, as it is done in earlycon_print_info(). Replace snprintf with more robust scnprintf so we could perhaps one day get rid of snprintf() entirely. Signed-off-by: Hugo Villeneuve Link: https://patch.msgid.link/20260521-tty-upio-v3-4-bf74567994a0@dimonoff.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/earlycon.c | 17 +++---- drivers/tty/serial/serial_core.c | 76 +++++++++++++++++--------------- include/linux/serial_core.h | 1 + 3 files changed, 47 insertions(+), 47 deletions(-) diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c index ab9af37f6cda..ce740cdc7ceb 100644 --- a/drivers/tty/serial/earlycon.c +++ b/drivers/tty/serial/earlycon.c @@ -75,19 +75,12 @@ static void __init earlycon_print_info(struct earlycon_device *device) { struct console *earlycon = device->con; struct uart_port *port = &device->port; + char ioinfos[64]; - if (port->iotype == UPIO_MEM || port->iotype == UPIO_MEM16 || - port->iotype == UPIO_MEM32 || port->iotype == UPIO_MEM32BE) - pr_info("%s%d at MMIO%s %pa (options '%s')\n", - earlycon->name, earlycon->index, - (port->iotype == UPIO_MEM) ? "" : - (port->iotype == UPIO_MEM16) ? "16" : - (port->iotype == UPIO_MEM32) ? "32" : "32be", - &port->mapbase, device->options); - else - pr_info("%s%d at I/O port 0x%lx (options '%s')\n", - earlycon->name, earlycon->index, - port->iobase, device->options); + uart_get_ioinfos(port, ioinfos, sizeof(ioinfos)); + + pr_info("%s%d%s (options '%s')\n", earlycon->name, earlycon->index, + ioinfos, device->options); } static int __init parse_options(struct earlycon_device *device, char *options) diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 6a8f27718648..9b16480b374a 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -1998,9 +1998,9 @@ static void uart_line_info(struct seq_file *m, struct uart_state *state) struct tty_port *port = &state->port; enum uart_pm_state pm_state; struct uart_port *uport; + char ioinfos[64]; char stat_buf[32]; unsigned int status; - int mmio; guard(mutex)(&port->mutex); @@ -2008,13 +2008,10 @@ static void uart_line_info(struct seq_file *m, struct uart_state *state) if (!uport) return; - mmio = uport->iotype >= UPIO_MEM; - seq_printf(m, "%u: uart:%s %s%08llX irq:%u", - uport->line, uart_type(uport), - mmio ? "mmio:0x" : "port:", - mmio ? (unsigned long long)uport->mapbase - : (unsigned long long)uport->iobase, - uport->irq); + seq_printf(m, "%u: uart:%s", uport->line, uart_type(uport)); + uart_get_ioinfos(uport, ioinfos, sizeof(ioinfos)); + seq_printf(m, "%s", ioinfos); + seq_printf(m, " irq:%u", uport->irq); if (uport->type == PORT_UNKNOWN) { seq_putc(m, '\n'); @@ -2488,38 +2485,47 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport) } EXPORT_SYMBOL(uart_resume_port); +static const char *uart_get_mmio_width(struct uart_port *port) +{ + switch (port->iotype) { + case UPIO_MEM16: + return "16"; + case UPIO_MEM32: + case UPIO_MEM32BE: + return "32be"; + case UPIO_AU: + case UPIO_MEM: + default: + return ""; + } +} + +void uart_get_ioinfos(struct uart_port *port, char *buf, size_t size) +{ + buf[0] = '\0'; + + if (uart_iotype_mmio(port->iotype)) { + scnprintf(buf, size, " MMIO%s:%pa", uart_get_mmio_width(port), &port->mapbase); + } else if (uart_iotype_io(port->iotype)) { + if (port->iotype == UPIO_PORT) + scnprintf(buf, size, " I/O:0x%lx", port->iobase); + else if (port->iotype == UPIO_HUB6) + scnprintf(buf, size, " I/O:0x%lx, offset 0x%x", port->iobase, port->hub6); + } +} +EXPORT_SYMBOL(uart_get_ioinfos); + static inline void uart_report_port(struct uart_driver *drv, struct uart_port *port) { - char address[64]; + char ioinfos[64]; - switch (port->iotype) { - case UPIO_PORT: - snprintf(address, sizeof(address), "I/O 0x%lx", port->iobase); - break; - case UPIO_HUB6: - snprintf(address, sizeof(address), - "I/O 0x%lx offset 0x%x", port->iobase, port->hub6); - break; - case UPIO_MEM: - case UPIO_MEM16: - case UPIO_MEM32: - case UPIO_MEM32BE: - case UPIO_AU: - case UPIO_TSI: - snprintf(address, sizeof(address), - "MMIO 0x%llx", (unsigned long long)port->mapbase); - break; - default: - strscpy(address, "*unknown*", sizeof(address)); - break; - } + uart_get_ioinfos(port, ioinfos, sizeof(ioinfos)); - pr_info("%s%s%s at %s (irq = %u, base_baud = %u) is a %s\n", - port->dev ? dev_name(port->dev) : "", - port->dev ? ": " : "", - port->name, - address, port->irq, port->uartclk / 16, uart_type(port)); + pr_info("%s%s%s%s (irq = %u, base_baud = %u) is a %s\n", + port->dev ? dev_name(port->dev) : "", + port->dev ? ": " : "", + port->name, ioinfos, port->irq, port->uartclk / 16, uart_type(port)); /* The magic multiplier feature is a bit obscure, so report it too. */ if (port->flags & UPF_MAGIC_MULTIPLIER) diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 3cfde1af12fe..66b1459d53fc 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -1339,6 +1339,7 @@ static inline int uart_handle_break(struct uart_port *port) int uart_get_rs485_mode(struct uart_port *port); +void uart_get_ioinfos(struct uart_port *port, char *buf, size_t size); bool uart_iotype_mmio(enum uart_iotype iotype); bool uart_iotype_io(enum uart_iotype iotype); From 548aa0c850081dc923ed057a1ae3ec80cbbba618 Mon Sep 17 00:00:00 2001 From: Hugo Villeneuve Date: Thu, 21 May 2026 14:16:51 -0400 Subject: [PATCH 36/51] serial: 8250: use uart_iotype_*() to simplify code Make use of new functions uart_iotype_mmio() and uart_iotype_legacy_io() to simplify and improve code readability. Signed-off-by: Hugo Villeneuve Link: https://patch.msgid.link/20260521-tty-upio-v3-5-bf74567994a0@dimonoff.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_port.c | 43 +++++------------------------ 1 file changed, 7 insertions(+), 36 deletions(-) diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index 630deb7dd344..dd63aedb8675 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -474,16 +474,10 @@ static void set_io_from_upio(struct uart_port *p) static void serial_port_out_sync(struct uart_port *p, int offset, int value) { - switch (p->iotype) { - case UPIO_MEM: - case UPIO_MEM16: - case UPIO_MEM32: - case UPIO_MEM32BE: - case UPIO_AU: + if (uart_iotype_mmio(p->iotype)) { p->serial_out(p, offset, value); p->serial_in(p, UART_LCR); /* safe, no side-effects */ - break; - default: + } else { p->serial_out(p, offset, value); } } @@ -2885,13 +2879,7 @@ static int serial8250_request_std_resource(struct uart_8250_port *up) unsigned int size = serial8250_port_size(up); struct uart_port *port = &up->port; - switch (port->iotype) { - case UPIO_AU: - case UPIO_TSI: - case UPIO_MEM32: - case UPIO_MEM32BE: - case UPIO_MEM16: - case UPIO_MEM: + if (uart_iotype_mmio(port->iotype)) { if (!port->mapbase) return -EINVAL; @@ -2905,14 +2893,9 @@ static int serial8250_request_std_resource(struct uart_8250_port *up) return -ENOMEM; } } - return 0; - case UPIO_HUB6: - case UPIO_PORT: + } else if (uart_iotype_io(port->iotype)) { if (!request_region(port->iobase, size, "serial")) return -EBUSY; - return 0; - case UPIO_UNKNOWN: - break; } return 0; @@ -2923,15 +2906,9 @@ static void serial8250_release_std_resource(struct uart_8250_port *up) unsigned int size = serial8250_port_size(up); struct uart_port *port = &up->port; - switch (port->iotype) { - case UPIO_AU: - case UPIO_TSI: - case UPIO_MEM32: - case UPIO_MEM32BE: - case UPIO_MEM16: - case UPIO_MEM: + if (uart_iotype_mmio(port->iotype)) { if (!port->mapbase) - break; + return; if (port->flags & UPF_IOREMAP) { iounmap(port->membase); @@ -2939,14 +2916,8 @@ static void serial8250_release_std_resource(struct uart_8250_port *up) } release_mem_region(port->mapbase, size); - break; - - case UPIO_HUB6: - case UPIO_PORT: + } else if (uart_iotype_io(port->iotype)) { release_region(port->iobase, size); - break; - case UPIO_UNKNOWN: - break; } } From 650d60c734ced3c0efa115d6bad031a24680be4e Mon Sep 17 00:00:00 2001 From: Hugo Villeneuve Date: Thu, 21 May 2026 14:16:52 -0400 Subject: [PATCH 37/51] serial: 8250_rsa: use uart_iotype_*() to simplify code Make use of new functions uart_iotype_mmio() and uart_iotype_legacy_io() to simplify and improve code readability, as well as avoid some variables init if the iotype is not valid. Signed-off-by: Hugo Villeneuve Link: https://patch.msgid.link/20260521-tty-upio-v3-6-bf74567994a0@dimonoff.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_rsa.c | 40 ++++++++++++++---------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/drivers/tty/serial/8250/8250_rsa.c b/drivers/tty/serial/8250/8250_rsa.c index fff9395948e3..da971437e89c 100644 --- a/drivers/tty/serial/8250/8250_rsa.c +++ b/drivers/tty/serial/8250/8250_rsa.c @@ -19,35 +19,33 @@ static const struct uart_ops *core_port_base_ops; static int rsa8250_request_resource(struct uart_8250_port *up) { struct uart_port *port = &up->port; - unsigned long start = UART_RSA_BASE << port->regshift; - unsigned int size = 8 << port->regshift; + unsigned long start; + unsigned int size; - switch (port->iotype) { - case UPIO_HUB6: - case UPIO_PORT: - start += port->iobase; - if (!request_region(start, size, "serial-rsa")) - return -EBUSY; - return 0; - default: + if (!uart_iotype_io(port->iotype)) return -EINVAL; - } + + start = UART_RSA_BASE << port->regshift; + start += port->iobase; + size = 8 << port->regshift; + + if (!request_region(start, size, "serial-rsa")) + return -EBUSY; + return 0; } static void rsa8250_release_resource(struct uart_8250_port *up) { struct uart_port *port = &up->port; - unsigned long offset = UART_RSA_BASE << port->regshift; - unsigned int size = 8 << port->regshift; + unsigned long offset; + unsigned int size; - switch (port->iotype) { - case UPIO_HUB6: - case UPIO_PORT: - release_region(port->iobase + offset, size); - break; - default: - break; - } + if (!uart_iotype_io(port->iotype)) + return; + + offset = UART_RSA_BASE << port->regshift; + size = 8 << port->regshift; + release_region(port->iobase + offset, size); } static void univ8250_config_port(struct uart_port *port, int flags) From 44b374622a3f93f2f9ce0a782f3fedb7cd758b6b Mon Sep 17 00:00:00 2001 From: Hugo Villeneuve Date: Thu, 21 May 2026 14:16:53 -0400 Subject: [PATCH 38/51] serial: core: add new I/O type for SPI and I2C bus devices I2C/SPI serial drivers don't use the following struct uart_port variables: port->membase port->mapbase port->iobase However, they are forced to set membase to a non-zero value so that uart_configure_port() will succeed because of the following check: /* If there isn't a port here, don't do anything further. */ if (!port->iobase && !port->mapbase && !port->membase) return; Add a new I/O type for SPI and I2C bus devices to remove the need to implement the kind of above-mentioned ambiguous workarounds to make them work. Now that UART report functions are using uart_iotype_*() functions, no more irrelevant I/O information are being printed for UPIO_BUS iotypes. Signed-off-by: Hugo Villeneuve Link: https://patch.msgid.link/20260521-tty-upio-v3-7-bf74567994a0@dimonoff.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_core.c | 11 ++++++----- include/linux/serial_core.h | 1 + include/uapi/linux/serial.h | 1 + 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 9b16480b374a..06d7f44643d6 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -2542,11 +2542,10 @@ uart_configure_port(struct uart_driver *drv, struct uart_state *state, { unsigned int flags; - /* - * If there isn't a port here, don't do anything further. - */ - if (!port->iobase && !port->mapbase && !port->membase) - return; + /* If there isn't a port here, don't do anything further. */ + if (uart_iotype_mmio(port->iotype) || uart_iotype_io(port->iotype)) + if (!port->iobase && !port->mapbase && !port->membase) + return; /* * Now do the auto configuration stuff. Note that config_port @@ -3249,6 +3248,8 @@ bool uart_match_port(const struct uart_port *port1, return hub6_match_port(port1, port2); else if (uart_iotype_mmio(port1->iotype)) return port1->mapbase == port2->mapbase; + else if (port1->iotype == UPIO_BUS) + return true; else return false; } diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 66b1459d53fc..c4cc4f66af4b 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -437,6 +437,7 @@ enum uart_iotype { UPIO_TSI = SERIAL_IO_TSI, /* Tsi108/109 type IO */ UPIO_MEM32BE = SERIAL_IO_MEM32BE, /* 32b big endian */ UPIO_MEM16 = SERIAL_IO_MEM16, /* 16b little endian */ + UPIO_BUS = SERIAL_IO_BUS, /* Serial bus I/O access (ex: SPI, I2C) */ }; struct uart_port { diff --git a/include/uapi/linux/serial.h b/include/uapi/linux/serial.h index de9b4733607e..e6f61538fc28 100644 --- a/include/uapi/linux/serial.h +++ b/include/uapi/linux/serial.h @@ -72,6 +72,7 @@ struct serial_struct { #define SERIAL_IO_TSI 5 #define SERIAL_IO_MEM32BE 6 #define SERIAL_IO_MEM16 7 +#define SERIAL_IO_BUS 8 #define UART_CLEAR_FIFO 0x01 #define UART_USE_FIFO 0x02 From ac60073d41fb00be2aa0ccb88b2445b95057700b Mon Sep 17 00:00:00 2001 From: Hugo Villeneuve Date: Thu, 21 May 2026 14:16:54 -0400 Subject: [PATCH 39/51] serial: sc16is7xx: use new UPIO_BUS as iotype Now that we have a new UPIO_BUS I/O type, use it to register our serial port and remove ambiguous membase/iobase workaround. Note that commit 5da6b1c079e6 ("sc16is7xx: Set iobase to device index") used the iobase field as an index within the device to allow infering the order through sysfs, but this is no longer needed since commit 1ef2c2df1199 ("serial: core: Fix serial core controller port name to show controller id"). Signed-off-by: Hugo Villeneuve Link: https://patch.msgid.link/20260521-tty-upio-v3-8-bf74567994a0@dimonoff.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sc16is7xx.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index 1fd64a47341d..4b638e69f36f 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -1472,14 +1472,7 @@ static int sc16is7xx_setup_channel(struct sc16is7xx_one *one, int i, port->type = PORT_SC16IS7XX; port->fifosize = SC16IS7XX_FIFO_SIZE; port->flags = UPF_FIXED_TYPE | UPF_LOW_LATENCY; - port->iobase = i; - /* - * Use all ones as membase to make sure uart_configure_port() in - * serial_core.c does not abort for SPI/I2C devices where the - * membase address is not applicable. - */ - port->membase = (void __iomem *)~0; - port->iotype = UPIO_PORT; + port->iotype = UPIO_BUS; port->rs485_config = sc16is7xx_config_rs485; port->rs485_supported = sc16is7xx_rs485_supported; port->ops = &sc16is7xx_ops; From 03efc41d6b840fb7d4fd58aedbccc76ad3105f47 Mon Sep 17 00:00:00 2001 From: Hugo Villeneuve Date: Thu, 21 May 2026 14:16:55 -0400 Subject: [PATCH 40/51] serial: max310x: use new UPIO_BUS as iotype Now that we have a new UPIO_BUS I/O type, use it to register our serial port and remove obscure membase/iobase workaround. Signed-off-by: Hugo Villeneuve Link: https://patch.msgid.link/20260521-tty-upio-v3-9-bf74567994a0@dimonoff.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/max310x.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index 022502986c5f..7592e69956d9 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -1401,14 +1401,7 @@ static int max310x_probe(struct device *dev, const struct max310x_devtype *devty s->p[i].port.type = PORT_MAX310X; s->p[i].port.fifosize = MAX310X_FIFO_SIZE; s->p[i].port.flags = UPF_FIXED_TYPE | UPF_LOW_LATENCY; - s->p[i].port.iotype = UPIO_PORT; - s->p[i].port.iobase = i; - /* - * Use all ones as membase to make sure uart_configure_port() in - * serial_core.c does not abort for SPI/I2C devices where the - * membase address is not applicable. - */ - s->p[i].port.membase = (void __iomem *)~0; + s->p[i].port.iotype = UPIO_BUS; s->p[i].port.uartclk = uartclk; s->p[i].port.rs485_config = max310x_rs485_config; s->p[i].port.rs485_supported = max310x_rs485_supported; From bef5e068b89b0f0cf974c987ebba9869a14b44c6 Mon Sep 17 00:00:00 2001 From: Hugo Villeneuve Date: Thu, 21 May 2026 14:16:56 -0400 Subject: [PATCH 41/51] serial: max3100: use new UPIO_BUS as iotype Now that we have a new UPIO_BUS I/O type, use it to register our serial port. This allows the driver to work properly when using DT where membase/iobase are not set. Signed-off-by: Hugo Villeneuve Link: https://patch.msgid.link/20260521-tty-upio-v3-10-bf74567994a0@dimonoff.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/max3100.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/tty/serial/max3100.c b/drivers/tty/serial/max3100.c index 475b0a6efce4..17a2ff410305 100644 --- a/drivers/tty/serial/max3100.c +++ b/drivers/tty/serial/max3100.c @@ -725,6 +725,7 @@ static int max3100_probe(struct spi_device *spi) max3100s[i]->port.ops = &max3100_ops; max3100s[i]->port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF; max3100s[i]->port.line = i; + max3100s[i]->port.iotype = UPIO_BUS; max3100s[i]->port.type = PORT_MAX3100; max3100s[i]->port.dev = &spi->dev; From da7b5fd4e17f8e44c5590f2d603c01d499f056e6 Mon Sep 17 00:00:00 2001 From: Hugo Villeneuve Date: Mon, 13 Jul 2026 21:26:07 -0400 Subject: [PATCH 42/51] serial: 8250_hub6: add missing include for hub6_match_port() Add missing include to fix compile warning: drivers/tty/serial/8250/8250_hub6.c:44:6: warning: no previous prototype for 'hub6_match_port' [-Wmissing-prototypes] Fixes: 3d406299d882 ("serial: 8250_hub6: add hub6_match_port()") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202607110715.VGT2dVVz-lkp@intel.com/ Closes: https://lore.kernel.org/oe-kbuild-all/202607111219.QG9uOW8H-lkp@intel.com/ Signed-off-by: Hugo Villeneuve Link: https://patch.msgid.link/20260714012610.576746-1-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_hub6.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/tty/serial/8250/8250_hub6.c b/drivers/tty/serial/8250/8250_hub6.c index eae32c924e29..b6767633c966 100644 --- a/drivers/tty/serial/8250/8250_hub6.c +++ b/drivers/tty/serial/8250/8250_hub6.c @@ -7,6 +7,8 @@ #include #include +#include "8250.h" + #define HUB6(card, port) \ { \ .iobase = 0x302, \ From facd005eee65df088cd73733741fbcf8d989fc5e Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 17 Jul 2026 12:57:02 +0200 Subject: [PATCH 43/51] Revert "serial: qcom-geni: remove .pm callback, use runtime PM in startup/shutdown" This reverts commit 3d71f8d7eeb374d0eb84c64c6ffd68bdcc0d42d4. It causes lots of build problems both in linux-next and reported by the 0-day bot. Reported-by: Mark Brown Reported-by: kernel test robot Cc: Praveen Talari Closes: https://lore.kernel.org/oe-kbuild-all/202607110008.JQ2vBeKC-lkp@intel.com/ Closes: https://lore.kernel.org/oe-kbuild-all/202607150830.LsNxeVYw-lkp@intel.com/ Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/qcom_geni_serial.c | 37 ++++++++++++++------------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c index 53d221447557..e037af56a159 100644 --- a/drivers/tty/serial/qcom_geni_serial.c +++ b/drivers/tty/serial/qcom_geni_serial.c @@ -1173,8 +1173,6 @@ static void qcom_geni_serial_shutdown(struct uart_port *uport) qcom_geni_serial_cancel_tx_cmd(uport); uart_port_unlock_irq(uport); - - pm_runtime_put_sync(uport->dev); } static void qcom_geni_serial_flush_buffer(struct uart_port *uport) @@ -1248,18 +1246,10 @@ static int qcom_geni_serial_startup(struct uart_port *uport) struct qcom_geni_serial_port *port = to_dev_port(uport); struct tty_port *tport = &uport->state->port; - ret = pm_runtime_resume_and_get(uport->dev); - if (ret < 0) { - dev_err(uport->dev, "Failed to resume and get %d\n", ret); - return ret; - } - if (!port->setup) { ret = qcom_geni_serial_port_setup(uport); - if (ret) { - pm_runtime_put_sync(uport->dev); + if (ret) return ret; - } } /* @@ -1741,6 +1731,22 @@ static int geni_serial_resource_init(struct uart_port *uport) return 0; } +static void qcom_geni_serial_pm(struct uart_port *uport, + unsigned int new_state, unsigned int old_state) +{ + + /* If we've never been called, treat it as off */ + if (old_state == UART_PM_STATE_UNDEFINED) + old_state = UART_PM_STATE_OFF; + + if (new_state == UART_PM_STATE_ON && old_state == UART_PM_STATE_OFF) + pm_runtime_resume_and_get(uport->dev); + else if (new_state == UART_PM_STATE_OFF && + old_state == UART_PM_STATE_ON) + pm_runtime_put_sync(uport->dev); + +} + /** * qcom_geni_rs485_config - Configure RS485 settings for the UART port * @uport: Pointer to the UART port structure @@ -1779,6 +1785,7 @@ static const struct uart_ops qcom_geni_console_pops = { .poll_put_char = qcom_geni_serial_poll_put_char, .poll_init = qcom_geni_serial_poll_init, #endif + .pm = qcom_geni_serial_pm, }; static const struct uart_ops qcom_geni_uart_pops = { @@ -1795,6 +1802,7 @@ static const struct uart_ops qcom_geni_uart_pops = { .type = qcom_geni_serial_get_type, .set_mctrl = qcom_geni_serial_set_mctrl, .get_mctrl = qcom_geni_serial_get_mctrl, + .pm = qcom_geni_serial_pm, }; static int qcom_geni_serial_probe(struct platform_device *pdev) @@ -1928,13 +1936,6 @@ static int qcom_geni_serial_probe(struct platform_device *pdev) devm_pm_runtime_enable(port->se.dev); - PM_RUNTIME_ACQUIRE_IF_ENABLED(uport->dev, pm); - ret = PM_RUNTIME_ACQUIRE_ERR(&pm); - if (ret < 0) { - dev_err(uport->dev, "Failed to resume and get %d\n", ret); - goto error; - } - ret = uart_add_one_port(drv, uport); if (ret) goto error; From 7ab80d1e72431f5b7acbc55d83a23b76814957cf Mon Sep 17 00:00:00 2001 From: Hugo Villeneuve Date: Wed, 15 Jul 2026 11:37:05 -0400 Subject: [PATCH 44/51] serial: 8250: fix compile error with hub6_match_port() when compiled as a module With CONFIG_SERIAL_8250_HUB6=m, we have the following compile error: ../drivers/tty/serial/8250/8250_hub6.c:46:6: error: redefinition of 'hub6_match_port' Fix hub6_match_port() prototype definition by using IS_REACHABLE() to support both built-in and module values, and substitute empty prototype otherwise. Fixes: 3d406299d8829 ("serial: 8250_hub6: add hub6_match_port()") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202607150717.2YxVdWpX-lkp@intel.com/ Signed-off-by: Hugo Villeneuve Link: https://patch.msgid.link/20260715153707.4181828-1-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h index 9d1068d0489d..b62f88eec881 100644 --- a/drivers/tty/serial/8250/8250.h +++ b/drivers/tty/serial/8250/8250.h @@ -8,6 +8,7 @@ */ #include +#include #include #include #include @@ -334,7 +335,7 @@ int fintek_8250_probe(struct uart_8250_port *uart); static inline int fintek_8250_probe(struct uart_8250_port *uart) { return 0; } #endif -#ifdef CONFIG_SERIAL_8250_HUB6 +#if IS_REACHABLE(CONFIG_SERIAL_8250_HUB6) bool hub6_match_port(const struct uart_port *port1, const struct uart_port *port2); #else static inline bool hub6_match_port(const struct uart_port *port1, const struct uart_port *port2) From d7614cd72dc1680801936c1d468704426c2d142a Mon Sep 17 00:00:00 2001 From: Hugo Villeneuve Date: Thu, 16 Jul 2026 17:12:10 -0400 Subject: [PATCH 45/51] serial: core: display 32 for MEM32 and MEM32BE UPIO types Displaying "32be" is wrong for UPIO_MEM32. Fix and simplify by displaying "32" for MEM32 and MEM32BE UPIO types. Fixes: 86305190f307 ("serial: uniformize serial port I/O infos display") Signed-off-by: Hugo Villeneuve Link: https://patch.msgid.link/20260716211216.2583291-1-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 06d7f44643d6..edd1e7be2a5c 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -2492,7 +2492,7 @@ static const char *uart_get_mmio_width(struct uart_port *port) return "16"; case UPIO_MEM32: case UPIO_MEM32BE: - return "32be"; + return "32"; case UPIO_AU: case UPIO_MEM: default: From 6e5bd7cc3a2f304a66d294011647d82074421979 Mon Sep 17 00:00:00 2001 From: Praveen Talari Date: Fri, 10 Jul 2026 22:42:13 +0530 Subject: [PATCH 46/51] serial: qcom-geni: Add tracepoints for Qualcomm GENI serial driver Add tracing to the Qualcomm GENI serial driver to improve runtime observability. Trace hooks are added at key points including termios and clock configuration, manual control get/set, interrupt handling, and data TX/RX paths. Reviewed-by: Konrad Dybcio Signed-off-by: Praveen Talari Link: https://patch.msgid.link/20260710-add-tracepoints-for-qcom-geni-serial-v6-2-2bb6b6836dfd@oss.qualcomm.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/qcom_geni_serial.c | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c index e037af56a159..751738f848ad 100644 --- a/drivers/tty/serial/qcom_geni_serial.c +++ b/drivers/tty/serial/qcom_geni_serial.c @@ -7,6 +7,9 @@ /* Disable MMIO tracing to prevent excessive logging of unwanted MMIO traces */ #define __DISABLE_TRACE_MMIO__ +#define CREATE_TRACE_POINTS +#include + #include #include #include @@ -228,7 +231,7 @@ static void qcom_geni_serial_config_port(struct uart_port *uport, int cfg_flags) static unsigned int qcom_geni_serial_get_mctrl(struct uart_port *uport) { unsigned int mctrl = TIOCM_DSR | TIOCM_CAR; - u32 geni_ios; + u32 geni_ios = 0; if (uart_console(uport)) { mctrl |= TIOCM_CTS; @@ -238,6 +241,8 @@ static unsigned int qcom_geni_serial_get_mctrl(struct uart_port *uport) mctrl |= TIOCM_CTS; } + trace_geni_serial_get_mctrl(uport->dev, mctrl, geni_ios); + return mctrl; } @@ -256,6 +261,8 @@ static void qcom_geni_serial_set_mctrl(struct uart_port *uport, if (port->manual_flow && !(mctrl & TIOCM_RTS) && !uport->suspended) uart_manual_rfr = UART_MANUAL_RFR_EN | UART_RFR_NOT_READY; writel(uart_manual_rfr, uport->membase + SE_UART_MANUAL_RFR); + + trace_geni_serial_set_mctrl(uport->dev, mctrl, uart_manual_rfr); } static const char *qcom_geni_serial_get_type(struct uart_port *uport) @@ -686,6 +693,8 @@ static void qcom_geni_serial_start_tx_dma(struct uart_port *uport) xmit_size = kfifo_out_linear_ptr(&tport->xmit_fifo, &tail, UART_XMIT_SIZE); + trace_geni_serial_tx_data(uport->dev, tail, xmit_size); + qcom_geni_set_rs485_mode(uport, SER_RS485_RTS_ON_SEND); qcom_geni_serial_setup_tx(uport, xmit_size); @@ -904,8 +913,10 @@ static void qcom_geni_serial_handle_rx_dma(struct uart_port *uport, bool drop) rx_in = readl(uport->membase + SE_DMA_RX_LEN_IN); if (!rx_in) dev_warn_ratelimited(uport->dev, "serial engine reports 0 RX bytes in!\n"); - else if (!drop) + else if (!drop) { + trace_geni_serial_rx_data(uport->dev, port->rx_buf, rx_in); handle_rx_uart(uport, rx_in); + } dma_sync_single_for_device(uport->dev->parent, port->rx_dma_addr, DMA_RX_BUF_SIZE, DMA_FROM_DEVICE); @@ -1074,6 +1085,10 @@ static irqreturn_t qcom_geni_serial_isr(int isr, void *dev) geni_status = readl(uport->membase + SE_GENI_STATUS); dma = readl(uport->membase + SE_GENI_DMA_MODE_EN); m_irq_en = readl(uport->membase + SE_GENI_M_IRQ_EN); + + trace_geni_serial_irq(uport->dev, m_irq_status, s_irq_status, + dma_tx_status, dma_rx_status); + writel(m_irq_status, uport->membase + SE_GENI_M_IRQ_CLEAR); writel(s_irq_status, uport->membase + SE_GENI_S_IRQ_CLEAR); writel(dma_tx_status, uport->membase + SE_DMA_TX_IRQ_CLR); @@ -1298,8 +1313,8 @@ static int geni_serial_set_rate(struct uart_port *uport, unsigned int baud) return -EINVAL; } - dev_dbg(port->se.dev, "desired_rate = %u, clk_rate = %lu, clk_div = %u, clk_idx = %u\n", - baud * sampling_rate, clk_rate, clk_div, clk_idx); + trace_geni_serial_clk_cfg(uport->dev, baud * sampling_rate, clk_rate, + clk_div, clk_idx); uport->uartclk = clk_rate; port->clk_rate = clk_rate; @@ -1459,6 +1474,10 @@ static void qcom_geni_serial_set_termios(struct uart_port *uport, writel(bits_per_char, uport->membase + SE_UART_TX_WORD_LEN); writel(bits_per_char, uport->membase + SE_UART_RX_WORD_LEN); writel(stop_bit_len, uport->membase + SE_UART_TX_STOP_BIT_LEN); + + trace_geni_serial_set_termios(uport->dev, baud, bits_per_char, + tx_trans_cfg, tx_parity_cfg, rx_trans_cfg, + rx_parity_cfg, stop_bit_len); } #ifdef CONFIG_SERIAL_QCOM_GENI_CONSOLE From dbe2afb952bdb782450d8f678adeb502791e7edd Mon Sep 17 00:00:00 2001 From: Praveen Talari Date: Wed, 15 Jul 2026 09:55:14 +0530 Subject: [PATCH 47/51] serial: qcom_geni: Add shutdown callback to quiesce hardware on reboot During system reboot, an active UART DMA transfer can leave the GENI Serial Engine in an indeterminate state. On VM-based platforms, if a DMA transfer is in progress when the VM is shut down, the SMMU can raise context faults as the DMA engine continues to access IOVAs that have already been invalidated during VM teardown. Add a shutdown callback to stop TX and RX and bring the hardware to idle before the system resets, preventing both hardware state corruption on reboot and SMMU faults during VM shutdown. The port lock is not taken here since shutdown runs from process context with the device already quiesced from the UART core's perspective; instead, the runtime PM status is checked so that TX/RX are only stopped while clocks and resources are still active, avoiding any register access once the device is runtime suspended. Signed-off-by: Praveen Talari Link: https://patch.msgid.link/20260715-add_shutdown_and_panic_notifier_serial-v1-1-23e3787c7109@oss.qualcomm.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/qcom_geni_serial.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c index 751738f848ad..04e1227390dd 100644 --- a/drivers/tty/serial/qcom_geni_serial.c +++ b/drivers/tty/serial/qcom_geni_serial.c @@ -2112,6 +2112,18 @@ static const struct dev_pm_ops qcom_geni_serial_pm_ops = { SYSTEM_SLEEP_PM_OPS(qcom_geni_serial_suspend, qcom_geni_serial_resume) }; +static void qcom_geni_serial_sys_shutdown(struct platform_device *pdev) +{ + struct qcom_geni_serial_port *port = platform_get_drvdata(pdev); + struct uart_port *uport = &port->uport; + + if (pm_runtime_status_suspended(uport->dev)) + return; + + qcom_geni_serial_stop_tx(uport); + qcom_geni_serial_stop_rx(uport); +} + static const struct of_device_id qcom_geni_serial_match_table[] = { #if IS_ENABLED(CONFIG_SERIAL_QCOM_GENI_CONSOLE) { @@ -2138,6 +2150,7 @@ MODULE_DEVICE_TABLE(of, qcom_geni_serial_match_table); static struct platform_driver qcom_geni_serial_platform_driver = { .remove = qcom_geni_serial_remove, .probe = qcom_geni_serial_probe, + .shutdown = qcom_geni_serial_sys_shutdown, .driver = { .name = "qcom_geni_serial", .of_match_table = qcom_geni_serial_match_table, From 5d51a3958ae03e2eda5db9e32b4b224d17faf7d2 Mon Sep 17 00:00:00 2001 From: Praveen Talari Date: Wed, 15 Jul 2026 09:55:15 +0530 Subject: [PATCH 48/51] serial: qcom_geni: Add panic notifier to stop UART on panic When a VM crashes with an active UART DMA transfer in progress, the SMMU raises context faults as the DMA engine continues to access IOVAs that are invalidated when the VM's memory context is torn down. These faults can affect other VMs sharing the same SMMU instance and obscure the root cause of the crash. Additionally, a stuck TX transfer on the panic console UART can cause the panic handler to stall, dropping the panic output. Register a panic notifier to stop TX and RX. The notifier does not take the port lock, since panic can be entered with the lock already held by the interrupted context, and there is no safe way to detect that here; instead, the device's runtime PM status is checked first so that TX/RX are only stopped while the hardware is still clocked and accessible, and the register accesses are skipped entirely once the device is runtime suspended. Signed-off-by: Praveen Talari Link: https://patch.msgid.link/20260715-add_shutdown_and_panic_notifier_serial-v1-2-23e3787c7109@oss.qualcomm.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/qcom_geni_serial.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c index 04e1227390dd..67b14fda4ff9 100644 --- a/drivers/tty/serial/qcom_geni_serial.c +++ b/drivers/tty/serial/qcom_geni_serial.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -156,6 +157,7 @@ struct qcom_geni_serial_port { struct qcom_geni_private_data private_data; const struct qcom_geni_device_data *dev_data; struct dev_pm_domain_list *pd_list; + struct notifier_block panic_nb; }; static const struct uart_ops qcom_geni_console_pops; @@ -1824,6 +1826,22 @@ static const struct uart_ops qcom_geni_uart_pops = { .pm = qcom_geni_serial_pm, }; +static int qcom_geni_serial_panic_notifier(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct qcom_geni_serial_port *port = + container_of(nb, struct qcom_geni_serial_port, panic_nb); + struct uart_port *uport = &port->uport; + + if (pm_runtime_status_suspended(uport->dev)) + return NOTIFY_OK; + + qcom_geni_serial_stop_tx(uport); + qcom_geni_serial_stop_rx(uport); + + return NOTIFY_OK; +} + static int qcom_geni_serial_probe(struct platform_device *pdev) { int ret = 0; @@ -1959,6 +1977,9 @@ static int qcom_geni_serial_probe(struct platform_device *pdev) if (ret) goto error; + port->panic_nb.notifier_call = qcom_geni_serial_panic_notifier; + atomic_notifier_chain_register(&panic_notifier_list, &port->panic_nb); + return 0; error: @@ -1977,6 +1998,8 @@ static void qcom_geni_serial_remove(struct platform_device *pdev) struct uart_port *uport = &port->uport; struct uart_driver *drv = port->private_data.drv; + atomic_notifier_chain_unregister(&panic_notifier_list, &port->panic_nb); + dev_pm_clear_wake_irq(&pdev->dev); device_init_wakeup(&pdev->dev, false); ida_free(&port_ida, uport->line); From a5ef89f7a8cda4c58bbe3dc68a475d446e5422bc Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 14 Jul 2026 13:20:36 -0600 Subject: [PATCH 49/51] dt-bindings: serial: snps-dw-apb-uart: Add RV1106 compatible Add the compatible for the UARTs of the Rockchip RV1106, which are compatible with the Synopsys DesignWare APB UART. Signed-off-by: Simon Glass Reviewed-by: Heiko Stuebner Link: https://patch.msgid.link/20260714132035.v2.1.c1d92213393f49330ec14d0c670a802181b4fcbf@changeid Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/serial/snps-dw-apb-uart.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/serial/snps-dw-apb-uart.yaml b/Documentation/devicetree/bindings/serial/snps-dw-apb-uart.yaml index 49f51b002879..c0d0524458c1 100644 --- a/Documentation/devicetree/bindings/serial/snps-dw-apb-uart.yaml +++ b/Documentation/devicetree/bindings/serial/snps-dw-apb-uart.yaml @@ -72,6 +72,7 @@ properties: - rockchip,rk3576-uart - rockchip,rk3588-uart - rockchip,rv1103b-uart + - rockchip,rv1106-uart - rockchip,rv1108-uart - rockchip,rv1126-uart - sophgo,sg2044-uart From 782f4dbd1794b4f30dc116a7ca42c5962c409be8 Mon Sep 17 00:00:00 2001 From: Karl Mehltretter Date: Fri, 17 Jul 2026 09:16:16 +0200 Subject: [PATCH 50/51] tty: hvc: restrict HVC_DCC to ARMv6+ and ARM64 hvc_dcc drives the JTAG DCC via the ARMv6/v7 CP14 debug registers (mrc/mcr p14, 0, rX, c0, c1/c5, 0 in asm/dcc.h). That encoding is undefined on older ARM cores, and also on ARMv7-M, but HVC_DCC only depends on ARM, so it can be enabled on e.g. ARM926 (ARCH_MULTI_V5), where hvc_dcc_console_init() runs __dcc_putchar() at boot and takes an undefined-instruction trap before the console is up: Internal error: Oops - undefined instruction: 0 [#1] ARM PC is at hvc_dcc_check+0x50/0x8c hvc_dcc_check from hvc_dcc_console_init+0x18/0x48 hvc_dcc_console_init from console_init+0x58/0x170 Kernel panic - not syncing: Fatal exception Restrict HVC_DCC to the CPUs where that encoding is valid: the CPU_V6 || CPU_V6K || CPU_V7 set that arch/arm/include/debug/icedcc.S guards it with, plus ARM64. Fixes: 16c63f8ea49c ("drivers: char: hvc: add arm JTAG DCC console support") Signed-off-by: Karl Mehltretter Reviewed-by: Arnd Bergmann Link: https://patch.msgid.link/20260717071616.91423-1-kmehltretter@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/hvc/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/hvc/Kconfig b/drivers/tty/hvc/Kconfig index c2a4e88b328f..5866195de26a 100644 --- a/drivers/tty/hvc/Kconfig +++ b/drivers/tty/hvc/Kconfig @@ -79,7 +79,7 @@ config HVC_UDBG config HVC_DCC bool "ARM JTAG DCC console" - depends on ARM || ARM64 + depends on (ARM && (CPU_V6 || CPU_V6K || CPU_V7)) || ARM64 select HVC_DRIVER select SERIAL_CORE_CONSOLE help From 57c0741b8c15b93ba4aa92c6618cde6f3f4115b2 Mon Sep 17 00:00:00 2001 From: Fushuai Wang Date: Fri, 24 Jul 2026 17:31:51 +0800 Subject: [PATCH 51/51] Revert "serial: 8250: Clear CON_PRINTBUFFER on port re-registration" This reverts commit d338ab1d90603f875c4f7ed223406535378173a5. uart_console() only indicates that the port is selected as the console. It does not mean that the console has already been registered or has printed the buffered messages. On platforms where an initial 8250 port is replaced when the real UART device is registered, clearing CON_PRINTBUFFER causes the console to start at the end of the printk ring buffer. Without earlycon, all messages logged before UART registration are therefore lost. Fixes: d338ab1d9060 ("serial: 8250: Clear CON_PRINTBUFFER on port re-registration") Reported-by: Mark Brown Reported-by: Anirudh Srinivasan Link: https://lore.kernel.org/all/20260522101042.21976-1-fushuai.wang@linux.dev/ Signed-off-by: Fushuai Wang Reviewed-by: John Ogness Link: https://patch.msgid.link/20260724093151.53216-1-fushuai.wang@linux.dev Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_core.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index c0e8a4efbdcc..f49862d90eeb 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -720,12 +720,8 @@ int serial8250_register_8250_port(const struct uart_8250_port *up) /* Preserve specified console flow control. */ cons_flow = uart_cons_flow_enabled(&uart->port); - if (uart->port.dev) { - if (uart_console(&uart->port)) - uart->port.cons->flags &= ~CON_PRINTBUFFER; - + if (uart->port.dev) uart_remove_one_port(&serial8250_reg, &uart->port); - } uart->port.ctrl_id = up->port.ctrl_id; uart->port.port_id = up->port.port_id;