diff --git a/Documentation/devicetree/bindings/serial/8250.yaml b/Documentation/devicetree/bindings/serial/8250.yaml index e9e07c2356bc..bb7b9c87a807 100644 --- a/Documentation/devicetree/bindings/serial/8250.yaml +++ b/Documentation/devicetree/bindings/serial/8250.yaml @@ -182,6 +182,7 @@ properties: - const: ns16550a - items: - enum: + - loongson,ls3a4000-uart - loongson,ls3a5000-uart - loongson,ls3a6000-uart - loongson,ls2k2000-uart diff --git a/Documentation/devicetree/bindings/serial/amlogic,meson-uart.yaml b/Documentation/devicetree/bindings/serial/amlogic,meson-uart.yaml index d8ad1bb6172d..a2702319685d 100644 --- a/Documentation/devicetree/bindings/serial/amlogic,meson-uart.yaml +++ b/Documentation/devicetree/bindings/serial/amlogic,meson-uart.yaml @@ -56,6 +56,7 @@ properties: items: - enum: - amlogic,a4-uart + - amlogic,a9-uart - amlogic,s6-uart - amlogic,s7-uart - amlogic,s7d-uart diff --git a/Documentation/devicetree/bindings/serial/atmel,at91-usart.yaml b/Documentation/devicetree/bindings/serial/atmel,at91-usart.yaml index 087a8926f8b4..375cd50bc5cc 100644 --- a/Documentation/devicetree/bindings/serial/atmel,at91-usart.yaml +++ b/Documentation/devicetree/bindings/serial/atmel,at91-usart.yaml @@ -24,6 +24,7 @@ properties: - const: atmel,at91sam9260-usart - items: - enum: + - microchip,lan9691-usart - microchip,sam9x60-usart - microchip,sam9x7-usart - microchip,sama7d65-usart diff --git a/Documentation/devicetree/bindings/serial/renesas,rsci.yaml b/Documentation/devicetree/bindings/serial/renesas,rsci.yaml index e059b14775eb..85ebb3056066 100644 --- a/Documentation/devicetree/bindings/serial/renesas,rsci.yaml +++ b/Documentation/devicetree/bindings/serial/renesas,rsci.yaml @@ -14,6 +14,7 @@ properties: compatible: oneOf: - enum: + - renesas,r9a08g046-rsci # RZ/G3L - renesas,r9a09g047-rsci # RZ/G3E - renesas,r9a09g077-rsci # RZ/T2H @@ -145,6 +146,31 @@ allOf: - resets - reset-names + - if: + properties: + compatible: + contains: + const: renesas,r9a08g046-rsci + then: + properties: + interrupts: + minItems: 6 + + interrupt-names: + minItems: 6 + + clocks: + minItems: 2 + maxItems: 3 + + clock-names: + minItems: 2 + maxItems: 3 + + required: + - resets + - reset-names + unevaluatedProperties: false examples: diff --git a/Documentation/devicetree/bindings/serial/snps-dw-apb-uart.yaml b/Documentation/devicetree/bindings/serial/snps-dw-apb-uart.yaml index 6efe43089a74..685c1eceb782 100644 --- a/Documentation/devicetree/bindings/serial/snps-dw-apb-uart.yaml +++ b/Documentation/devicetree/bindings/serial/snps-dw-apb-uart.yaml @@ -71,6 +71,7 @@ properties: - rockchip,rk3568-uart - rockchip,rk3576-uart - rockchip,rk3588-uart + - rockchip,rv1103b-uart - rockchip,rv1108-uart - rockchip,rv1126-uart - sophgo,sg2044-uart diff --git a/drivers/tty/hvc/hvc_iucv.c b/drivers/tty/hvc/hvc_iucv.c index 1dcdb9e99bd8..37db8a3e5158 100644 --- a/drivers/tty/hvc/hvc_iucv.c +++ b/drivers/tty/hvc/hvc_iucv.c @@ -130,7 +130,7 @@ static struct iucv_handler hvc_iucv_handler = { */ static struct hvc_iucv_private *hvc_iucv_get_private(uint32_t num) { - if (num > hvc_iucv_devices) + if (num >= hvc_iucv_devices) return NULL; return hvc_iucv_table[num]; } diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index cb427e93372d..cc7f7091ed9a 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -532,14 +532,16 @@ static void __init legacy_pty_init(void) pty_driver = tty_alloc_driver(legacy_count, TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW | - TTY_DRIVER_DYNAMIC_ALLOC); + TTY_DRIVER_DYNAMIC_ALLOC | + TTY_DRIVER_NO_WORKQUEUE); if (IS_ERR(pty_driver)) panic("Couldn't allocate pty driver"); pty_slave_driver = tty_alloc_driver(legacy_count, TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW | - TTY_DRIVER_DYNAMIC_ALLOC); + TTY_DRIVER_DYNAMIC_ALLOC | + TTY_DRIVER_NO_WORKQUEUE); if (IS_ERR(pty_slave_driver)) panic("Couldn't allocate pty slave driver"); @@ -849,7 +851,8 @@ static void __init unix98_pty_init(void) TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_DEVPTS_MEM | - TTY_DRIVER_DYNAMIC_ALLOC); + TTY_DRIVER_DYNAMIC_ALLOC | + TTY_DRIVER_NO_WORKQUEUE); if (IS_ERR(ptm_driver)) panic("Couldn't allocate Unix98 ptm driver"); pts_driver = tty_alloc_driver(NR_UNIX98_PTY_MAX, @@ -857,7 +860,8 @@ static void __init unix98_pty_init(void) TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_DEVPTS_MEM | - TTY_DRIVER_DYNAMIC_ALLOC); + TTY_DRIVER_DYNAMIC_ALLOC | + TTY_DRIVER_NO_WORKQUEUE); if (IS_ERR(pts_driver)) panic("Couldn't allocate Unix98 pts driver"); diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index d2e2c5dfef99..a428e88938eb 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -524,7 +524,7 @@ console_initcall(univ8250_console_init); struct uart_driver serial8250_reg = { .owner = THIS_MODULE, - .driver_name = "serial", + .driver_name = "serial_8250", .dev_name = "ttyS", .major = TTY_MAJOR, .minor = 64, diff --git a/drivers/tty/serial/8250/8250_fintek.c b/drivers/tty/serial/8250/8250_fintek.c index b4461a89b8d0..976c5748905c 100644 --- a/drivers/tty/serial/8250/8250_fintek.c +++ b/drivers/tty/serial/8250/8250_fintek.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Probe for F81216A LPC to 4 UART + * Probe for F81216A LPC to 4 UART and F81214E LPC/eSPI to 2 UART * * Copyright (C) 2014-2016 Ricardo Ribalda, Qtechnology A/S */ @@ -23,6 +23,7 @@ #define CHIP_ID_F81216AD 0x1602 #define CHIP_ID_F81216E 0x1617 #define CHIP_ID_F81216H 0x0501 +#define CHIP_ID_F81214E 0x1417 #define CHIP_ID_F81216 0x0802 #define VENDOR_ID1 0x23 #define VENDOR_ID1_VAL 0x19 @@ -161,6 +162,7 @@ static int fintek_8250_check_id(struct fintek_8250 *pdata) case CHIP_ID_F81216AD: case CHIP_ID_F81216E: case CHIP_ID_F81216H: + case CHIP_ID_F81214E: case CHIP_ID_F81216: break; default: @@ -185,6 +187,7 @@ static int fintek_8250_get_ldn_range(struct fintek_8250 *pdata, int *min, case CHIP_ID_F81216AD: case CHIP_ID_F81216E: case CHIP_ID_F81216H: + case CHIP_ID_F81214E: case CHIP_ID_F81216: *min = F81216_LDN_LOW; *max = F81216_LDN_HIGH; @@ -255,6 +258,7 @@ static void fintek_8250_set_irq_mode(struct fintek_8250 *pdata, bool is_level) case CHIP_ID_F81216AD: case CHIP_ID_F81216E: case CHIP_ID_F81216H: + case CHIP_ID_F81214E: case CHIP_ID_F81216: sio_write_mask_reg(pdata, FINTEK_IRQ_MODE, IRQ_SHARE, IRQ_SHARE); @@ -269,6 +273,7 @@ static void fintek_8250_set_max_fifo(struct fintek_8250 *pdata) switch (pdata->pid) { case CHIP_ID_F81216E: /* 128Bytes FIFO */ case CHIP_ID_F81216H: + case CHIP_ID_F81214E: case CHIP_ID_F81966: case CHIP_ID_F81866: sio_write_mask_reg(pdata, FIFO_CTRL, @@ -304,6 +309,7 @@ static void fintek_8250_set_termios(struct uart_port *port, switch (pdata->pid) { case CHIP_ID_F81216E: case CHIP_ID_F81216H: + case CHIP_ID_F81214E: reg = RS485; break; case CHIP_ID_F81966: @@ -354,6 +360,7 @@ static void fintek_8250_set_termios_handler(struct uart_8250_port *uart) switch (pdata->pid) { case CHIP_ID_F81216E: case CHIP_ID_F81216H: + case CHIP_ID_F81214E: case CHIP_ID_F81966: case CHIP_ID_F81866: uart->port.set_termios = fintek_8250_set_termios; @@ -446,6 +453,7 @@ static void fintek_8250_set_rs485_handler(struct uart_8250_port *uart) break; case CHIP_ID_F81216E: /* F81216E does not support RS485 delays */ + case CHIP_ID_F81214E: /* F81214E does not support RS485 delays */ uart->port.rs485_config = fintek_8250_rs485_config; uart->port.rs485_supported = fintek_8250_rs485_supported; break; diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index 6cfd1b2af5b7..2fbd8f2603b5 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -100,6 +100,8 @@ #define PCI_DEVICE_ID_ADDIDATA_CPCI7420_NG 0x7025 #define PCI_DEVICE_ID_ADDIDATA_CPCI7300_NG 0x7026 +#define PCI_VENDOR_ID_SYSTEMBASE 0x14a1 + /* Unknown vendors/cards - this should not be in linux/pci_ids.h */ #define PCI_SUBDEVICE_ID_UNKNOWN_0x1584 0x1584 #define PCI_SUBDEVICE_ID_UNKNOWN_0x1588 0x1588 @@ -2133,6 +2135,35 @@ pci_moxa_setup(struct serial_private *priv, return setup_port(priv, port, bar, offset, 0); } +#define SB_OPTR_IMR0 0x0c /* Interrupt mask register, p0 to p7 */ +static int pci_systembase_init(struct pci_dev *dev) +{ + resource_size_t iobase; + + if (!IS_ENABLED(CONFIG_HAS_IOPORT)) + return serial_8250_warn_need_ioport(dev); + + iobase = pci_resource_start(dev, 1); + + /* This will support up to 8 ports */ + outb(0xff, iobase + SB_OPTR_IMR0); + + return 0; +} + +static void pci_systembase_exit(struct pci_dev *dev) +{ + resource_size_t iobase; + + if (!IS_ENABLED(CONFIG_HAS_IOPORT)) { + serial_8250_warn_need_ioport(dev); + return; + } + + iobase = pci_resource_start(dev, 0); + outb(0x00, iobase + SB_OPTR_IMR0); +} + /* * Master list of serial port init/setup/exit quirks. * This does not describe the general nature of the port. @@ -2481,6 +2512,16 @@ static struct pci_serial_quirk pci_serial_quirks[] = { .init = pci_siig_init, .setup = pci_siig_setup, }, + /* Systembase */ + { + .vendor = PCI_VENDOR_ID_SYSTEMBASE, + .device = 0x0008, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_systembase_init, + .setup = pci_default_setup, + .exit = pci_systembase_exit, + }, /* * Titan cards */ @@ -3054,6 +3095,7 @@ enum pci_board_num_t { pbn_b0_1_921600, pbn_b0_2_921600, pbn_b0_4_921600, + pbn_b0_8_921600, pbn_b0_2_1130000, @@ -3254,6 +3296,12 @@ static struct pciserial_board pci_boards[] = { .base_baud = 921600, .uart_offset = 8, }, + [pbn_b0_8_921600] = { + .flags = FL_BASE0, + .num_ports = 8, + .base_baud = 921600, + .uart_offset = 8, + }, [pbn_b0_2_1130000] = { .flags = FL_BASE0, @@ -6169,6 +6217,9 @@ static const struct pci_device_id serial_pci_tbl[] = { PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b0_1_115200 }, + /* Systembase Multi I/O cards */ + { PCI_VDEVICE(SYSTEMBASE, 0x0008), pbn_b0_8_921600 }, + /* Fintek PCI serial cards */ { PCI_DEVICE(0x1c29, 0x1104), .driver_data = pbn_fintek_4 }, { PCI_DEVICE(0x1c29, 0x1108), .driver_data = pbn_fintek_8 }, diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index 328711b5df1a..af78cc02f38e 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -2372,8 +2372,8 @@ void serial8250_do_shutdown(struct uart_port *port) synchronize_irq(port->irq); - if (up->dma) - serial8250_release_dma(up); + serial8250_release_dma(up); + up->dma = NULL; scoped_guard(uart_port_lock_irqsave, port) { if (port->flags & UPF_FOURPORT) { diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig index fd4e8b6ab60d..fc3e58d62233 100644 --- a/drivers/tty/serial/8250/Kconfig +++ b/drivers/tty/serial/8250/Kconfig @@ -465,11 +465,12 @@ config SERIAL_8250_OMAP_TTYO_FIXUP config SERIAL_8250_LOONGSON tristate "Loongson 8250 based serial port" depends on SERIAL_8250 - depends on LOONGARCH || COMPILE_TEST + depends on LOONGARCH || MACH_LOONGSON64 || COMPILE_TEST help - If you have a machine based on LoongArch CPU you can enable - its onboard serial ports by enabling this option. The option - is applicable to both devicetree and ACPI, say Y to this option. + If you have a machine based on LoongArch CPU or MIPS-based Loongson + 3A4000 CPU you can enable its onboard serial ports by enabling this + option. The option is applicable to both devicetree and ACPI, say Y + to enable this option. If unsure, say N. config SERIAL_8250_LPC18XX diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index f86775cfdcc9..9aa61c93d7bc 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -255,14 +255,13 @@ config SERIAL_SAMSUNG_CONSOLE config SERIAL_TEGRA tristate "NVIDIA Tegra20/30 SoC serial controller" - depends on (ARCH_TEGRA && TEGRA20_APB_DMA) || COMPILE_TEST + depends on ARCH_TEGRA || COMPILE_TEST select SERIAL_CORE help Support for the on-chip UARTs on the NVIDIA Tegra series SOCs providing /dev/ttyTHS0, 1, 2, 3 and 4 (note, some machines may not provide all of these ports, depending on how the serial port - are enabled). This driver uses the APB DMA to achieve higher baudrate - and better performance. + are enabled). config SERIAL_TEGRA_TCU tristate "NVIDIA Tegra Combined UART" @@ -803,7 +802,7 @@ config SERIAL_CPM_CONSOLE config SERIAL_PIC32 tristate "Microchip PIC32 serial support" - depends on MACH_PIC32 || (MIPS && COMPILE_TEST) + depends on MACH_PIC32 || COMPILE_TEST select SERIAL_CORE help If you have a PIC32, this driver supports the serial ports. @@ -1593,32 +1592,6 @@ config SERIAL_NUVOTON_MA35D1_CONSOLE but you can alter that using a kernel command line option such as "console=ttyNVTx". -config SERIAL_ESP32 - tristate "Espressif ESP32 UART support" - depends on XTENSA_PLATFORM_ESP32 || (COMPILE_TEST && OF) - select SERIAL_CORE - select SERIAL_CORE_CONSOLE - select SERIAL_EARLYCON - help - Driver for the UART controllers of the Espressif ESP32xx SoCs. - When earlycon option is enabled the following kernel command line - snippets may be used: - earlycon=esp32s3uart,mmio32,0x60000000,115200n8,40000000 - earlycon=esp32uart,mmio32,0x3ff40000,115200n8 - -config SERIAL_ESP32_ACM - tristate "Espressif ESP32 USB ACM gadget support" - depends on XTENSA_PLATFORM_ESP32 || (COMPILE_TEST && OF) - select SERIAL_CORE - select SERIAL_CORE_CONSOLE - select SERIAL_EARLYCON - help - Driver for the CDC ACM gadget controller of the Espressif ESP32S3 - SoCs that share separate USB controller with the JTAG adapter. - When earlycon option is enabled the following kernel command line - snippet may be used: - earlycon=esp32s3acm,mmio32,0x60038000 - endmenu config SERIAL_MCTRL_GPIO diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index a2ccbc508ec5..bba7b21a4a1d 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -37,8 +37,6 @@ obj-$(CONFIG_SERIAL_CLPS711X) += clps711x.o obj-$(CONFIG_SERIAL_CPM) += cpm_uart.o obj-$(CONFIG_SERIAL_CONEXANT_DIGICOLOR) += digicolor-usart.o obj-$(CONFIG_SERIAL_DZ) += dz.o -obj-$(CONFIG_SERIAL_ESP32) += esp32_uart.o -obj-$(CONFIG_SERIAL_ESP32_ACM) += esp32_acm.o obj-$(CONFIG_SERIAL_FSL_LINFLEXUART) += fsl_linflexuart.o obj-$(CONFIG_SERIAL_FSL_LPUART) += fsl_lpuart.o obj-$(CONFIG_SERIAL_ICOM) += icom.o diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 7f17d288c807..028e37ad8d79 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -114,6 +114,8 @@ struct vendor_data { bool cts_event_workaround; bool always_enabled; bool fixed_options; + bool skip_ibrd_fbrd; + bool set_uartclk_rate; unsigned int (*get_fifosize)(struct amba_device *dev); }; @@ -216,6 +218,28 @@ static struct vendor_data vendor_st = { .get_fifosize = get_fifosize_st, }; +static unsigned int get_fifosize_nvidia(struct amba_device *dev) +{ + return 32; +} + +static struct vendor_data vendor_nvidia = { + .reg_offset = pl011_std_offsets, + .ifls = UART011_IFLS_RX4_8 | UART011_IFLS_TX4_8, + .fr_busy = UART01x_FR_BUSY, + .fr_dsr = UART01x_FR_DSR, + .fr_cts = UART01x_FR_CTS, + .fr_ri = UART011_FR_RI, + .oversampling = false, + .dma_threshold = false, + .cts_event_workaround = false, + .always_enabled = false, + .fixed_options = false, + .skip_ibrd_fbrd = true, + .set_uartclk_rate = true, + .get_fifosize = get_fifosize_nvidia, +}; + /* Deals with DMA transactions */ struct pl011_dmabuf { @@ -625,6 +649,15 @@ static int pl011_dma_tx_refill(struct uart_amba_port *uap) count = PL011_DMA_BUFFER_SIZE; count = kfifo_out_peek(&tport->xmit_fifo, dmatx->buf, count); + + /* + * Align the TX buffer length to the DMA controller's copy_align + * requirements. Some DMA controllers (e.g., Tegra GPC DMA) require + * word-aligned transfers. Unaligned bytes will be sent via PIO. + */ + if (chan->device->copy_align) + count = ALIGN_DOWN(count, 1 << chan->device->copy_align); + dmatx->len = count; dmatx->dma = dma_map_single(dma_dev->dev, dmatx->buf, count, DMA_TO_DEVICE); @@ -2095,6 +2128,7 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios, unsigned int lcr_h, old_cr; unsigned long flags; unsigned int baud, quot, clkdiv; + unsigned int max_baud; unsigned int bits; if (uap->vendor->oversampling) @@ -2102,11 +2136,34 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios, else clkdiv = 16; + max_baud = port->uartclk / clkdiv; + + if (uap->vendor->set_uartclk_rate) { + long max_clkrate = clk_round_rate(uap->clk, UINT_MAX); + + /* + * Clock is reprogrammable - determine max baud from the clock's + * maximum rate, not the current uartclk. + */ + if (max_clkrate > 0) + max_baud = max_clkrate / clkdiv; + } + /* * Ask the core to calculate the divisor for us. */ - baud = uart_get_baud_rate(port, termios, old, 0, - port->uartclk / clkdiv); + baud = uart_get_baud_rate(port, termios, old, 0, max_baud); + + if (uap->vendor->set_uartclk_rate) { + int err; + + err = clk_set_rate(uap->clk, baud * clkdiv); + if (err) { + dev_err(port->dev, "Failed to set clock rate: %d\n", err); + return; + } + } + #ifdef CONFIG_DMA_ENGINE /* * Adjust RX DMA polling rate with baud rate if not specified. @@ -2115,11 +2172,6 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios, uap->dmarx.poll_rate = DIV_ROUND_UP(10000000, baud); #endif - if (baud > port->uartclk / 16) - quot = DIV_ROUND_CLOSEST(port->uartclk * 8, baud); - else - quot = DIV_ROUND_CLOSEST(port->uartclk * 4, baud); - switch (termios->c_cflag & CSIZE) { case CS5: lcr_h = UART01x_LCRH_WLEN_5; @@ -2190,21 +2242,28 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios, old_cr &= ~ST_UART011_CR_OVSFACT; } - /* - * Workaround for the ST Micro oversampling variants to - * increase the bitrate slightly, by lowering the divisor, - * to avoid delayed sampling of start bit at high speeds, - * else we see data corruption. - */ - if (uap->vendor->oversampling) { - if (baud >= 3000000 && baud < 3250000 && quot > 1) - quot -= 1; - else if (baud > 3250000 && quot > 2) - quot -= 2; + if (!uap->vendor->skip_ibrd_fbrd) { + if (baud > port->uartclk / 16) + quot = DIV_ROUND_CLOSEST(port->uartclk * 8, baud); + else + quot = DIV_ROUND_CLOSEST(port->uartclk * 4, baud); + + /* + * Workaround for the ST Micro oversampling variants to + * increase the bitrate slightly, by lowering the divisor, + * to avoid delayed sampling of start bit at high speeds, + * else we see data corruption. + */ + if (uap->vendor->oversampling) { + if (baud >= 3000000 && baud < 3250000 && quot > 1) + quot -= 1; + else if (baud > 3250000 && quot > 2) + quot -= 2; + } + /* Set baud rate */ + pl011_write(quot & 0x3f, uap, REG_FBRD); + pl011_write(quot >> 6, uap, REG_IBRD); } - /* Set baud rate */ - pl011_write(quot & 0x3f, uap, REG_FBRD); - pl011_write(quot >> 6, uap, REG_IBRD); /* * ----------v----------v----------v----------v----- @@ -2374,6 +2433,7 @@ static void pl011_console_get_options(struct uart_amba_port *uap, int *baud, int *parity, int *bits) { unsigned int lcr_h, ibrd, fbrd; + unsigned int clkdiv; if (!(pl011_read(uap, REG_CR) & UART01x_CR_UARTEN)) return; @@ -2393,10 +2453,15 @@ static void pl011_console_get_options(struct uart_amba_port *uap, int *baud, else *bits = 8; - ibrd = pl011_read(uap, REG_IBRD); - fbrd = pl011_read(uap, REG_FBRD); + if (uap->vendor->skip_ibrd_fbrd) { + clkdiv = 64; + } else { + ibrd = pl011_read(uap, REG_IBRD); + fbrd = pl011_read(uap, REG_FBRD); + clkdiv = 64 * ibrd + fbrd; + } - *baud = uap->port.uartclk * 4 / (64 * ibrd + fbrd); + *baud = uap->port.uartclk * 4 / clkdiv; if (uap->vendor->oversampling && (pl011_read(uap, REG_CR) & ST_UART011_CR_OVSFACT)) @@ -2700,6 +2765,37 @@ static int pl011_early_read(struct console *con, char *s, unsigned int n) */ static int __init pl011_early_console_setup(struct earlycon_device *device, const char *opt) +{ + unsigned int cr; + + if (!device->port.membase) + return -ENODEV; + + device->con->write = pl011_early_write; + device->con->read = pl011_early_read; + + if (device->port.iotype == UPIO_MEM32) + cr = readl(device->port.membase + UART011_CR); + else + cr = readw(device->port.membase + UART011_CR); + cr &= UART011_CR_RTS | UART011_CR_DTR; + cr |= UART01x_CR_UARTEN | UART011_CR_RXE | UART011_CR_TXE; + if (device->port.iotype == UPIO_MEM32) + writel(cr, device->port.membase + UART011_CR); + else + writew(cr, device->port.membase + UART011_CR); + + return 0; +} + +OF_EARLYCON_DECLARE(pl011, "arm,pl011", pl011_early_console_setup); + +/* + * The SBSA UART has no defined control register and is assumed to + * be pre-enabled by firmware, so we do not write to UART011_CR. + */ +static int __init sbsa_uart_early_console_setup(struct earlycon_device *device, + const char *opt) { if (!device->port.membase) return -ENODEV; @@ -2710,9 +2806,7 @@ static int __init pl011_early_console_setup(struct earlycon_device *device, return 0; } -OF_EARLYCON_DECLARE(pl011, "arm,pl011", pl011_early_console_setup); - -OF_EARLYCON_DECLARE(pl011, "arm,sbsa-uart", pl011_early_console_setup); +OF_EARLYCON_DECLARE(pl011, "arm,sbsa-uart", sbsa_uart_early_console_setup); /* * On Qualcomm Datacenter Technologies QDF2400 SOCs affected by @@ -3081,6 +3175,11 @@ static const struct amba_id pl011_ids[] = { .mask = 0x00ffffff, .data = &vendor_st, }, + { + .id = 0x0006b011, + .mask = 0x000fffff, + .data = &vendor_nvidia, + }, { 0, 0 }, }; diff --git a/drivers/tty/serial/apbuart.c b/drivers/tty/serial/apbuart.c index 364599f256db..3e46341cfff8 100644 --- a/drivers/tty/serial/apbuart.c +++ b/drivers/tty/serial/apbuart.c @@ -505,7 +505,7 @@ console_initcall(apbuart_console_init); static struct uart_driver grlib_apbuart_driver = { .owner = THIS_MODULE, - .driver_name = "serial", + .driver_name = "serial_apbuart", .dev_name = "ttyS", .major = SERIAL_APBUART_MAJOR, .minor = SERIAL_APBUART_MINOR, diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index 08dd8f887956..5d8c1cfc1c60 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -1927,7 +1927,7 @@ static int atmel_startup(struct uart_port *port) atmel_uart_writel(port, ATMEL_US_FMR, fmr); } - /* Save current CSR for comparison in atmel_tasklet_func() */ + /* Save current CSR for comparison in atmel_handle_status() */ atmel_port->irq_status_prev = atmel_uart_readl(port, ATMEL_US_CSR); /* diff --git a/drivers/tty/serial/dz.c b/drivers/tty/serial/dz.c index eba91daedef8..e53c54353c3e 100644 --- a/drivers/tty/serial/dz.c +++ b/drivers/tty/serial/dz.c @@ -914,7 +914,7 @@ console_initcall(dz_serial_console_init); static struct uart_driver dz_reg = { .owner = THIS_MODULE, - .driver_name = "serial", + .driver_name = "serial_dz", .dev_name = "ttyS", .major = TTY_MAJOR, .minor = 64, diff --git a/drivers/tty/serial/esp32_acm.c b/drivers/tty/serial/esp32_acm.c deleted file mode 100644 index bb7cc65427f0..000000000000 --- a/drivers/tty/serial/esp32_acm.c +++ /dev/null @@ -1,459 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define DRIVER_NAME "esp32s3-acm" -#define DEV_NAME "ttyGS" -#define UART_NR 4 - -#define ESP32S3_ACM_TX_FIFO_SIZE 64 - -#define USB_SERIAL_JTAG_EP1_REG 0x00 -#define USB_SERIAL_JTAG_EP1_CONF_REG 0x04 -#define USB_SERIAL_JTAG_WR_DONE BIT(0) -#define USB_SERIAL_JTAG_SERIAL_IN_EP_DATA_FREE BIT(1) -#define USB_SERIAL_JTAG_INT_ST_REG 0x0c -#define USB_SERIAL_JTAG_SERIAL_OUT_RECV_PKT_INT_ST BIT(2) -#define USB_SERIAL_JTAG_SERIAL_IN_EMPTY_INT_ST BIT(3) -#define USB_SERIAL_JTAG_INT_ENA_REG 0x10 -#define USB_SERIAL_JTAG_SERIAL_OUT_RECV_PKT_INT_ENA BIT(2) -#define USB_SERIAL_JTAG_SERIAL_IN_EMPTY_INT_ENA BIT(3) -#define USB_SERIAL_JTAG_INT_CLR_REG 0x14 -#define USB_SERIAL_JTAG_IN_EP1_ST_REG 0x2c -#define USB_SERIAL_JTAG_IN_EP1_WR_ADDR GENMASK(8, 2) -#define USB_SERIAL_JTAG_OUT_EP1_ST_REG 0x3c -#define USB_SERIAL_JTAG_OUT_EP1_REC_DATA_CNT GENMASK(22, 16) - -static const struct of_device_id esp32s3_acm_dt_ids[] = { - { - .compatible = "esp,esp32s3-acm", - }, { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(of, esp32s3_acm_dt_ids); - -static struct uart_port *esp32s3_acm_ports[UART_NR]; - -static void esp32s3_acm_write(struct uart_port *port, unsigned long reg, u32 v) -{ - writel(v, port->membase + reg); -} - -static u32 esp32s3_acm_read(struct uart_port *port, unsigned long reg) -{ - return readl(port->membase + reg); -} - -static u32 esp32s3_acm_tx_fifo_free(struct uart_port *port) -{ - u32 status = esp32s3_acm_read(port, USB_SERIAL_JTAG_EP1_CONF_REG); - - return status & USB_SERIAL_JTAG_SERIAL_IN_EP_DATA_FREE; -} - -static u32 esp32s3_acm_tx_fifo_cnt(struct uart_port *port) -{ - u32 status = esp32s3_acm_read(port, USB_SERIAL_JTAG_IN_EP1_ST_REG); - - return FIELD_GET(USB_SERIAL_JTAG_IN_EP1_WR_ADDR, status); -} - -static u32 esp32s3_acm_rx_fifo_cnt(struct uart_port *port) -{ - u32 status = esp32s3_acm_read(port, USB_SERIAL_JTAG_OUT_EP1_ST_REG); - - return FIELD_GET(USB_SERIAL_JTAG_OUT_EP1_REC_DATA_CNT, status); -} - -/* return TIOCSER_TEMT when transmitter is not busy */ -static unsigned int esp32s3_acm_tx_empty(struct uart_port *port) -{ - return esp32s3_acm_tx_fifo_cnt(port) == 0 ? TIOCSER_TEMT : 0; -} - -static void esp32s3_acm_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ -} - -static unsigned int esp32s3_acm_get_mctrl(struct uart_port *port) -{ - return TIOCM_CAR; -} - -static void esp32s3_acm_stop_tx(struct uart_port *port) -{ - u32 int_ena; - - int_ena = esp32s3_acm_read(port, USB_SERIAL_JTAG_INT_ENA_REG); - int_ena &= ~USB_SERIAL_JTAG_SERIAL_IN_EMPTY_INT_ENA; - esp32s3_acm_write(port, USB_SERIAL_JTAG_INT_ENA_REG, int_ena); -} - -static void esp32s3_acm_rxint(struct uart_port *port) -{ - struct tty_port *tty_port = &port->state->port; - u32 rx_fifo_cnt = esp32s3_acm_rx_fifo_cnt(port); - unsigned long flags; - u32 i; - - if (!rx_fifo_cnt) - return; - - spin_lock_irqsave(&port->lock, flags); - - for (i = 0; i < rx_fifo_cnt; ++i) { - u32 rx = esp32s3_acm_read(port, USB_SERIAL_JTAG_EP1_REG); - - ++port->icount.rx; - tty_insert_flip_char(tty_port, rx, TTY_NORMAL); - } - spin_unlock_irqrestore(&port->lock, flags); - - tty_flip_buffer_push(tty_port); -} - -static void esp32s3_acm_push(struct uart_port *port) -{ - if (esp32s3_acm_tx_fifo_free(port)) - esp32s3_acm_write(port, USB_SERIAL_JTAG_EP1_CONF_REG, - USB_SERIAL_JTAG_WR_DONE); -} - -static void esp32s3_acm_put_char(struct uart_port *port, u8 c) -{ - esp32s3_acm_write(port, USB_SERIAL_JTAG_EP1_REG, c); -} - -static void esp32s3_acm_put_char_sync(struct uart_port *port, u8 c) -{ - unsigned long timeout = jiffies + HZ; - - while (!esp32s3_acm_tx_fifo_free(port)) { - if (time_after(jiffies, timeout)) { - dev_warn(port->dev, "timeout waiting for TX FIFO\n"); - return; - } - cpu_relax(); - } - esp32s3_acm_put_char(port, c); - esp32s3_acm_push(port); -} - -static void esp32s3_acm_transmit_buffer(struct uart_port *port) -{ - u32 tx_fifo_used; - unsigned int pending; - u8 ch; - - if (!esp32s3_acm_tx_fifo_free(port)) - return; - - tx_fifo_used = esp32s3_acm_tx_fifo_cnt(port); - pending = uart_port_tx_limited(port, ch, - ESP32S3_ACM_TX_FIFO_SIZE - tx_fifo_used, - true, esp32s3_acm_put_char(port, ch), - ({})); - if (pending) { - u32 int_ena; - - int_ena = esp32s3_acm_read(port, USB_SERIAL_JTAG_INT_ENA_REG); - int_ena |= USB_SERIAL_JTAG_SERIAL_IN_EMPTY_INT_ENA; - esp32s3_acm_write(port, USB_SERIAL_JTAG_INT_ENA_REG, int_ena); - } - esp32s3_acm_push(port); -} - -static void esp32s3_acm_txint(struct uart_port *port) -{ - esp32s3_acm_transmit_buffer(port); -} - -static irqreturn_t esp32s3_acm_int(int irq, void *dev_id) -{ - struct uart_port *port = dev_id; - u32 status; - - status = esp32s3_acm_read(port, USB_SERIAL_JTAG_INT_ST_REG); - esp32s3_acm_write(port, USB_SERIAL_JTAG_INT_CLR_REG, status); - - if (status & USB_SERIAL_JTAG_SERIAL_OUT_RECV_PKT_INT_ST) - esp32s3_acm_rxint(port); - if (status & USB_SERIAL_JTAG_SERIAL_IN_EMPTY_INT_ST) - esp32s3_acm_txint(port); - - return IRQ_RETVAL(status); -} - -static void esp32s3_acm_start_tx(struct uart_port *port) -{ - esp32s3_acm_transmit_buffer(port); -} - -static void esp32s3_acm_stop_rx(struct uart_port *port) -{ - u32 int_ena; - - int_ena = esp32s3_acm_read(port, USB_SERIAL_JTAG_INT_ENA_REG); - int_ena &= ~USB_SERIAL_JTAG_SERIAL_OUT_RECV_PKT_INT_ENA; - esp32s3_acm_write(port, USB_SERIAL_JTAG_INT_ENA_REG, int_ena); -} - -static int esp32s3_acm_startup(struct uart_port *port) -{ - int ret; - - ret = request_irq(port->irq, esp32s3_acm_int, 0, DRIVER_NAME, port); - if (ret) - return ret; - esp32s3_acm_write(port, USB_SERIAL_JTAG_INT_ENA_REG, - USB_SERIAL_JTAG_SERIAL_OUT_RECV_PKT_INT_ENA); - - return 0; -} - -static void esp32s3_acm_shutdown(struct uart_port *port) -{ - esp32s3_acm_write(port, USB_SERIAL_JTAG_INT_ENA_REG, 0); - free_irq(port->irq, port); -} - -static void esp32s3_acm_set_termios(struct uart_port *port, - struct ktermios *termios, - const struct ktermios *old) -{ -} - -static const char *esp32s3_acm_type(struct uart_port *port) -{ - return "ESP32S3 ACM"; -} - -/* configure/auto-configure the port */ -static void esp32s3_acm_config_port(struct uart_port *port, int flags) -{ - if (flags & UART_CONFIG_TYPE) - port->type = PORT_GENERIC; -} - -#ifdef CONFIG_CONSOLE_POLL -static void esp32s3_acm_poll_put_char(struct uart_port *port, unsigned char c) -{ - esp32s3_acm_put_char_sync(port, c); -} - -static int esp32s3_acm_poll_get_char(struct uart_port *port) -{ - if (esp32s3_acm_rx_fifo_cnt(port)) - return esp32s3_acm_read(port, USB_SERIAL_JTAG_EP1_REG); - else - return NO_POLL_CHAR; -} -#endif - -static const struct uart_ops esp32s3_acm_pops = { - .tx_empty = esp32s3_acm_tx_empty, - .set_mctrl = esp32s3_acm_set_mctrl, - .get_mctrl = esp32s3_acm_get_mctrl, - .stop_tx = esp32s3_acm_stop_tx, - .start_tx = esp32s3_acm_start_tx, - .stop_rx = esp32s3_acm_stop_rx, - .startup = esp32s3_acm_startup, - .shutdown = esp32s3_acm_shutdown, - .set_termios = esp32s3_acm_set_termios, - .type = esp32s3_acm_type, - .config_port = esp32s3_acm_config_port, -#ifdef CONFIG_CONSOLE_POLL - .poll_put_char = esp32s3_acm_poll_put_char, - .poll_get_char = esp32s3_acm_poll_get_char, -#endif -}; - -static void esp32s3_acm_string_write(struct uart_port *port, const char *s, - unsigned int count) -{ - uart_console_write(port, s, count, esp32s3_acm_put_char_sync); -} - -static void -esp32s3_acm_console_write(struct console *co, const char *s, unsigned int count) -{ - struct uart_port *port = esp32s3_acm_ports[co->index]; - unsigned long flags; - bool locked = true; - - if (port->sysrq) - locked = false; - else if (oops_in_progress) - locked = spin_trylock_irqsave(&port->lock, flags); - else - spin_lock_irqsave(&port->lock, flags); - - esp32s3_acm_string_write(port, s, count); - - if (locked) - spin_unlock_irqrestore(&port->lock, flags); -} - -static struct uart_driver esp32s3_acm_reg; -static struct console esp32s3_acm_console = { - .name = DEV_NAME, - .write = esp32s3_acm_console_write, - .device = uart_console_device, - .flags = CON_PRINTBUFFER, - .index = -1, - .data = &esp32s3_acm_reg, -}; - -static void esp32s3_acm_earlycon_write(struct console *con, const char *s, - unsigned int n) -{ - struct earlycon_device *dev = con->data; - - uart_console_write(&dev->port, s, n, esp32s3_acm_put_char_sync); -} - -#ifdef CONFIG_CONSOLE_POLL -static int esp32s3_acm_earlycon_read(struct console *con, char *s, unsigned int n) -{ - struct earlycon_device *dev = con->data; - unsigned int num_read = 0; - - while (num_read < n) { - int c = esp32s3_acm_poll_get_char(&dev->port); - - if (c == NO_POLL_CHAR) - break; - s[num_read++] = c; - } - return num_read; -} -#endif - -static int __init esp32s3_acm_early_console_setup(struct earlycon_device *device, - const char *options) -{ - if (!device->port.membase) - return -ENODEV; - - device->con->write = esp32s3_acm_earlycon_write; -#ifdef CONFIG_CONSOLE_POLL - device->con->read = esp32s3_acm_earlycon_read; -#endif - return 0; -} - -OF_EARLYCON_DECLARE(esp32s3acm, "esp,esp32s3-acm", - esp32s3_acm_early_console_setup); - -static struct uart_driver esp32s3_acm_reg = { - .owner = THIS_MODULE, - .driver_name = DRIVER_NAME, - .dev_name = DEV_NAME, - .nr = ARRAY_SIZE(esp32s3_acm_ports), - .cons = &esp32s3_acm_console, -}; - -static int esp32s3_acm_probe(struct platform_device *pdev) -{ - struct device_node *np = pdev->dev.of_node; - struct uart_port *port; - struct resource *res; - int ret; - - port = devm_kzalloc(&pdev->dev, sizeof(*port), GFP_KERNEL); - if (!port) - return -ENOMEM; - - ret = of_alias_get_id(np, "serial"); - if (ret < 0) { - dev_err(&pdev->dev, "failed to get alias id, errno %d\n", ret); - return ret; - } - if (ret >= UART_NR) { - dev_err(&pdev->dev, "driver limited to %d serial ports\n", - UART_NR); - return -ENOMEM; - } - - port->line = ret; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENODEV; - - port->mapbase = res->start; - port->membase = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(port->membase)) - return PTR_ERR(port->membase); - - port->dev = &pdev->dev; - port->type = PORT_GENERIC; - port->iotype = UPIO_MEM; - port->irq = platform_get_irq(pdev, 0); - port->ops = &esp32s3_acm_pops; - port->flags = UPF_BOOT_AUTOCONF; - port->has_sysrq = 1; - port->fifosize = ESP32S3_ACM_TX_FIFO_SIZE; - - esp32s3_acm_ports[port->line] = port; - - platform_set_drvdata(pdev, port); - - return uart_add_one_port(&esp32s3_acm_reg, port); -} - -static void esp32s3_acm_remove(struct platform_device *pdev) -{ - struct uart_port *port = platform_get_drvdata(pdev); - - uart_remove_one_port(&esp32s3_acm_reg, port); -} - - -static struct platform_driver esp32s3_acm_driver = { - .probe = esp32s3_acm_probe, - .remove = esp32s3_acm_remove, - .driver = { - .name = DRIVER_NAME, - .of_match_table = esp32s3_acm_dt_ids, - }, -}; - -static int __init esp32s3_acm_init(void) -{ - int ret; - - ret = uart_register_driver(&esp32s3_acm_reg); - if (ret) - return ret; - - ret = platform_driver_register(&esp32s3_acm_driver); - if (ret) - uart_unregister_driver(&esp32s3_acm_reg); - - return ret; -} - -static void __exit esp32s3_acm_exit(void) -{ - platform_driver_unregister(&esp32s3_acm_driver); - uart_unregister_driver(&esp32s3_acm_reg); -} - -module_init(esp32s3_acm_init); -module_exit(esp32s3_acm_exit); - -MODULE_AUTHOR("Max Filippov "); -MODULE_DESCRIPTION("Espressif ESP32 USB ACM gadget support"); -MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/esp32_uart.c b/drivers/tty/serial/esp32_uart.c deleted file mode 100644 index 667c2198a03a..000000000000 --- a/drivers/tty/serial/esp32_uart.c +++ /dev/null @@ -1,779 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define DRIVER_NAME "esp32-uart" -#define DEV_NAME "ttyS" -#define UART_NR 3 - -#define ESP32_UART_TX_FIFO_SIZE 127 -#define ESP32_UART_RX_FIFO_SIZE 127 - -#define UART_FIFO_REG 0x00 -#define UART_INT_RAW_REG 0x04 -#define UART_INT_ST_REG 0x08 -#define UART_INT_ENA_REG 0x0c -#define UART_INT_CLR_REG 0x10 -#define UART_RXFIFO_FULL_INT BIT(0) -#define UART_TXFIFO_EMPTY_INT BIT(1) -#define UART_BRK_DET_INT BIT(7) -#define UART_CLKDIV_REG 0x14 -#define ESP32_UART_CLKDIV GENMASK(19, 0) -#define ESP32S3_UART_CLKDIV GENMASK(11, 0) -#define UART_CLKDIV_SHIFT 0 -#define UART_CLKDIV_FRAG GENMASK(23, 20) -#define UART_STATUS_REG 0x1c -#define ESP32_UART_RXFIFO_CNT GENMASK(7, 0) -#define ESP32S3_UART_RXFIFO_CNT GENMASK(9, 0) -#define UART_RXFIFO_CNT_SHIFT 0 -#define UART_DSRN BIT(13) -#define UART_CTSN BIT(14) -#define ESP32_UART_TXFIFO_CNT GENMASK(23, 16) -#define ESP32S3_UART_TXFIFO_CNT GENMASK(25, 16) -#define UART_TXFIFO_CNT_SHIFT 16 -#define UART_CONF0_REG 0x20 -#define UART_PARITY BIT(0) -#define UART_PARITY_EN BIT(1) -#define UART_BIT_NUM GENMASK(3, 2) -#define UART_BIT_NUM_5 0 -#define UART_BIT_NUM_6 1 -#define UART_BIT_NUM_7 2 -#define UART_BIT_NUM_8 3 -#define UART_STOP_BIT_NUM GENMASK(5, 4) -#define UART_STOP_BIT_NUM_1 1 -#define UART_STOP_BIT_NUM_2 3 -#define UART_SW_RTS BIT(6) -#define UART_SW_DTR BIT(7) -#define UART_LOOPBACK BIT(14) -#define UART_TX_FLOW_EN BIT(15) -#define UART_RTS_INV BIT(23) -#define UART_DTR_INV BIT(24) -#define UART_CONF1_REG 0x24 -#define UART_RXFIFO_FULL_THRHD_SHIFT 0 -#define ESP32_UART_TXFIFO_EMPTY_THRHD_SHIFT 8 -#define ESP32S3_UART_TXFIFO_EMPTY_THRHD_SHIFT 10 -#define ESP32_UART_RX_FLOW_EN BIT(23) -#define ESP32S3_UART_RX_FLOW_EN BIT(22) -#define ESP32S3_UART_CLK_CONF_REG 0x78 -#define ESP32S3_UART_SCLK_DIV_B GENMASK(5, 0) -#define ESP32S3_UART_SCLK_DIV_A GENMASK(11, 6) -#define ESP32S3_UART_SCLK_DIV_NUM GENMASK(19, 12) -#define ESP32S3_UART_SCLK_SEL GENMASK(21, 20) -#define APB_CLK 1 -#define RC_FAST_CLK 2 -#define XTAL_CLK 3 -#define ESP32S3_UART_SCLK_EN BIT(22) -#define ESP32S3_UART_RST_CORE BIT(23) -#define ESP32S3_UART_TX_SCLK_EN BIT(24) -#define ESP32S3_UART_RX_SCLK_EN BIT(25) -#define ESP32S3_UART_TX_RST_CORE BIT(26) -#define ESP32S3_UART_RX_RST_CORE BIT(27) - -#define ESP32S3_UART_CLK_CONF_DEFAULT \ - (ESP32S3_UART_RX_SCLK_EN | \ - ESP32S3_UART_TX_SCLK_EN | \ - ESP32S3_UART_SCLK_EN | \ - FIELD_PREP(ESP32S3_UART_SCLK_SEL, XTAL_CLK)) - -struct esp32_port { - struct uart_port port; - struct clk *clk; -}; - -struct esp32_uart_variant { - u32 clkdiv_mask; - u32 rxfifo_cnt_mask; - u32 txfifo_cnt_mask; - u32 txfifo_empty_thrhd_shift; - u32 rx_flow_en; - const char *type; - bool has_clkconf; -}; - -static const struct esp32_uart_variant esp32_variant = { - .clkdiv_mask = ESP32_UART_CLKDIV, - .rxfifo_cnt_mask = ESP32_UART_RXFIFO_CNT, - .txfifo_cnt_mask = ESP32_UART_TXFIFO_CNT, - .txfifo_empty_thrhd_shift = ESP32_UART_TXFIFO_EMPTY_THRHD_SHIFT, - .rx_flow_en = ESP32_UART_RX_FLOW_EN, - .type = "ESP32 UART", -}; - -static const struct esp32_uart_variant esp32s3_variant = { - .clkdiv_mask = ESP32S3_UART_CLKDIV, - .rxfifo_cnt_mask = ESP32S3_UART_RXFIFO_CNT, - .txfifo_cnt_mask = ESP32S3_UART_TXFIFO_CNT, - .txfifo_empty_thrhd_shift = ESP32S3_UART_TXFIFO_EMPTY_THRHD_SHIFT, - .rx_flow_en = ESP32S3_UART_RX_FLOW_EN, - .type = "ESP32S3 UART", - .has_clkconf = true, -}; - -static const struct of_device_id esp32_uart_dt_ids[] = { - { - .compatible = "esp,esp32-uart", - .data = &esp32_variant, - }, { - .compatible = "esp,esp32s3-uart", - .data = &esp32s3_variant, - }, { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(of, esp32_uart_dt_ids); - -static struct esp32_port *esp32_uart_ports[UART_NR]; - -static const struct esp32_uart_variant *port_variant(struct uart_port *port) -{ - return port->private_data; -} - -static void esp32_uart_write(struct uart_port *port, unsigned long reg, u32 v) -{ - writel(v, port->membase + reg); -} - -static u32 esp32_uart_read(struct uart_port *port, unsigned long reg) -{ - return readl(port->membase + reg); -} - -static u32 esp32_uart_tx_fifo_cnt(struct uart_port *port) -{ - u32 status = esp32_uart_read(port, UART_STATUS_REG); - - return (status & port_variant(port)->txfifo_cnt_mask) >> UART_TXFIFO_CNT_SHIFT; -} - -static u32 esp32_uart_rx_fifo_cnt(struct uart_port *port) -{ - u32 status = esp32_uart_read(port, UART_STATUS_REG); - - return (status & port_variant(port)->rxfifo_cnt_mask) >> UART_RXFIFO_CNT_SHIFT; -} - -/* return TIOCSER_TEMT when transmitter is not busy */ -static unsigned int esp32_uart_tx_empty(struct uart_port *port) -{ - return esp32_uart_tx_fifo_cnt(port) ? 0 : TIOCSER_TEMT; -} - -static void esp32_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ - u32 conf0 = esp32_uart_read(port, UART_CONF0_REG); - - conf0 &= ~(UART_LOOPBACK | - UART_SW_RTS | UART_RTS_INV | - UART_SW_DTR | UART_DTR_INV); - - if (mctrl & TIOCM_RTS) - conf0 |= UART_SW_RTS; - if (mctrl & TIOCM_DTR) - conf0 |= UART_SW_DTR; - if (mctrl & TIOCM_LOOP) - conf0 |= UART_LOOPBACK; - - esp32_uart_write(port, UART_CONF0_REG, conf0); -} - -static unsigned int esp32_uart_get_mctrl(struct uart_port *port) -{ - u32 status = esp32_uart_read(port, UART_STATUS_REG); - unsigned int ret = TIOCM_CAR; - - if (status & UART_DSRN) - ret |= TIOCM_DSR; - if (status & UART_CTSN) - ret |= TIOCM_CTS; - - return ret; -} - -static void esp32_uart_stop_tx(struct uart_port *port) -{ - u32 int_ena; - - int_ena = esp32_uart_read(port, UART_INT_ENA_REG); - int_ena &= ~UART_TXFIFO_EMPTY_INT; - esp32_uart_write(port, UART_INT_ENA_REG, int_ena); -} - -static void esp32_uart_rxint(struct uart_port *port) -{ - struct tty_port *tty_port = &port->state->port; - u32 rx_fifo_cnt = esp32_uart_rx_fifo_cnt(port); - unsigned long flags; - u32 i; - - if (!rx_fifo_cnt) - return; - - spin_lock_irqsave(&port->lock, flags); - - for (i = 0; i < rx_fifo_cnt; ++i) { - u32 rx = esp32_uart_read(port, UART_FIFO_REG); - - if (!rx && - (esp32_uart_read(port, UART_INT_ST_REG) & UART_BRK_DET_INT)) { - esp32_uart_write(port, UART_INT_CLR_REG, UART_BRK_DET_INT); - ++port->icount.brk; - uart_handle_break(port); - } else { - if (uart_handle_sysrq_char(port, (unsigned char)rx)) - continue; - tty_insert_flip_char(tty_port, rx, TTY_NORMAL); - ++port->icount.rx; - } - } - spin_unlock_irqrestore(&port->lock, flags); - - tty_flip_buffer_push(tty_port); -} - -static void esp32_uart_put_char(struct uart_port *port, u8 c) -{ - esp32_uart_write(port, UART_FIFO_REG, c); -} - -static void esp32_uart_put_char_sync(struct uart_port *port, u8 c) -{ - unsigned long timeout = jiffies + HZ; - - while (esp32_uart_tx_fifo_cnt(port) >= ESP32_UART_TX_FIFO_SIZE) { - if (time_after(jiffies, timeout)) { - dev_warn(port->dev, "timeout waiting for TX FIFO\n"); - return; - } - cpu_relax(); - } - esp32_uart_put_char(port, c); -} - -static void esp32_uart_transmit_buffer(struct uart_port *port) -{ - u32 tx_fifo_used = esp32_uart_tx_fifo_cnt(port); - unsigned int pending; - u8 ch; - - if (tx_fifo_used >= ESP32_UART_TX_FIFO_SIZE) - return; - - pending = uart_port_tx_limited(port, ch, - ESP32_UART_TX_FIFO_SIZE - tx_fifo_used, - true, esp32_uart_put_char(port, ch), - ({})); - if (pending) { - u32 int_ena; - - int_ena = esp32_uart_read(port, UART_INT_ENA_REG); - int_ena |= UART_TXFIFO_EMPTY_INT; - esp32_uart_write(port, UART_INT_ENA_REG, int_ena); - } -} - -static void esp32_uart_txint(struct uart_port *port) -{ - esp32_uart_transmit_buffer(port); -} - -static irqreturn_t esp32_uart_int(int irq, void *dev_id) -{ - struct uart_port *port = dev_id; - u32 status; - - status = esp32_uart_read(port, UART_INT_ST_REG); - - if (status & (UART_RXFIFO_FULL_INT | UART_BRK_DET_INT)) - esp32_uart_rxint(port); - if (status & UART_TXFIFO_EMPTY_INT) - esp32_uart_txint(port); - - esp32_uart_write(port, UART_INT_CLR_REG, status); - - return IRQ_RETVAL(status); -} - -static void esp32_uart_start_tx(struct uart_port *port) -{ - esp32_uart_transmit_buffer(port); -} - -static void esp32_uart_stop_rx(struct uart_port *port) -{ - u32 int_ena; - - int_ena = esp32_uart_read(port, UART_INT_ENA_REG); - int_ena &= ~UART_RXFIFO_FULL_INT; - esp32_uart_write(port, UART_INT_ENA_REG, int_ena); -} - -static int esp32_uart_startup(struct uart_port *port) -{ - int ret = 0; - unsigned long flags; - struct esp32_port *sport = container_of(port, struct esp32_port, port); - - ret = clk_prepare_enable(sport->clk); - if (ret) - return ret; - - ret = request_irq(port->irq, esp32_uart_int, 0, DRIVER_NAME, port); - if (ret) { - clk_disable_unprepare(sport->clk); - return ret; - } - - spin_lock_irqsave(&port->lock, flags); - if (port_variant(port)->has_clkconf) - esp32_uart_write(port, ESP32S3_UART_CLK_CONF_REG, - ESP32S3_UART_CLK_CONF_DEFAULT); - esp32_uart_write(port, UART_CONF1_REG, - (1 << UART_RXFIFO_FULL_THRHD_SHIFT) | - (1 << port_variant(port)->txfifo_empty_thrhd_shift)); - esp32_uart_write(port, UART_INT_CLR_REG, UART_RXFIFO_FULL_INT | UART_BRK_DET_INT); - esp32_uart_write(port, UART_INT_ENA_REG, UART_RXFIFO_FULL_INT | UART_BRK_DET_INT); - spin_unlock_irqrestore(&port->lock, flags); - - return ret; -} - -static void esp32_uart_shutdown(struct uart_port *port) -{ - struct esp32_port *sport = container_of(port, struct esp32_port, port); - - esp32_uart_write(port, UART_INT_ENA_REG, 0); - free_irq(port->irq, port); - clk_disable_unprepare(sport->clk); -} - -static bool esp32_uart_set_baud(struct uart_port *port, u32 baud) -{ - u32 sclk = port->uartclk; - u32 div = sclk / baud; - - if (port_variant(port)->has_clkconf) { - u32 sclk_div = div / port_variant(port)->clkdiv_mask; - - if (div > port_variant(port)->clkdiv_mask) { - sclk /= (sclk_div + 1); - div = sclk / baud; - } - esp32_uart_write(port, ESP32S3_UART_CLK_CONF_REG, - FIELD_PREP(ESP32S3_UART_SCLK_DIV_NUM, sclk_div) | - ESP32S3_UART_CLK_CONF_DEFAULT); - } - - if (div <= port_variant(port)->clkdiv_mask) { - u32 frag = (sclk * 16) / baud - div * 16; - - esp32_uart_write(port, UART_CLKDIV_REG, - div | FIELD_PREP(UART_CLKDIV_FRAG, frag)); - return true; - } - - return false; -} - -static void esp32_uart_set_termios(struct uart_port *port, - struct ktermios *termios, - const struct ktermios *old) -{ - unsigned long flags; - u32 conf0, conf1; - u32 baud; - const u32 rx_flow_en = port_variant(port)->rx_flow_en; - u32 max_div = port_variant(port)->clkdiv_mask; - - termios->c_cflag &= ~CMSPAR; - - if (port_variant(port)->has_clkconf) - max_div *= FIELD_MAX(ESP32S3_UART_SCLK_DIV_NUM); - - baud = uart_get_baud_rate(port, termios, old, - port->uartclk / max_div, - port->uartclk / 16); - - spin_lock_irqsave(&port->lock, flags); - - conf0 = esp32_uart_read(port, UART_CONF0_REG); - conf0 &= ~(UART_PARITY_EN | UART_PARITY | UART_BIT_NUM | UART_STOP_BIT_NUM); - - conf1 = esp32_uart_read(port, UART_CONF1_REG); - conf1 &= ~rx_flow_en; - - if (termios->c_cflag & PARENB) { - conf0 |= UART_PARITY_EN; - if (termios->c_cflag & PARODD) - conf0 |= UART_PARITY; - } - - switch (termios->c_cflag & CSIZE) { - case CS5: - conf0 |= FIELD_PREP(UART_BIT_NUM, UART_BIT_NUM_5); - break; - case CS6: - conf0 |= FIELD_PREP(UART_BIT_NUM, UART_BIT_NUM_6); - break; - case CS7: - conf0 |= FIELD_PREP(UART_BIT_NUM, UART_BIT_NUM_7); - break; - case CS8: - conf0 |= FIELD_PREP(UART_BIT_NUM, UART_BIT_NUM_8); - break; - } - - if (termios->c_cflag & CSTOPB) - conf0 |= FIELD_PREP(UART_STOP_BIT_NUM, UART_STOP_BIT_NUM_2); - else - conf0 |= FIELD_PREP(UART_STOP_BIT_NUM, UART_STOP_BIT_NUM_1); - - if (termios->c_cflag & CRTSCTS) - conf1 |= rx_flow_en; - - esp32_uart_write(port, UART_CONF0_REG, conf0); - esp32_uart_write(port, UART_CONF1_REG, conf1); - - if (baud) { - esp32_uart_set_baud(port, baud); - uart_update_timeout(port, termios->c_cflag, baud); - } else { - if (esp32_uart_set_baud(port, 115200)) { - baud = 115200; - tty_termios_encode_baud_rate(termios, baud, baud); - uart_update_timeout(port, termios->c_cflag, baud); - } else { - dev_warn(port->dev, - "unable to set speed to %d baud or the default 115200\n", - baud); - } - } - spin_unlock_irqrestore(&port->lock, flags); -} - -static const char *esp32_uart_type(struct uart_port *port) -{ - return port_variant(port)->type; -} - -/* configure/auto-configure the port */ -static void esp32_uart_config_port(struct uart_port *port, int flags) -{ - if (flags & UART_CONFIG_TYPE) - port->type = PORT_GENERIC; -} - -#ifdef CONFIG_CONSOLE_POLL -static int esp32_uart_poll_init(struct uart_port *port) -{ - struct esp32_port *sport = container_of(port, struct esp32_port, port); - - return clk_prepare_enable(sport->clk); -} - -static void esp32_uart_poll_put_char(struct uart_port *port, unsigned char c) -{ - esp32_uart_put_char_sync(port, c); -} - -static int esp32_uart_poll_get_char(struct uart_port *port) -{ - if (esp32_uart_rx_fifo_cnt(port)) - return esp32_uart_read(port, UART_FIFO_REG); - else - return NO_POLL_CHAR; - -} -#endif - -static const struct uart_ops esp32_uart_pops = { - .tx_empty = esp32_uart_tx_empty, - .set_mctrl = esp32_uart_set_mctrl, - .get_mctrl = esp32_uart_get_mctrl, - .stop_tx = esp32_uart_stop_tx, - .start_tx = esp32_uart_start_tx, - .stop_rx = esp32_uart_stop_rx, - .startup = esp32_uart_startup, - .shutdown = esp32_uart_shutdown, - .set_termios = esp32_uart_set_termios, - .type = esp32_uart_type, - .config_port = esp32_uart_config_port, -#ifdef CONFIG_CONSOLE_POLL - .poll_init = esp32_uart_poll_init, - .poll_put_char = esp32_uart_poll_put_char, - .poll_get_char = esp32_uart_poll_get_char, -#endif -}; - -static void esp32_uart_console_putchar(struct uart_port *port, u8 c) -{ - esp32_uart_put_char_sync(port, c); -} - -static void esp32_uart_string_write(struct uart_port *port, const char *s, - unsigned int count) -{ - uart_console_write(port, s, count, esp32_uart_console_putchar); -} - -static void -esp32_uart_console_write(struct console *co, const char *s, unsigned int count) -{ - struct esp32_port *sport = esp32_uart_ports[co->index]; - struct uart_port *port = &sport->port; - unsigned long flags; - bool locked = true; - - if (port->sysrq) - locked = false; - else if (oops_in_progress) - locked = spin_trylock_irqsave(&port->lock, flags); - else - spin_lock_irqsave(&port->lock, flags); - - esp32_uart_string_write(port, s, count); - - if (locked) - spin_unlock_irqrestore(&port->lock, flags); -} - -static int __init esp32_uart_console_setup(struct console *co, char *options) -{ - struct esp32_port *sport; - int baud = 115200; - int bits = 8; - int parity = 'n'; - int flow = 'n'; - int ret; - - /* - * check whether an invalid uart number has been specified, and - * if so, search for the first available port that does have - * console support. - */ - if (co->index == -1 || co->index >= ARRAY_SIZE(esp32_uart_ports)) - co->index = 0; - - sport = esp32_uart_ports[co->index]; - if (!sport) - return -ENODEV; - - ret = clk_prepare_enable(sport->clk); - if (ret) - return ret; - - if (options) - uart_parse_options(options, &baud, &parity, &bits, &flow); - - return uart_set_options(&sport->port, co, baud, parity, bits, flow); -} - -static int esp32_uart_console_exit(struct console *co) -{ - struct esp32_port *sport = esp32_uart_ports[co->index]; - - clk_disable_unprepare(sport->clk); - return 0; -} - -static struct uart_driver esp32_uart_reg; -static struct console esp32_uart_console = { - .name = DEV_NAME, - .write = esp32_uart_console_write, - .device = uart_console_device, - .setup = esp32_uart_console_setup, - .exit = esp32_uart_console_exit, - .flags = CON_PRINTBUFFER, - .index = -1, - .data = &esp32_uart_reg, -}; - -static void esp32_uart_earlycon_putchar(struct uart_port *port, u8 c) -{ - esp32_uart_put_char_sync(port, c); -} - -static void esp32_uart_earlycon_write(struct console *con, const char *s, - unsigned int n) -{ - struct earlycon_device *dev = con->data; - - uart_console_write(&dev->port, s, n, esp32_uart_earlycon_putchar); -} - -#ifdef CONFIG_CONSOLE_POLL -static int esp32_uart_earlycon_read(struct console *con, char *s, unsigned int n) -{ - struct earlycon_device *dev = con->data; - unsigned int num_read = 0; - - while (num_read < n) { - int c = esp32_uart_poll_get_char(&dev->port); - - if (c == NO_POLL_CHAR) - break; - s[num_read++] = c; - } - return num_read; -} -#endif - -static int __init esp32xx_uart_early_console_setup(struct earlycon_device *device, - const char *options) -{ - if (!device->port.membase) - return -ENODEV; - - device->con->write = esp32_uart_earlycon_write; -#ifdef CONFIG_CONSOLE_POLL - device->con->read = esp32_uart_earlycon_read; -#endif - if (device->port.uartclk != BASE_BAUD * 16) - esp32_uart_set_baud(&device->port, device->baud); - - return 0; -} - -static int __init esp32_uart_early_console_setup(struct earlycon_device *device, - const char *options) -{ - device->port.private_data = (void *)&esp32_variant; - - return esp32xx_uart_early_console_setup(device, options); -} - -OF_EARLYCON_DECLARE(esp32uart, "esp,esp32-uart", - esp32_uart_early_console_setup); - -static int __init esp32s3_uart_early_console_setup(struct earlycon_device *device, - const char *options) -{ - device->port.private_data = (void *)&esp32s3_variant; - - return esp32xx_uart_early_console_setup(device, options); -} - -OF_EARLYCON_DECLARE(esp32s3uart, "esp,esp32s3-uart", - esp32s3_uart_early_console_setup); - -static struct uart_driver esp32_uart_reg = { - .owner = THIS_MODULE, - .driver_name = DRIVER_NAME, - .dev_name = DEV_NAME, - .nr = ARRAY_SIZE(esp32_uart_ports), - .cons = &esp32_uart_console, -}; - -static int esp32_uart_probe(struct platform_device *pdev) -{ - struct device_node *np = pdev->dev.of_node; - struct uart_port *port; - struct esp32_port *sport; - struct resource *res; - int ret; - - sport = devm_kzalloc(&pdev->dev, sizeof(*sport), GFP_KERNEL); - if (!sport) - return -ENOMEM; - - port = &sport->port; - - ret = of_alias_get_id(np, "serial"); - if (ret < 0) { - dev_err(&pdev->dev, "failed to get alias id, errno %d\n", ret); - return ret; - } - if (ret >= UART_NR) { - dev_err(&pdev->dev, "driver limited to %d serial ports\n", UART_NR); - return -ENOMEM; - } - - port->line = ret; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENODEV; - - port->mapbase = res->start; - port->membase = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(port->membase)) - return PTR_ERR(port->membase); - - sport->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(sport->clk)) - return PTR_ERR(sport->clk); - - port->uartclk = clk_get_rate(sport->clk); - port->dev = &pdev->dev; - port->type = PORT_GENERIC; - port->iotype = UPIO_MEM; - port->irq = platform_get_irq(pdev, 0); - port->ops = &esp32_uart_pops; - port->flags = UPF_BOOT_AUTOCONF; - port->has_sysrq = 1; - port->fifosize = ESP32_UART_TX_FIFO_SIZE; - port->private_data = (void *)device_get_match_data(&pdev->dev); - - esp32_uart_ports[port->line] = sport; - - platform_set_drvdata(pdev, port); - - return uart_add_one_port(&esp32_uart_reg, port); -} - -static void esp32_uart_remove(struct platform_device *pdev) -{ - struct uart_port *port = platform_get_drvdata(pdev); - - uart_remove_one_port(&esp32_uart_reg, port); -} - - -static struct platform_driver esp32_uart_driver = { - .probe = esp32_uart_probe, - .remove = esp32_uart_remove, - .driver = { - .name = DRIVER_NAME, - .of_match_table = esp32_uart_dt_ids, - }, -}; - -static int __init esp32_uart_init(void) -{ - int ret; - - ret = uart_register_driver(&esp32_uart_reg); - if (ret) - return ret; - - ret = platform_driver_register(&esp32_uart_driver); - if (ret) - uart_unregister_driver(&esp32_uart_reg); - - return ret; -} - -static void __exit esp32_uart_exit(void) -{ - platform_driver_unregister(&esp32_uart_driver); - uart_unregister_driver(&esp32_uart_reg); -} - -module_init(esp32_uart_init); -module_exit(esp32_uart_exit); - -MODULE_AUTHOR("Max Filippov "); -MODULE_DESCRIPTION("Espressif ESP32 UART support"); -MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index c488e5d372ff..251a50c8aa38 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -1442,9 +1442,9 @@ static void imx_uart_enable_dma(struct imx_port *sport) imx_uart_setup_ufcr(sport, TXTL_DMA, RXTL_DMA); - /* set UCR1 */ + /* set UCR1 except TXDMAEN which would be enabled in imx_uart_dma_tx */ ucr1 = imx_uart_readl(sport, UCR1); - ucr1 |= UCR1_RXDMAEN | UCR1_TXDMAEN | UCR1_ATDMAEN; + ucr1 |= UCR1_RXDMAEN | UCR1_ATDMAEN; imx_uart_writel(sport, ucr1, UCR1); sport->dma_is_enabled = 1; @@ -1567,8 +1567,9 @@ static int imx_uart_startup(struct uart_port *port) imx_uart_enable_ms(&sport->port); if (dma_is_inited) { - imx_uart_enable_dma(sport); + /* Note: enable dma request after transfer start! */ imx_uart_start_rx_dma(sport); + imx_uart_enable_dma(sport); } else { ucr1 = imx_uart_readl(sport, UCR1); ucr1 |= UCR1_RRDYEN; diff --git a/drivers/tty/serial/ip22zilog.c b/drivers/tty/serial/ip22zilog.c index 6e19c6713849..3fb8fdf8a853 100644 --- a/drivers/tty/serial/ip22zilog.c +++ b/drivers/tty/serial/ip22zilog.c @@ -1015,7 +1015,7 @@ static struct console ip22zilog_console = { static struct uart_driver ip22zilog_reg = { .owner = THIS_MODULE, - .driver_name = "serial", + .driver_name = "serial_ip22zilog", .dev_name = "ttyS", .major = TTY_MAJOR, .minor = 64, @@ -1025,7 +1025,7 @@ static struct uart_driver ip22zilog_reg = { #endif }; -static void __init ip22zilog_prepare(struct uart_ip22zilog_port *up) +static void ip22zilog_prepare(struct uart_ip22zilog_port *up) { unsigned char sysrq_on = IS_ENABLED(CONFIG_SERIAL_IP22_ZILOG_CONSOLE); int brg; diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c index cc65c9fb6446..693b491f1e75 100644 --- a/drivers/tty/serial/mxs-auart.c +++ b/drivers/tty/serial/mxs-auart.c @@ -1318,7 +1318,8 @@ auart_console_write(struct console *co, const char *str, unsigned int count) s = auart_port[co->index]; port = &s->port; - clk_enable(s->clk); + if (clk_enable(s->clk)) + return; /* First save the CR then disable the interrupts */ old_ctrl2 = mxs_read(s, REG_CTRL2); diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c index e6b0a55f0cfb..b365dd5da3cb 100644 --- a/drivers/tty/serial/qcom_geni_serial.c +++ b/drivers/tty/serial/qcom_geni_serial.c @@ -146,6 +146,7 @@ struct qcom_geni_serial_port { int wakeup_irq; bool rx_tx_swap; bool cts_rts_swap; + bool manual_flow; struct qcom_geni_private_data private_data; const struct qcom_geni_device_data *dev_data; @@ -250,7 +251,7 @@ static void qcom_geni_serial_set_mctrl(struct uart_port *uport, if (mctrl & TIOCM_LOOP) port->loopback = RX_TX_CTS_RTS_SORTED; - if (!(mctrl & TIOCM_RTS) && !uport->suspended) + 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); } @@ -1281,7 +1282,7 @@ 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\n, clk_idx = %u\n", + 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); uport->uartclk = clk_rate; @@ -1401,11 +1402,21 @@ static void qcom_geni_serial_set_termios(struct uart_port *uport, else stop_bit_len = TX_STOP_BIT_LEN_1; - /* flow control, clear the CTS_MASK bit if using flow control. */ - if (termios->c_cflag & CRTSCTS) + /* Configure flow control based on CRTSCTS flag. + * When CRTSCTS is set, use HW/auto flow control mode, where HW + * controls the RTS/CTS pin based FIFO state. + * When CRTSCTS is clear, the CTS pin value is ignored for TX + * path and RTS pin can be set/cleared using registers, for RX + * path. + */ + + if (termios->c_cflag & CRTSCTS) { tx_trans_cfg &= ~UART_CTS_MASK; - else + port->manual_flow = false; + } else { tx_trans_cfg |= UART_CTS_MASK; + port->manual_flow = true; + } if (baud) { uart_update_timeout(uport, termios->c_cflag, baud); diff --git a/drivers/tty/serial/rsci.c b/drivers/tty/serial/rsci.c index c3f12df693ad..b00c9e385169 100644 --- a/drivers/tty/serial/rsci.c +++ b/drivers/tty/serial/rsci.c @@ -695,6 +695,13 @@ struct sci_of_data of_rsci_rzg3e_data = { .params = &rsci_rzg3e_port_params, }; +struct sci_of_data of_rsci_rzg3l_data = { + .type = RSCI_PORT_SCIF32_SINGLE_TCLK, + .ops = &rsci_port_ops, + .uart_ops = &rsci_uart_ops, + .params = &rsci_rzg3e_port_params, +}; + struct sci_of_data of_rsci_rzt2h_data = { .type = RSCI_PORT_SCIF16, .ops = &rsci_port_ops, @@ -703,6 +710,11 @@ struct sci_of_data of_rsci_rzt2h_data = { }; #ifdef CONFIG_SERIAL_SH_SCI_EARLYCON +static int __init rsci_rzg3l_early_console_setup(struct earlycon_device *device, + const char *opt) +{ + return scix_early_console_setup(device, &of_rsci_rzg3l_data); +} static int __init rsci_rzg3e_early_console_setup(struct earlycon_device *device, const char *opt) @@ -716,6 +728,7 @@ static int __init rsci_rzt2h_early_console_setup(struct earlycon_device *device, return scix_early_console_setup(device, &of_rsci_rzt2h_data); } +OF_EARLYCON_DECLARE(rsci, "renesas,r9a08g046-rsci", rsci_rzg3l_early_console_setup); OF_EARLYCON_DECLARE(rsci, "renesas,r9a09g047-rsci", rsci_rzg3e_early_console_setup); OF_EARLYCON_DECLARE(rsci, "renesas,r9a09g077-rsci", rsci_rzt2h_early_console_setup); diff --git a/drivers/tty/serial/rsci.h b/drivers/tty/serial/rsci.h index 2aa2ba3973ee..0985fd1b3348 100644 --- a/drivers/tty/serial/rsci.h +++ b/drivers/tty/serial/rsci.h @@ -6,6 +6,7 @@ #include "sh-sci-common.h" extern struct sci_of_data of_rsci_rzg3e_data; +extern struct sci_of_data of_rsci_rzg3l_data; extern struct sci_of_data of_rsci_rzt2h_data; #endif /* __RSCI_H__ */ diff --git a/drivers/tty/serial/samsung_tty.c b/drivers/tty/serial/samsung_tty.c index c1fabad6ba1f..e27806bf2cf3 100644 --- a/drivers/tty/serial/samsung_tty.c +++ b/drivers/tty/serial/samsung_tty.c @@ -1562,12 +1562,12 @@ static void s3c24xx_serial_set_termios(struct uart_port *port, ulcon |= S3C2410_LCON_PNONE; } - uart_port_lock_irqsave(port, &flags); - dev_dbg(port->dev, "setting ulcon to %08x, brddiv to %d, udivslot %08x\n", ulcon, quot, udivslot); + uart_port_lock_irqsave(port, &flags); + wr_regl(port, S3C2410_ULCON, ulcon); wr_regl(port, S3C2410_UBRDIV, quot); @@ -1587,12 +1587,6 @@ static void s3c24xx_serial_set_termios(struct uart_port *port, if (ourport->info->has_divslot) wr_regl(port, S3C2443_DIVSLOT, udivslot); - dev_dbg(port->dev, - "uart: ulcon = 0x%08x, ucon = 0x%08x, ufcon = 0x%08x\n", - rd_regl(port, S3C2410_ULCON), - rd_regl(port, S3C2410_UCON), - rd_regl(port, S3C2410_UFCON)); - /* * Update the per-port timeout. */ diff --git a/drivers/tty/serial/sh-sci-common.h b/drivers/tty/serial/sh-sci-common.h index f363a659c46a..01ff9fced803 100644 --- a/drivers/tty/serial/sh-sci-common.h +++ b/drivers/tty/serial/sh-sci-common.h @@ -9,6 +9,7 @@ enum SCI_PORT_TYPE { RSCI_PORT_SCIF16 = BIT(7) | 0, RSCI_PORT_SCIF32 = BIT(7) | 1, + RSCI_PORT_SCIF32_SINGLE_TCLK = BIT(7) | 2, }; enum SCI_CLKS { diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index bd7486315338..6c819b6b2425 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1184,7 +1184,8 @@ static int sci_handle_errors(struct uart_port *port) static bool sci_is_rsci_type(u8 type) { - return (type == RSCI_PORT_SCIF16 || type == RSCI_PORT_SCIF32); + return (type == RSCI_PORT_SCIF16 || type == RSCI_PORT_SCIF32 || + type == RSCI_PORT_SCIF32_SINGLE_TCLK); } static int sci_handle_fifo_overrun(struct uart_port *port) @@ -3181,7 +3182,8 @@ static int sci_init_clocks(struct sci_port *sci_port, struct device *dev) if (sci_port->type == PORT_HSCIF) { clk_names[SCI_SCK] = "hsck"; - } else if (sci_port->type == RSCI_PORT_SCIF16) { + } else if (sci_port->type == RSCI_PORT_SCIF16 || + sci_port->type == RSCI_PORT_SCIF32_SINGLE_TCLK) { clk_names[SCI_FCK] = "operation"; clk_names[SCI_BRG_INT] = "bus"; } else if (sci_port->type == RSCI_PORT_SCIF32) { @@ -3196,7 +3198,8 @@ static int sci_init_clocks(struct sci_port *sci_port, struct device *dev) if (IS_ERR(clk)) return PTR_ERR(clk); - if (!clk && sci_port->type == RSCI_PORT_SCIF16 && + if (!clk && (sci_port->type == RSCI_PORT_SCIF16 || + sci_port->type == RSCI_PORT_SCIF32_SINGLE_TCLK) && (i == SCI_FCK || i == SCI_BRG_INT)) return dev_err_probe(dev, -ENODEV, "failed to get %s\n", name); @@ -3330,6 +3333,7 @@ static int sci_init_single(struct platform_device *dev, break; case PORT_SCIFA: case RSCI_PORT_SCIF32: + case RSCI_PORT_SCIF32_SINGLE_TCLK: sci_port->rx_trigger = 32; break; case PORT_SCIF: @@ -3663,6 +3667,10 @@ static const struct of_device_id of_sci_match[] __maybe_unused = { .data = &of_sci_scif_rzv2h, }, #ifdef CONFIG_SERIAL_RSCI + { + .compatible = "renesas,r9a08g046-rsci", + .data = &of_rsci_rzg3l_data, + }, { .compatible = "renesas,r9a09g047-rsci", .data = &of_rsci_rzg3e_data, diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index c593d20a1b5b..a072b75dbaf2 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/tty/serial/zs.c b/drivers/tty/serial/zs.c index 79ea7108a0f3..72a3c0d90f40 100644 --- a/drivers/tty/serial/zs.c +++ b/drivers/tty/serial/zs.c @@ -1252,7 +1252,7 @@ console_initcall(zs_serial_console_init); static struct uart_driver zs_reg = { .owner = THIS_MODULE, - .driver_name = "serial", + .driver_name = "serial_zs", .dev_name = "ttyS", .major = TTY_MAJOR, .minor = 64, diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index 79ec953824d5..96be90db53b7 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -59,6 +59,13 @@ void tty_buffer_lock_exclusive(struct tty_port *port) } EXPORT_SYMBOL_GPL(tty_buffer_lock_exclusive); +static bool tty_buffer_queue_work(struct tty_bufhead *buf) +{ + struct workqueue_struct *flip_wq = READ_ONCE(buf->flip_wq); + + return queue_work(flip_wq ?: system_dfl_wq, &buf->work); +} + /** * tty_buffer_unlock_exclusive - release exclusive access * @port: tty port owning the flip buffer @@ -76,7 +83,7 @@ void tty_buffer_unlock_exclusive(struct tty_port *port) mutex_unlock(&buf->lock); if (restart) - queue_work(system_dfl_wq, &buf->work); + tty_buffer_queue_work(buf); } EXPORT_SYMBOL_GPL(tty_buffer_unlock_exclusive); @@ -530,7 +537,7 @@ void tty_flip_buffer_push(struct tty_port *port) struct tty_bufhead *buf = &port->buf; tty_flip_buffer_commit(buf->tail); - queue_work(system_dfl_wq, &buf->work); + tty_buffer_queue_work(buf); } EXPORT_SYMBOL(tty_flip_buffer_push); @@ -560,7 +567,7 @@ int tty_insert_flip_string_and_push_buffer(struct tty_port *port, tty_flip_buffer_commit(buf->tail); spin_unlock_irqrestore(&port->lock, flags); - queue_work(system_dfl_wq, &buf->work); + tty_buffer_queue_work(buf); return size; } @@ -613,7 +620,7 @@ void tty_buffer_set_lock_subclass(struct tty_port *port) bool tty_buffer_restart_work(struct tty_port *port) { - return queue_work(system_dfl_wq, &port->buf.work); + return tty_buffer_queue_work(&port->buf); } bool tty_buffer_cancel_work(struct tty_port *port) diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index a5d0457e0e28..6b283fd03ff8 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -3443,10 +3443,27 @@ int tty_register_driver(struct tty_driver *driver) if (error < 0) goto err; + /* + * Drivers that do not define driver_name are potentially in-memory devices + * like vty, which generally do not require special workqueue settings. + */ + if (!(driver->flags & TTY_DRIVER_NO_WORKQUEUE) && driver->driver_name) { + driver->flip_wq = alloc_workqueue("%s-%s", WQ_UNBOUND | WQ_SYSFS, + 0, driver->name, driver->driver_name); + if (!driver->flip_wq) { + error = -ENOMEM; + goto err_unreg_char; + } + for (i = 0; i < driver->num; i++) { + if (driver->ports[i]) + tty_port_link_driver_wq(driver->ports[i], driver); + } + } + if (driver->flags & TTY_DRIVER_DYNAMIC_ALLOC) { error = tty_cdev_add(driver, dev, 0, driver->num); if (error) - goto err_unreg_char; + goto err_destroy_wq; } scoped_guard(mutex, &tty_mutex) @@ -3472,6 +3489,10 @@ int tty_register_driver(struct tty_driver *driver) scoped_guard(mutex, &tty_mutex) list_del(&driver->tty_drivers); +err_destroy_wq: + if (driver->flip_wq) + destroy_workqueue(driver->flip_wq); + err_unreg_char: unregister_chrdev_region(dev, driver->num); err: @@ -3491,6 +3512,8 @@ void tty_unregister_driver(struct tty_driver *driver) driver->num); scoped_guard(mutex, &tty_mutex) list_del(&driver->tty_drivers); + if (driver->flip_wq) + destroy_workqueue(driver->flip_wq); } EXPORT_SYMBOL(tty_unregister_driver); diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index 888f2f8f9481..27fe8236f662 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -44,7 +44,7 @@ enum { static DEFINE_RAW_SPINLOCK(tty_ldiscs_lock); /* Line disc dispatch table */ -static struct tty_ldisc_ops *tty_ldiscs[NR_LDISCS]; +static const struct tty_ldisc_ops *tty_ldiscs[NR_LDISCS]; /** * tty_register_ldisc - install a line discipline @@ -55,7 +55,7 @@ static struct tty_ldisc_ops *tty_ldiscs[NR_LDISCS]; * * Locking: takes %tty_ldiscs_lock to guard against ldisc races */ -int tty_register_ldisc(struct tty_ldisc_ops *new_ldisc) +int tty_register_ldisc(const struct tty_ldisc_ops *new_ldisc) { unsigned long flags; @@ -80,7 +80,7 @@ EXPORT_SYMBOL(tty_register_ldisc); * Locking: takes %tty_ldiscs_lock to guard against ldisc races */ -void tty_unregister_ldisc(struct tty_ldisc_ops *ldisc) +void tty_unregister_ldisc(const struct tty_ldisc_ops *ldisc) { unsigned long flags; @@ -90,10 +90,10 @@ void tty_unregister_ldisc(struct tty_ldisc_ops *ldisc) } EXPORT_SYMBOL(tty_unregister_ldisc); -static struct tty_ldisc_ops *get_ldops(int disc) +static const struct tty_ldisc_ops *get_ldops(int disc) { unsigned long flags; - struct tty_ldisc_ops *ldops, *ret; + const struct tty_ldisc_ops *ldops, *ret; raw_spin_lock_irqsave(&tty_ldiscs_lock, flags); ret = ERR_PTR(-EINVAL); @@ -107,7 +107,7 @@ static struct tty_ldisc_ops *get_ldops(int disc) return ret; } -static void put_ldops(struct tty_ldisc_ops *ldops) +static void put_ldops(const struct tty_ldisc_ops *ldops) { unsigned long flags; @@ -139,7 +139,7 @@ int tty_ldisc_autoload = IS_BUILTIN(CONFIG_LDISC_AUTOLOAD); static struct tty_ldisc *tty_ldisc_get(struct tty_struct *tty, int disc) { struct tty_ldisc *ld; - struct tty_ldisc_ops *ldops; + const struct tty_ldisc_ops *ldops; if (disc < N_TTY || disc >= NR_LDISCS) return ERR_PTR(-EINVAL); @@ -202,7 +202,7 @@ static void tty_ldiscs_seq_stop(struct seq_file *m, void *v) static int tty_ldiscs_seq_show(struct seq_file *m, void *v) { int i = *(loff_t *)v; - struct tty_ldisc_ops *ldops; + const struct tty_ldisc_ops *ldops; ldops = get_ldops(i); if (IS_ERR(ldops)) diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index fe67c5cb0a3f..54359310e293 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -99,6 +99,23 @@ void tty_port_init(struct tty_port *port) } EXPORT_SYMBOL(tty_port_init); +/** + * tty_port_link_wq - link tty_port and flip workqueue + * @port: tty_port of the device + * @flip_wq: workqueue to queue flip buffer work on + * + * Whenever %TTY_DRIVER_NO_WORKQUEUE is used, every tty_port can be linked to + * a workqueue manually by this function. + * tty_port will use system_dfl_wq when buf.flip_wq is NULL. + * + * Note that tty_port API will NOT destroy the workqueue. + */ +void tty_port_link_wq(struct tty_port *port, struct workqueue_struct *flip_wq) +{ + port->buf.flip_wq = flip_wq; +} +EXPORT_SYMBOL_GPL(tty_port_link_wq); + /** * tty_port_link_device - link tty and tty_port * @port: tty_port of the device @@ -157,6 +174,7 @@ struct device *tty_port_register_device_attr(struct tty_port *port, const struct attribute_group **attr_grp) { tty_port_link_device(port, driver, index); + tty_port_link_driver_wq(port, driver); return tty_register_device_attr(driver, index, device, drvdata, attr_grp); } @@ -183,6 +201,7 @@ struct device *tty_port_register_device_attr_serdev(struct tty_port *port, struct device *dev; tty_port_link_device(port, driver, index); + tty_port_link_driver_wq(port, driver); dev = serdev_tty_port_register(port, host, parent, driver, index); if (PTR_ERR(dev) != -ENODEV) { @@ -210,6 +229,7 @@ void tty_port_unregister_device(struct tty_port *port, { int ret; + WRITE_ONCE(port->buf.flip_wq, NULL); ret = serdev_tty_port_unregister(port); if (ret == 0) return; @@ -257,6 +277,7 @@ void tty_port_destroy(struct tty_port *port) { tty_buffer_cancel_work(port); tty_buffer_free_all(port); + WRITE_ONCE(port->buf.flip_wq, NULL); } EXPORT_SYMBOL(tty_port_destroy); @@ -703,6 +724,7 @@ int tty_port_install(struct tty_port *port, struct tty_driver *driver, struct tty_struct *tty) { tty->port = port; + tty_port_link_driver_wq(port, driver); return tty_standard_install(driver, tty); } EXPORT_SYMBOL_GPL(tty_port_install); diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c index 13bc048f45e8..dfdea0842149 100644 --- a/drivers/tty/vt/keyboard.c +++ b/drivers/tty/vt/keyboard.c @@ -74,7 +74,7 @@ static inline int kbd_defleds(void) k_self, k_fn, k_spec, k_pad,\ k_dead, k_cons, k_cur, k_shift,\ k_meta, k_ascii, k_lock, k_lowercase,\ - k_slock, k_dead2, k_brl, k_ignore + k_slock, k_dead2, k_brl, k_csi typedef void (k_handler_fn)(struct vc_data *vc, unsigned char value, char up_flag); @@ -127,6 +127,7 @@ static const unsigned char max_vals[] = { [ KT_SLOCK ] = NR_LOCK - 1, [ KT_DEAD2 ] = 255, [ KT_BRL ] = NR_BRL - 1, + [ KT_CSI ] = 99, }; static const int NR_TYPES = ARRAY_SIZE(max_vals); @@ -644,10 +645,6 @@ static void fn_null(struct vc_data *vc) /* * Special key handlers */ -static void k_ignore(struct vc_data *vc, unsigned char value, char up_flag) -{ -} - static void k_spec(struct vc_data *vc, unsigned char value, char up_flag) { if (up_flag) @@ -765,14 +762,39 @@ static void k_fn(struct vc_data *vc, unsigned char value, char up_flag) pr_err("k_fn called with value=%d\n", value); } +/* + * Compute xterm-style modifier parameter for CSI sequences. + * Returns 1 + (shift ? 1 : 0) + (alt ? 2 : 0) + (ctrl ? 4 : 0) + */ +static int csi_modifier_param(void) +{ + int mod = 1; + + if (shift_state & (BIT(KG_SHIFT) | BIT(KG_SHIFTL) | BIT(KG_SHIFTR))) + mod += 1; + if (shift_state & (BIT(KG_ALT) | BIT(KG_ALTGR))) + mod += 2; + if (shift_state & (BIT(KG_CTRL) | BIT(KG_CTRLL) | BIT(KG_CTRLR))) + mod += 4; + return mod; +} + static void k_cur(struct vc_data *vc, unsigned char value, char up_flag) { static const char cur_chars[] = "BDCA"; + int mod; if (up_flag) return; - applkey(vc, cur_chars[value], vc_kbd_mode(kbd, VC_CKMODE)); + mod = csi_modifier_param(); + if (mod > 1) { + char buf[] = { 0x1b, '[', '1', ';', '0' + mod, cur_chars[value], 0x00 }; + + puts_queue(vc, buf); + } else { + applkey(vc, cur_chars[value], vc_kbd_mode(kbd, VC_CKMODE)); + } } static void k_pad(struct vc_data *vc, unsigned char value, char up_flag) @@ -1004,6 +1026,37 @@ static void k_brl(struct vc_data *vc, unsigned char value, char up_flag) } } +/* + * Handle KT_CSI keysym type: generate CSI tilde sequences with modifier + * support. The value encodes the CSI parameter number, producing sequences + * like ESC [ ~ or ESC [ ; ~ when modifiers are held. + */ +static void k_csi(struct vc_data *vc, unsigned char value, char up_flag) +{ + char buf[10]; + int i = 0; + int mod; + + if (up_flag) + return; + + mod = csi_modifier_param(); + + buf[i++] = 0x1b; + buf[i++] = '['; + if (value >= 10) + buf[i++] = '0' + value / 10; + buf[i++] = '0' + value % 10; + if (mod > 1) { + buf[i++] = ';'; + buf[i++] = '0' + mod; + } + buf[i++] = '~'; + buf[i] = 0x00; + + puts_queue(vc, buf); +} + #if IS_ENABLED(CONFIG_INPUT_LEDS) && IS_ENABLED(CONFIG_LEDS_TRIGGERS) struct kbd_led_trigger { @@ -1445,6 +1498,21 @@ static void kbd_keycode(unsigned int keycode, int down, bool hw_raw) param.ledstate = kbd->ledflagstate; key_map = key_maps[shift_final]; + /* + * Fall back to the plain map if modifiers are active, the modifier- + * specific map is missing or has no entry, and the plain map has a + * modifier-aware key type (KT_CUR or KT_CSI). These handlers encode + * the modifier state into the emitted escape sequence. + */ + if (shift_final && keycode < NR_KEYS && + (!key_map || key_map[keycode] == K_HOLE) && key_maps[0]) { + unsigned short plain = key_maps[0][keycode]; + unsigned char type = KTYP(plain); + + if (type >= 0xf0 && (type - 0xf0 == KT_CUR || type - 0xf0 == KT_CSI)) + key_map = key_maps[0]; + } + rc = atomic_notifier_call_chain(&keyboard_notifier_list, KBD_KEYCODE, ¶m); if (rc == NOTIFY_STOP || !key_map) { diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 8470aaf00531..e99636ab9db5 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -1680,9 +1680,7 @@ static void rgb_background(struct vc_data *vc, const struct rgb *c) /* * ITU T.416 Higher colour modes. They break the usual properties of SGR codes - * and thus need to be detected and ignored by hand. That standard also - * wants : rather than ; as separators but sequences containing : are currently - * completely ignored by the parser. + * and thus need to be detected and ignored by hand. * * Subcommands 3 (CMY) and 4 (CMYK) are so insane there's no point in * supporting them. @@ -1739,6 +1737,7 @@ enum { CSI_m_BG_COLOR_END = 47, CSI_m_BG_COLOR = 48, CSI_m_DEFAULT_BG_COLOR = 49, + CSI_m_UNDERLINE_COLOR = 58, CSI_m_BRIGHT_FG_COLOR_BEG = 90, CSI_m_BRIGHT_FG_COLOR_END = 97, CSI_m_BRIGHT_FG_COLOR_OFF = CSI_m_BRIGHT_FG_COLOR_BEG - CSI_m_FG_COLOR_BEG, @@ -2218,6 +2217,7 @@ static void restore_cur(struct vc_data *vc) * @ESesc: ESC parsed * @ESsquare: CSI parsed -- modifiers/parameters/ctrl chars expected * @ESgetpars: CSI parsed -- parameters/ctrl chars expected + * @ESgetsubpars: CSI m parsed -- subparameters expected * @ESfunckey: CSI [ parsed * @EShash: ESC # parsed * @ESsetG0: ESC ( parsed @@ -2238,6 +2238,7 @@ enum vc_ctl_state { ESesc, ESsquare, ESgetpars, + ESgetsubpars, ESfunckey, EShash, ESsetG0, @@ -2759,6 +2760,47 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, u8 c) fallthrough; case ESgetpars: /* ESC [ aka CSI, parameters expected */ switch (c) { + case ':': /* ITU-T T.416 color subparameters */ + if (vc->vc_par[vc->vc_npar] == CSI_m_FG_COLOR || + vc->vc_par[vc->vc_npar] == CSI_m_BG_COLOR || + vc->vc_par[vc->vc_npar] == CSI_m_UNDERLINE_COLOR) + vc->vc_state = ESgetsubpars; + else + break; + fallthrough; + case ';': + if (vc->vc_npar < NPAR - 1) { + vc->vc_npar++; + return; + } + break; + case '0' ... '9': + vc->vc_par[vc->vc_npar] *= 10; + vc->vc_par[vc->vc_npar] += c - '0'; + return; + } + if (c >= ASCII_CSI_IGNORE_FIRST && c <= ASCII_CSI_IGNORE_LAST) { + vc->vc_state = EScsiignore; + return; + } + + /* parameters done, handle the control char @c */ + + vc->vc_state = ESnormal; + + switch (vc->vc_priv) { + case EPdec: + csi_DEC(tty, vc, c); + return; + case EPecma: + csi_ECMA(tty, vc, c); + return; + default: + return; + } + case ESgetsubpars: /* ESC [ 38/48/58, subparameters expected */ + switch (c) { + case ':': case ';': if (vc->vc_npar < NPAR - 1) { vc->vc_npar++; diff --git a/include/linux/serdev.h b/include/linux/serdev.h index 188c0ba62d50..b6c3d957ec15 100644 --- a/include/linux/serdev.h +++ b/include/linux/serdev.h @@ -37,8 +37,8 @@ struct serdev_device_ops { * @nr: Device number on serdev bus. * @ctrl: serdev controller managing this device. * @ops: Device operations. - * @write_comp Completion used by serdev_device_write() internally - * @write_lock Lock to serialize access when writing data + * @write_comp: Completion used by serdev_device_write() internally + * @write_lock: Lock to serialize access when writing data */ struct serdev_device { struct device dev; @@ -57,6 +57,7 @@ struct serdev_device { * structure. * @probe: binds this driver to a serdev device. * @remove: unbinds this driver from the serdev device. + * @shutdown: shut down this serdev device. */ struct serdev_device_driver { struct device_driver driver; @@ -120,7 +121,7 @@ static inline void serdev_device_set_drvdata(struct serdev_device *serdev, void /** * serdev_device_put() - decrement serdev device refcount - * @serdev serdev device. + * @serdev: serdev device. */ static inline void serdev_device_put(struct serdev_device *serdev) { @@ -148,7 +149,7 @@ static inline void serdev_controller_set_drvdata(struct serdev_controller *ctrl, /** * serdev_controller_put() - decrement controller refcount - * @ctrl serdev controller. + * @ctrl: serdev controller. */ static inline void serdev_controller_put(struct serdev_controller *ctrl) { diff --git a/include/linux/tty_buffer.h b/include/linux/tty_buffer.h index 31125e3be3c5..48adcb0e8ff3 100644 --- a/include/linux/tty_buffer.h +++ b/include/linux/tty_buffer.h @@ -34,6 +34,7 @@ static inline u8 *flag_buf_ptr(struct tty_buffer *b, unsigned int ofs) struct tty_bufhead { struct tty_buffer *head; /* Queue head */ + struct workqueue_struct *flip_wq; struct work_struct work; struct mutex lock; atomic_t priority; diff --git a/include/linux/tty_driver.h b/include/linux/tty_driver.h index 188ee9b768eb..1f2896e56e77 100644 --- a/include/linux/tty_driver.h +++ b/include/linux/tty_driver.h @@ -69,6 +69,10 @@ struct serial_struct; * Do not create numbered ``/dev`` nodes. For example, create * ``/dev/ttyprintk`` and not ``/dev/ttyprintk0``. Applicable only when a * driver for a single tty device is being allocated. + * + * @TTY_DRIVER_NO_WORKQUEUE: + * Do not create workqueue when tty_register_driver(). Whenever set, flip + * buffer workqueue can be set by tty_port_link_wq() for every port. */ enum tty_driver_flag { TTY_DRIVER_INSTALLED = BIT(0), @@ -79,6 +83,7 @@ enum tty_driver_flag { TTY_DRIVER_HARDWARE_BREAK = BIT(5), TTY_DRIVER_DYNAMIC_ALLOC = BIT(6), TTY_DRIVER_UNNUMBERED_NODE = BIT(7), + TTY_DRIVER_NO_WORKQUEUE = BIT(8), }; enum tty_driver_type { @@ -506,6 +511,7 @@ struct tty_operations { * @flags: tty driver flags (%TTY_DRIVER_) * @proc_entry: proc fs entry, used internally * @other: driver of the linked tty; only used for the PTY driver + * @flip_wq: workqueue to queue flip buffer work on * @ttys: array of active &struct tty_struct, set by tty_standard_install() * @ports: array of &struct tty_port; can be set during initialization by * tty_port_link_device() and similar @@ -539,6 +545,7 @@ struct tty_driver { unsigned long flags; struct proc_dir_entry *proc_entry; struct tty_driver *other; + struct workqueue_struct *flip_wq; /* * Pointer to the tty data structures diff --git a/include/linux/tty_ldisc.h b/include/linux/tty_ldisc.h index c5cccc3fc1e8..d227a58e3e49 100644 --- a/include/linux/tty_ldisc.h +++ b/include/linux/tty_ldisc.h @@ -266,7 +266,7 @@ struct tty_ldisc_ops { }; struct tty_ldisc { - struct tty_ldisc_ops *ops; + const struct tty_ldisc_ops *ops; struct tty_struct *tty; }; @@ -281,8 +281,8 @@ struct tty_ldisc *tty_ldisc_ref_wait(struct tty_struct *); void tty_ldisc_flush(struct tty_struct *tty); -int tty_register_ldisc(struct tty_ldisc_ops *new_ldisc); -void tty_unregister_ldisc(struct tty_ldisc_ops *ldisc); +int tty_register_ldisc(const struct tty_ldisc_ops *new_ldisc); +void tty_unregister_ldisc(const struct tty_ldisc_ops *ldisc); int tty_set_ldisc(struct tty_struct *tty, int disc); #endif /* _LINUX_TTY_LDISC_H */ diff --git a/include/linux/tty_port.h b/include/linux/tty_port.h index 660c254f1efe..d2a7882c0b58 100644 --- a/include/linux/tty_port.h +++ b/include/linux/tty_port.h @@ -138,6 +138,7 @@ struct tty_port { kernel */ void tty_port_init(struct tty_port *port); +void tty_port_link_wq(struct tty_port *port, struct workqueue_struct *flip_wq); void tty_port_link_device(struct tty_port *port, struct tty_driver *driver, unsigned index); struct device *tty_port_register_device(struct tty_port *port, @@ -165,6 +166,18 @@ static inline struct tty_port *tty_port_get(struct tty_port *port) return NULL; } +/* + * Never overwrite the workqueue set by tty_port_link_wq(). + * No effect when %TTY_DRIVER_NO_WORKQUEUE is set, as driver->flip_wq is + * %NULL. + */ +static inline void tty_port_link_driver_wq(struct tty_port *port, + struct tty_driver *driver) +{ + if (!port->buf.flip_wq) + tty_port_link_wq(port, driver->flip_wq); +} + /* If the cts flow control is enabled, return true. */ static inline bool tty_port_cts_enabled(const struct tty_port *port) { diff --git a/include/uapi/linux/keyboard.h b/include/uapi/linux/keyboard.h index 36d230cedf12..48ecb0cefb45 100644 --- a/include/uapi/linux/keyboard.h +++ b/include/uapi/linux/keyboard.h @@ -41,6 +41,7 @@ #define KT_SLOCK 12 #define KT_DEAD2 13 #define KT_BRL 14 +#define KT_CSI 15 /* CSI sequences with modifier support */ #define K(t,v) (((t)<<8)|(v)) #define KTYP(x) ((x) >> 8) @@ -461,5 +462,33 @@ #define NR_BRL 11 +/* KT_CSI keys: value is the CSI parameter number for ESC [ ~ */ +#define K_CSI_HOME K(KT_CSI, 1) /* ESC [ 1 ~ */ +#define K_CSI_INSERT K(KT_CSI, 2) /* ESC [ 2 ~ */ +#define K_CSI_DELETE K(KT_CSI, 3) /* ESC [ 3 ~ */ +#define K_CSI_END K(KT_CSI, 4) /* ESC [ 4 ~ */ +#define K_CSI_PGUP K(KT_CSI, 5) /* ESC [ 5 ~ */ +#define K_CSI_PGDN K(KT_CSI, 6) /* ESC [ 6 ~ */ +#define K_CSI_F1 K(KT_CSI, 11) /* ESC [ 11 ~ */ +#define K_CSI_F2 K(KT_CSI, 12) /* ESC [ 12 ~ */ +#define K_CSI_F3 K(KT_CSI, 13) /* ESC [ 13 ~ */ +#define K_CSI_F4 K(KT_CSI, 14) /* ESC [ 14 ~ */ +#define K_CSI_F5 K(KT_CSI, 15) /* ESC [ 15 ~ */ +#define K_CSI_F6 K(KT_CSI, 17) /* ESC [ 17 ~ */ +#define K_CSI_F7 K(KT_CSI, 18) /* ESC [ 18 ~ */ +#define K_CSI_F8 K(KT_CSI, 19) /* ESC [ 19 ~ */ +#define K_CSI_F9 K(KT_CSI, 20) /* ESC [ 20 ~ */ +#define K_CSI_F10 K(KT_CSI, 21) /* ESC [ 21 ~ */ +#define K_CSI_F11 K(KT_CSI, 23) /* ESC [ 23 ~ */ +#define K_CSI_F12 K(KT_CSI, 24) /* ESC [ 24 ~ */ +#define K_CSI_F13 K(KT_CSI, 25) /* ESC [ 25 ~ */ +#define K_CSI_F14 K(KT_CSI, 26) /* ESC [ 26 ~ */ +#define K_CSI_F15 K(KT_CSI, 28) /* ESC [ 28 ~ */ +#define K_CSI_F16 K(KT_CSI, 29) /* ESC [ 29 ~ */ +#define K_CSI_F17 K(KT_CSI, 31) /* ESC [ 31 ~ */ +#define K_CSI_F18 K(KT_CSI, 32) /* ESC [ 32 ~ */ +#define K_CSI_F19 K(KT_CSI, 33) /* ESC [ 33 ~ */ +#define K_CSI_F20 K(KT_CSI, 34) /* ESC [ 34 ~ */ + #define MAX_DIACR 256 #endif /* _UAPI__LINUX_KEYBOARD_H */