summaryrefslogtreecommitdiff
path: root/drivers/tty
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/tty')
-rw-r--r--drivers/tty/hvc/hvc_iucv.c2
-rw-r--r--drivers/tty/pty.c12
-rw-r--r--drivers/tty/serial/8250/8250_core.c2
-rw-r--r--drivers/tty/serial/8250/8250_fintek.c10
-rw-r--r--drivers/tty/serial/8250/8250_pci.c51
-rw-r--r--drivers/tty/serial/8250/8250_port.c4
-rw-r--r--drivers/tty/serial/8250/Kconfig9
-rw-r--r--drivers/tty/serial/Kconfig33
-rw-r--r--drivers/tty/serial/Makefile2
-rw-r--r--drivers/tty/serial/amba-pl011.c149
-rw-r--r--drivers/tty/serial/apbuart.c2
-rw-r--r--drivers/tty/serial/atmel_serial.c2
-rw-r--r--drivers/tty/serial/dz.c2
-rw-r--r--drivers/tty/serial/esp32_acm.c459
-rw-r--r--drivers/tty/serial/esp32_uart.c779
-rw-r--r--drivers/tty/serial/imx.c7
-rw-r--r--drivers/tty/serial/ip22zilog.c4
-rw-r--r--drivers/tty/serial/mxs-auart.c3
-rw-r--r--drivers/tty/serial/qcom_geni_serial.c21
-rw-r--r--drivers/tty/serial/rsci.c13
-rw-r--r--drivers/tty/serial/rsci.h1
-rw-r--r--drivers/tty/serial/samsung_tty.c10
-rw-r--r--drivers/tty/serial/sh-sci-common.h1
-rw-r--r--drivers/tty/serial/sh-sci.c14
-rw-r--r--drivers/tty/serial/xilinx_uartps.c1
-rw-r--r--drivers/tty/serial/zs.c2
-rw-r--r--drivers/tty/tty_buffer.c15
-rw-r--r--drivers/tty/tty_io.c25
-rw-r--r--drivers/tty/tty_ldisc.c16
-rw-r--r--drivers/tty/tty_port.c22
-rw-r--r--drivers/tty/vt/keyboard.c80
-rw-r--r--drivers/tty/vt/vt.c48
32 files changed, 444 insertions, 1357 deletions
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))
@@ -2701,18 +2766,47 @@ 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);
-OF_EARLYCON_DECLARE(pl011, "arm,sbsa-uart", 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;
+
+ device->con->write = pl011_early_write;
+ device->con->read = pl011_early_read;
+
+ return 0;
+}
+
+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 <linux/bitfield.h>
-#include <linux/bits.h>
-#include <linux/console.h>
-#include <linux/delay.h>
-#include <linux/io.h>
-#include <linux/irq.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/platform_device.h>
-#include <linux/serial_core.h>
-#include <linux/slab.h>
-#include <linux/tty_flip.h>
-#include <asm/serial.h>
-
-#define DRIVER_NAME "esp32s3-acm"
-#define DEV_NAME "ttyGS"
-#define UART_NR 4
-
-#define ESP32S3_ACM_TX_FIFO_SIZE 64
-
-#define USB_SERIAL_JTAG_EP1_REG 0x00
-#define USB_SERIAL_JTAG_EP1_CONF_REG 0x04
-#define USB_SERIAL_JTAG_WR_DONE BIT(0)
-#define USB_SERIAL_JTAG_SERIAL_IN_EP_DATA_FREE BIT(1)
-#define USB_SERIAL_JTAG_INT_ST_REG 0x0c
-#define USB_SERIAL_JTAG_SERIAL_OUT_RECV_PKT_INT_ST BIT(2)
-#define USB_SERIAL_JTAG_SERIAL_IN_EMPTY_INT_ST BIT(3)
-#define USB_SERIAL_JTAG_INT_ENA_REG 0x10
-#define USB_SERIAL_JTAG_SERIAL_OUT_RECV_PKT_INT_ENA BIT(2)
-#define USB_SERIAL_JTAG_SERIAL_IN_EMPTY_INT_ENA BIT(3)
-#define USB_SERIAL_JTAG_INT_CLR_REG 0x14
-#define USB_SERIAL_JTAG_IN_EP1_ST_REG 0x2c
-#define USB_SERIAL_JTAG_IN_EP1_WR_ADDR GENMASK(8, 2)
-#define USB_SERIAL_JTAG_OUT_EP1_ST_REG 0x3c
-#define USB_SERIAL_JTAG_OUT_EP1_REC_DATA_CNT GENMASK(22, 16)
-
-static const struct of_device_id esp32s3_acm_dt_ids[] = {
- {
- .compatible = "esp,esp32s3-acm",
- }, { /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(of, esp32s3_acm_dt_ids);
-
-static struct uart_port *esp32s3_acm_ports[UART_NR];
-
-static void esp32s3_acm_write(struct uart_port *port, unsigned long reg, u32 v)
-{
- writel(v, port->membase + reg);
-}
-
-static u32 esp32s3_acm_read(struct uart_port *port, unsigned long reg)
-{
- return readl(port->membase + reg);
-}
-
-static u32 esp32s3_acm_tx_fifo_free(struct uart_port *port)
-{
- u32 status = esp32s3_acm_read(port, USB_SERIAL_JTAG_EP1_CONF_REG);
-
- return status & USB_SERIAL_JTAG_SERIAL_IN_EP_DATA_FREE;
-}
-
-static u32 esp32s3_acm_tx_fifo_cnt(struct uart_port *port)
-{
- u32 status = esp32s3_acm_read(port, USB_SERIAL_JTAG_IN_EP1_ST_REG);
-
- return FIELD_GET(USB_SERIAL_JTAG_IN_EP1_WR_ADDR, status);
-}
-
-static u32 esp32s3_acm_rx_fifo_cnt(struct uart_port *port)
-{
- u32 status = esp32s3_acm_read(port, USB_SERIAL_JTAG_OUT_EP1_ST_REG);
-
- return FIELD_GET(USB_SERIAL_JTAG_OUT_EP1_REC_DATA_CNT, status);
-}
-
-/* return TIOCSER_TEMT when transmitter is not busy */
-static unsigned int esp32s3_acm_tx_empty(struct uart_port *port)
-{
- return esp32s3_acm_tx_fifo_cnt(port) == 0 ? TIOCSER_TEMT : 0;
-}
-
-static void esp32s3_acm_set_mctrl(struct uart_port *port, unsigned int mctrl)
-{
-}
-
-static unsigned int esp32s3_acm_get_mctrl(struct uart_port *port)
-{
- return TIOCM_CAR;
-}
-
-static void esp32s3_acm_stop_tx(struct uart_port *port)
-{
- u32 int_ena;
-
- int_ena = esp32s3_acm_read(port, USB_SERIAL_JTAG_INT_ENA_REG);
- int_ena &= ~USB_SERIAL_JTAG_SERIAL_IN_EMPTY_INT_ENA;
- esp32s3_acm_write(port, USB_SERIAL_JTAG_INT_ENA_REG, int_ena);
-}
-
-static void esp32s3_acm_rxint(struct uart_port *port)
-{
- struct tty_port *tty_port = &port->state->port;
- u32 rx_fifo_cnt = esp32s3_acm_rx_fifo_cnt(port);
- unsigned long flags;
- u32 i;
-
- if (!rx_fifo_cnt)
- return;
-
- spin_lock_irqsave(&port->lock, flags);
-
- for (i = 0; i < rx_fifo_cnt; ++i) {
- u32 rx = esp32s3_acm_read(port, USB_SERIAL_JTAG_EP1_REG);
-
- ++port->icount.rx;
- tty_insert_flip_char(tty_port, rx, TTY_NORMAL);
- }
- spin_unlock_irqrestore(&port->lock, flags);
-
- tty_flip_buffer_push(tty_port);
-}
-
-static void esp32s3_acm_push(struct uart_port *port)
-{
- if (esp32s3_acm_tx_fifo_free(port))
- esp32s3_acm_write(port, USB_SERIAL_JTAG_EP1_CONF_REG,
- USB_SERIAL_JTAG_WR_DONE);
-}
-
-static void esp32s3_acm_put_char(struct uart_port *port, u8 c)
-{
- esp32s3_acm_write(port, USB_SERIAL_JTAG_EP1_REG, c);
-}
-
-static void esp32s3_acm_put_char_sync(struct uart_port *port, u8 c)
-{
- unsigned long timeout = jiffies + HZ;
-
- while (!esp32s3_acm_tx_fifo_free(port)) {
- if (time_after(jiffies, timeout)) {
- dev_warn(port->dev, "timeout waiting for TX FIFO\n");
- return;
- }
- cpu_relax();
- }
- esp32s3_acm_put_char(port, c);
- esp32s3_acm_push(port);
-}
-
-static void esp32s3_acm_transmit_buffer(struct uart_port *port)
-{
- u32 tx_fifo_used;
- unsigned int pending;
- u8 ch;
-
- if (!esp32s3_acm_tx_fifo_free(port))
- return;
-
- tx_fifo_used = esp32s3_acm_tx_fifo_cnt(port);
- pending = uart_port_tx_limited(port, ch,
- ESP32S3_ACM_TX_FIFO_SIZE - tx_fifo_used,
- true, esp32s3_acm_put_char(port, ch),
- ({}));
- if (pending) {
- u32 int_ena;
-
- int_ena = esp32s3_acm_read(port, USB_SERIAL_JTAG_INT_ENA_REG);
- int_ena |= USB_SERIAL_JTAG_SERIAL_IN_EMPTY_INT_ENA;
- esp32s3_acm_write(port, USB_SERIAL_JTAG_INT_ENA_REG, int_ena);
- }
- esp32s3_acm_push(port);
-}
-
-static void esp32s3_acm_txint(struct uart_port *port)
-{
- esp32s3_acm_transmit_buffer(port);
-}
-
-static irqreturn_t esp32s3_acm_int(int irq, void *dev_id)
-{
- struct uart_port *port = dev_id;
- u32 status;
-
- status = esp32s3_acm_read(port, USB_SERIAL_JTAG_INT_ST_REG);
- esp32s3_acm_write(port, USB_SERIAL_JTAG_INT_CLR_REG, status);
-
- if (status & USB_SERIAL_JTAG_SERIAL_OUT_RECV_PKT_INT_ST)
- esp32s3_acm_rxint(port);
- if (status & USB_SERIAL_JTAG_SERIAL_IN_EMPTY_INT_ST)
- esp32s3_acm_txint(port);
-
- return IRQ_RETVAL(status);
-}
-
-static void esp32s3_acm_start_tx(struct uart_port *port)
-{
- esp32s3_acm_transmit_buffer(port);
-}
-
-static void esp32s3_acm_stop_rx(struct uart_port *port)
-{
- u32 int_ena;
-
- int_ena = esp32s3_acm_read(port, USB_SERIAL_JTAG_INT_ENA_REG);
- int_ena &= ~USB_SERIAL_JTAG_SERIAL_OUT_RECV_PKT_INT_ENA;
- esp32s3_acm_write(port, USB_SERIAL_JTAG_INT_ENA_REG, int_ena);
-}
-
-static int esp32s3_acm_startup(struct uart_port *port)
-{
- int ret;
-
- ret = request_irq(port->irq, esp32s3_acm_int, 0, DRIVER_NAME, port);
- if (ret)
- return ret;
- esp32s3_acm_write(port, USB_SERIAL_JTAG_INT_ENA_REG,
- USB_SERIAL_JTAG_SERIAL_OUT_RECV_PKT_INT_ENA);
-
- return 0;
-}
-
-static void esp32s3_acm_shutdown(struct uart_port *port)
-{
- esp32s3_acm_write(port, USB_SERIAL_JTAG_INT_ENA_REG, 0);
- free_irq(port->irq, port);
-}
-
-static void esp32s3_acm_set_termios(struct uart_port *port,
- struct ktermios *termios,
- const struct ktermios *old)
-{
-}
-
-static const char *esp32s3_acm_type(struct uart_port *port)
-{
- return "ESP32S3 ACM";
-}
-
-/* configure/auto-configure the port */
-static void esp32s3_acm_config_port(struct uart_port *port, int flags)
-{
- if (flags & UART_CONFIG_TYPE)
- port->type = PORT_GENERIC;
-}
-
-#ifdef CONFIG_CONSOLE_POLL
-static void esp32s3_acm_poll_put_char(struct uart_port *port, unsigned char c)
-{
- esp32s3_acm_put_char_sync(port, c);
-}
-
-static int esp32s3_acm_poll_get_char(struct uart_port *port)
-{
- if (esp32s3_acm_rx_fifo_cnt(port))
- return esp32s3_acm_read(port, USB_SERIAL_JTAG_EP1_REG);
- else
- return NO_POLL_CHAR;
-}
-#endif
-
-static const struct uart_ops esp32s3_acm_pops = {
- .tx_empty = esp32s3_acm_tx_empty,
- .set_mctrl = esp32s3_acm_set_mctrl,
- .get_mctrl = esp32s3_acm_get_mctrl,
- .stop_tx = esp32s3_acm_stop_tx,
- .start_tx = esp32s3_acm_start_tx,
- .stop_rx = esp32s3_acm_stop_rx,
- .startup = esp32s3_acm_startup,
- .shutdown = esp32s3_acm_shutdown,
- .set_termios = esp32s3_acm_set_termios,
- .type = esp32s3_acm_type,
- .config_port = esp32s3_acm_config_port,
-#ifdef CONFIG_CONSOLE_POLL
- .poll_put_char = esp32s3_acm_poll_put_char,
- .poll_get_char = esp32s3_acm_poll_get_char,
-#endif
-};
-
-static void esp32s3_acm_string_write(struct uart_port *port, const char *s,
- unsigned int count)
-{
- uart_console_write(port, s, count, esp32s3_acm_put_char_sync);
-}
-
-static void
-esp32s3_acm_console_write(struct console *co, const char *s, unsigned int count)
-{
- struct uart_port *port = esp32s3_acm_ports[co->index];
- unsigned long flags;
- bool locked = true;
-
- if (port->sysrq)
- locked = false;
- else if (oops_in_progress)
- locked = spin_trylock_irqsave(&port->lock, flags);
- else
- spin_lock_irqsave(&port->lock, flags);
-
- esp32s3_acm_string_write(port, s, count);
-
- if (locked)
- spin_unlock_irqrestore(&port->lock, flags);
-}
-
-static struct uart_driver esp32s3_acm_reg;
-static struct console esp32s3_acm_console = {
- .name = DEV_NAME,
- .write = esp32s3_acm_console_write,
- .device = uart_console_device,
- .flags = CON_PRINTBUFFER,
- .index = -1,
- .data = &esp32s3_acm_reg,
-};
-
-static void esp32s3_acm_earlycon_write(struct console *con, const char *s,
- unsigned int n)
-{
- struct earlycon_device *dev = con->data;
-
- uart_console_write(&dev->port, s, n, esp32s3_acm_put_char_sync);
-}
-
-#ifdef CONFIG_CONSOLE_POLL
-static int esp32s3_acm_earlycon_read(struct console *con, char *s, unsigned int n)
-{
- struct earlycon_device *dev = con->data;
- unsigned int num_read = 0;
-
- while (num_read < n) {
- int c = esp32s3_acm_poll_get_char(&dev->port);
-
- if (c == NO_POLL_CHAR)
- break;
- s[num_read++] = c;
- }
- return num_read;
-}
-#endif
-
-static int __init esp32s3_acm_early_console_setup(struct earlycon_device *device,
- const char *options)
-{
- if (!device->port.membase)
- return -ENODEV;
-
- device->con->write = esp32s3_acm_earlycon_write;
-#ifdef CONFIG_CONSOLE_POLL
- device->con->read = esp32s3_acm_earlycon_read;
-#endif
- return 0;
-}
-
-OF_EARLYCON_DECLARE(esp32s3acm, "esp,esp32s3-acm",
- esp32s3_acm_early_console_setup);
-
-static struct uart_driver esp32s3_acm_reg = {
- .owner = THIS_MODULE,
- .driver_name = DRIVER_NAME,
- .dev_name = DEV_NAME,
- .nr = ARRAY_SIZE(esp32s3_acm_ports),
- .cons = &esp32s3_acm_console,
-};
-
-static int esp32s3_acm_probe(struct platform_device *pdev)
-{
- struct device_node *np = pdev->dev.of_node;
- struct uart_port *port;
- struct resource *res;
- int ret;
-
- port = devm_kzalloc(&pdev->dev, sizeof(*port), GFP_KERNEL);
- if (!port)
- return -ENOMEM;
-
- ret = of_alias_get_id(np, "serial");
- if (ret < 0) {
- dev_err(&pdev->dev, "failed to get alias id, errno %d\n", ret);
- return ret;
- }
- if (ret >= UART_NR) {
- dev_err(&pdev->dev, "driver limited to %d serial ports\n",
- UART_NR);
- return -ENOMEM;
- }
-
- port->line = ret;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res)
- return -ENODEV;
-
- port->mapbase = res->start;
- port->membase = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(port->membase))
- return PTR_ERR(port->membase);
-
- port->dev = &pdev->dev;
- port->type = PORT_GENERIC;
- port->iotype = UPIO_MEM;
- port->irq = platform_get_irq(pdev, 0);
- port->ops = &esp32s3_acm_pops;
- port->flags = UPF_BOOT_AUTOCONF;
- port->has_sysrq = 1;
- port->fifosize = ESP32S3_ACM_TX_FIFO_SIZE;
-
- esp32s3_acm_ports[port->line] = port;
-
- platform_set_drvdata(pdev, port);
-
- return uart_add_one_port(&esp32s3_acm_reg, port);
-}
-
-static void esp32s3_acm_remove(struct platform_device *pdev)
-{
- struct uart_port *port = platform_get_drvdata(pdev);
-
- uart_remove_one_port(&esp32s3_acm_reg, port);
-}
-
-
-static struct platform_driver esp32s3_acm_driver = {
- .probe = esp32s3_acm_probe,
- .remove = esp32s3_acm_remove,
- .driver = {
- .name = DRIVER_NAME,
- .of_match_table = esp32s3_acm_dt_ids,
- },
-};
-
-static int __init esp32s3_acm_init(void)
-{
- int ret;
-
- ret = uart_register_driver(&esp32s3_acm_reg);
- if (ret)
- return ret;
-
- ret = platform_driver_register(&esp32s3_acm_driver);
- if (ret)
- uart_unregister_driver(&esp32s3_acm_reg);
-
- return ret;
-}
-
-static void __exit esp32s3_acm_exit(void)
-{
- platform_driver_unregister(&esp32s3_acm_driver);
- uart_unregister_driver(&esp32s3_acm_reg);
-}
-
-module_init(esp32s3_acm_init);
-module_exit(esp32s3_acm_exit);
-
-MODULE_AUTHOR("Max Filippov <jcmvbkbc@gmail.com>");
-MODULE_DESCRIPTION("Espressif ESP32 USB ACM gadget support");
-MODULE_LICENSE("GPL");
diff --git a/drivers/tty/serial/esp32_uart.c b/drivers/tty/serial/esp32_uart.c
deleted file mode 100644
index 667c2198a03a..000000000000
--- a/drivers/tty/serial/esp32_uart.c
+++ /dev/null
@@ -1,779 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include <linux/bitfield.h>
-#include <linux/bits.h>
-#include <linux/clk.h>
-#include <linux/console.h>
-#include <linux/delay.h>
-#include <linux/io.h>
-#include <linux/irq.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/platform_device.h>
-#include <linux/property.h>
-#include <linux/serial_core.h>
-#include <linux/slab.h>
-#include <linux/tty_flip.h>
-#include <asm/serial.h>
-
-#define DRIVER_NAME "esp32-uart"
-#define DEV_NAME "ttyS"
-#define UART_NR 3
-
-#define ESP32_UART_TX_FIFO_SIZE 127
-#define ESP32_UART_RX_FIFO_SIZE 127
-
-#define UART_FIFO_REG 0x00
-#define UART_INT_RAW_REG 0x04
-#define UART_INT_ST_REG 0x08
-#define UART_INT_ENA_REG 0x0c
-#define UART_INT_CLR_REG 0x10
-#define UART_RXFIFO_FULL_INT BIT(0)
-#define UART_TXFIFO_EMPTY_INT BIT(1)
-#define UART_BRK_DET_INT BIT(7)
-#define UART_CLKDIV_REG 0x14
-#define ESP32_UART_CLKDIV GENMASK(19, 0)
-#define ESP32S3_UART_CLKDIV GENMASK(11, 0)
-#define UART_CLKDIV_SHIFT 0
-#define UART_CLKDIV_FRAG GENMASK(23, 20)
-#define UART_STATUS_REG 0x1c
-#define ESP32_UART_RXFIFO_CNT GENMASK(7, 0)
-#define ESP32S3_UART_RXFIFO_CNT GENMASK(9, 0)
-#define UART_RXFIFO_CNT_SHIFT 0
-#define UART_DSRN BIT(13)
-#define UART_CTSN BIT(14)
-#define ESP32_UART_TXFIFO_CNT GENMASK(23, 16)
-#define ESP32S3_UART_TXFIFO_CNT GENMASK(25, 16)
-#define UART_TXFIFO_CNT_SHIFT 16
-#define UART_CONF0_REG 0x20
-#define UART_PARITY BIT(0)
-#define UART_PARITY_EN BIT(1)
-#define UART_BIT_NUM GENMASK(3, 2)
-#define UART_BIT_NUM_5 0
-#define UART_BIT_NUM_6 1
-#define UART_BIT_NUM_7 2
-#define UART_BIT_NUM_8 3
-#define UART_STOP_BIT_NUM GENMASK(5, 4)
-#define UART_STOP_BIT_NUM_1 1
-#define UART_STOP_BIT_NUM_2 3
-#define UART_SW_RTS BIT(6)
-#define UART_SW_DTR BIT(7)
-#define UART_LOOPBACK BIT(14)
-#define UART_TX_FLOW_EN BIT(15)
-#define UART_RTS_INV BIT(23)
-#define UART_DTR_INV BIT(24)
-#define UART_CONF1_REG 0x24
-#define UART_RXFIFO_FULL_THRHD_SHIFT 0
-#define ESP32_UART_TXFIFO_EMPTY_THRHD_SHIFT 8
-#define ESP32S3_UART_TXFIFO_EMPTY_THRHD_SHIFT 10
-#define ESP32_UART_RX_FLOW_EN BIT(23)
-#define ESP32S3_UART_RX_FLOW_EN BIT(22)
-#define ESP32S3_UART_CLK_CONF_REG 0x78
-#define ESP32S3_UART_SCLK_DIV_B GENMASK(5, 0)
-#define ESP32S3_UART_SCLK_DIV_A GENMASK(11, 6)
-#define ESP32S3_UART_SCLK_DIV_NUM GENMASK(19, 12)
-#define ESP32S3_UART_SCLK_SEL GENMASK(21, 20)
-#define APB_CLK 1
-#define RC_FAST_CLK 2
-#define XTAL_CLK 3
-#define ESP32S3_UART_SCLK_EN BIT(22)
-#define ESP32S3_UART_RST_CORE BIT(23)
-#define ESP32S3_UART_TX_SCLK_EN BIT(24)
-#define ESP32S3_UART_RX_SCLK_EN BIT(25)
-#define ESP32S3_UART_TX_RST_CORE BIT(26)
-#define ESP32S3_UART_RX_RST_CORE BIT(27)
-
-#define ESP32S3_UART_CLK_CONF_DEFAULT \
- (ESP32S3_UART_RX_SCLK_EN | \
- ESP32S3_UART_TX_SCLK_EN | \
- ESP32S3_UART_SCLK_EN | \
- FIELD_PREP(ESP32S3_UART_SCLK_SEL, XTAL_CLK))
-
-struct esp32_port {
- struct uart_port port;
- struct clk *clk;
-};
-
-struct esp32_uart_variant {
- u32 clkdiv_mask;
- u32 rxfifo_cnt_mask;
- u32 txfifo_cnt_mask;
- u32 txfifo_empty_thrhd_shift;
- u32 rx_flow_en;
- const char *type;
- bool has_clkconf;
-};
-
-static const struct esp32_uart_variant esp32_variant = {
- .clkdiv_mask = ESP32_UART_CLKDIV,
- .rxfifo_cnt_mask = ESP32_UART_RXFIFO_CNT,
- .txfifo_cnt_mask = ESP32_UART_TXFIFO_CNT,
- .txfifo_empty_thrhd_shift = ESP32_UART_TXFIFO_EMPTY_THRHD_SHIFT,
- .rx_flow_en = ESP32_UART_RX_FLOW_EN,
- .type = "ESP32 UART",
-};
-
-static const struct esp32_uart_variant esp32s3_variant = {
- .clkdiv_mask = ESP32S3_UART_CLKDIV,
- .rxfifo_cnt_mask = ESP32S3_UART_RXFIFO_CNT,
- .txfifo_cnt_mask = ESP32S3_UART_TXFIFO_CNT,
- .txfifo_empty_thrhd_shift = ESP32S3_UART_TXFIFO_EMPTY_THRHD_SHIFT,
- .rx_flow_en = ESP32S3_UART_RX_FLOW_EN,
- .type = "ESP32S3 UART",
- .has_clkconf = true,
-};
-
-static const struct of_device_id esp32_uart_dt_ids[] = {
- {
- .compatible = "esp,esp32-uart",
- .data = &esp32_variant,
- }, {
- .compatible = "esp,esp32s3-uart",
- .data = &esp32s3_variant,
- }, { /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(of, esp32_uart_dt_ids);
-
-static struct esp32_port *esp32_uart_ports[UART_NR];
-
-static const struct esp32_uart_variant *port_variant(struct uart_port *port)
-{
- return port->private_data;
-}
-
-static void esp32_uart_write(struct uart_port *port, unsigned long reg, u32 v)
-{
- writel(v, port->membase + reg);
-}
-
-static u32 esp32_uart_read(struct uart_port *port, unsigned long reg)
-{
- return readl(port->membase + reg);
-}
-
-static u32 esp32_uart_tx_fifo_cnt(struct uart_port *port)
-{
- u32 status = esp32_uart_read(port, UART_STATUS_REG);
-
- return (status & port_variant(port)->txfifo_cnt_mask) >> UART_TXFIFO_CNT_SHIFT;
-}
-
-static u32 esp32_uart_rx_fifo_cnt(struct uart_port *port)
-{
- u32 status = esp32_uart_read(port, UART_STATUS_REG);
-
- return (status & port_variant(port)->rxfifo_cnt_mask) >> UART_RXFIFO_CNT_SHIFT;
-}
-
-/* return TIOCSER_TEMT when transmitter is not busy */
-static unsigned int esp32_uart_tx_empty(struct uart_port *port)
-{
- return esp32_uart_tx_fifo_cnt(port) ? 0 : TIOCSER_TEMT;
-}
-
-static void esp32_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
-{
- u32 conf0 = esp32_uart_read(port, UART_CONF0_REG);
-
- conf0 &= ~(UART_LOOPBACK |
- UART_SW_RTS | UART_RTS_INV |
- UART_SW_DTR | UART_DTR_INV);
-
- if (mctrl & TIOCM_RTS)
- conf0 |= UART_SW_RTS;
- if (mctrl & TIOCM_DTR)
- conf0 |= UART_SW_DTR;
- if (mctrl & TIOCM_LOOP)
- conf0 |= UART_LOOPBACK;
-
- esp32_uart_write(port, UART_CONF0_REG, conf0);
-}
-
-static unsigned int esp32_uart_get_mctrl(struct uart_port *port)
-{
- u32 status = esp32_uart_read(port, UART_STATUS_REG);
- unsigned int ret = TIOCM_CAR;
-
- if (status & UART_DSRN)
- ret |= TIOCM_DSR;
- if (status & UART_CTSN)
- ret |= TIOCM_CTS;
-
- return ret;
-}
-
-static void esp32_uart_stop_tx(struct uart_port *port)
-{
- u32 int_ena;
-
- int_ena = esp32_uart_read(port, UART_INT_ENA_REG);
- int_ena &= ~UART_TXFIFO_EMPTY_INT;
- esp32_uart_write(port, UART_INT_ENA_REG, int_ena);
-}
-
-static void esp32_uart_rxint(struct uart_port *port)
-{
- struct tty_port *tty_port = &port->state->port;
- u32 rx_fifo_cnt = esp32_uart_rx_fifo_cnt(port);
- unsigned long flags;
- u32 i;
-
- if (!rx_fifo_cnt)
- return;
-
- spin_lock_irqsave(&port->lock, flags);
-
- for (i = 0; i < rx_fifo_cnt; ++i) {
- u32 rx = esp32_uart_read(port, UART_FIFO_REG);
-
- if (!rx &&
- (esp32_uart_read(port, UART_INT_ST_REG) & UART_BRK_DET_INT)) {
- esp32_uart_write(port, UART_INT_CLR_REG, UART_BRK_DET_INT);
- ++port->icount.brk;
- uart_handle_break(port);
- } else {
- if (uart_handle_sysrq_char(port, (unsigned char)rx))
- continue;
- tty_insert_flip_char(tty_port, rx, TTY_NORMAL);
- ++port->icount.rx;
- }
- }
- spin_unlock_irqrestore(&port->lock, flags);
-
- tty_flip_buffer_push(tty_port);
-}
-
-static void esp32_uart_put_char(struct uart_port *port, u8 c)
-{
- esp32_uart_write(port, UART_FIFO_REG, c);
-}
-
-static void esp32_uart_put_char_sync(struct uart_port *port, u8 c)
-{
- unsigned long timeout = jiffies + HZ;
-
- while (esp32_uart_tx_fifo_cnt(port) >= ESP32_UART_TX_FIFO_SIZE) {
- if (time_after(jiffies, timeout)) {
- dev_warn(port->dev, "timeout waiting for TX FIFO\n");
- return;
- }
- cpu_relax();
- }
- esp32_uart_put_char(port, c);
-}
-
-static void esp32_uart_transmit_buffer(struct uart_port *port)
-{
- u32 tx_fifo_used = esp32_uart_tx_fifo_cnt(port);
- unsigned int pending;
- u8 ch;
-
- if (tx_fifo_used >= ESP32_UART_TX_FIFO_SIZE)
- return;
-
- pending = uart_port_tx_limited(port, ch,
- ESP32_UART_TX_FIFO_SIZE - tx_fifo_used,
- true, esp32_uart_put_char(port, ch),
- ({}));
- if (pending) {
- u32 int_ena;
-
- int_ena = esp32_uart_read(port, UART_INT_ENA_REG);
- int_ena |= UART_TXFIFO_EMPTY_INT;
- esp32_uart_write(port, UART_INT_ENA_REG, int_ena);
- }
-}
-
-static void esp32_uart_txint(struct uart_port *port)
-{
- esp32_uart_transmit_buffer(port);
-}
-
-static irqreturn_t esp32_uart_int(int irq, void *dev_id)
-{
- struct uart_port *port = dev_id;
- u32 status;
-
- status = esp32_uart_read(port, UART_INT_ST_REG);
-
- if (status & (UART_RXFIFO_FULL_INT | UART_BRK_DET_INT))
- esp32_uart_rxint(port);
- if (status & UART_TXFIFO_EMPTY_INT)
- esp32_uart_txint(port);
-
- esp32_uart_write(port, UART_INT_CLR_REG, status);
-
- return IRQ_RETVAL(status);
-}
-
-static void esp32_uart_start_tx(struct uart_port *port)
-{
- esp32_uart_transmit_buffer(port);
-}
-
-static void esp32_uart_stop_rx(struct uart_port *port)
-{
- u32 int_ena;
-
- int_ena = esp32_uart_read(port, UART_INT_ENA_REG);
- int_ena &= ~UART_RXFIFO_FULL_INT;
- esp32_uart_write(port, UART_INT_ENA_REG, int_ena);
-}
-
-static int esp32_uart_startup(struct uart_port *port)
-{
- int ret = 0;
- unsigned long flags;
- struct esp32_port *sport = container_of(port, struct esp32_port, port);
-
- ret = clk_prepare_enable(sport->clk);
- if (ret)
- return ret;
-
- ret = request_irq(port->irq, esp32_uart_int, 0, DRIVER_NAME, port);
- if (ret) {
- clk_disable_unprepare(sport->clk);
- return ret;
- }
-
- spin_lock_irqsave(&port->lock, flags);
- if (port_variant(port)->has_clkconf)
- esp32_uart_write(port, ESP32S3_UART_CLK_CONF_REG,
- ESP32S3_UART_CLK_CONF_DEFAULT);
- esp32_uart_write(port, UART_CONF1_REG,
- (1 << UART_RXFIFO_FULL_THRHD_SHIFT) |
- (1 << port_variant(port)->txfifo_empty_thrhd_shift));
- esp32_uart_write(port, UART_INT_CLR_REG, UART_RXFIFO_FULL_INT | UART_BRK_DET_INT);
- esp32_uart_write(port, UART_INT_ENA_REG, UART_RXFIFO_FULL_INT | UART_BRK_DET_INT);
- spin_unlock_irqrestore(&port->lock, flags);
-
- return ret;
-}
-
-static void esp32_uart_shutdown(struct uart_port *port)
-{
- struct esp32_port *sport = container_of(port, struct esp32_port, port);
-
- esp32_uart_write(port, UART_INT_ENA_REG, 0);
- free_irq(port->irq, port);
- clk_disable_unprepare(sport->clk);
-}
-
-static bool esp32_uart_set_baud(struct uart_port *port, u32 baud)
-{
- u32 sclk = port->uartclk;
- u32 div = sclk / baud;
-
- if (port_variant(port)->has_clkconf) {
- u32 sclk_div = div / port_variant(port)->clkdiv_mask;
-
- if (div > port_variant(port)->clkdiv_mask) {
- sclk /= (sclk_div + 1);
- div = sclk / baud;
- }
- esp32_uart_write(port, ESP32S3_UART_CLK_CONF_REG,
- FIELD_PREP(ESP32S3_UART_SCLK_DIV_NUM, sclk_div) |
- ESP32S3_UART_CLK_CONF_DEFAULT);
- }
-
- if (div <= port_variant(port)->clkdiv_mask) {
- u32 frag = (sclk * 16) / baud - div * 16;
-
- esp32_uart_write(port, UART_CLKDIV_REG,
- div | FIELD_PREP(UART_CLKDIV_FRAG, frag));
- return true;
- }
-
- return false;
-}
-
-static void esp32_uart_set_termios(struct uart_port *port,
- struct ktermios *termios,
- const struct ktermios *old)
-{
- unsigned long flags;
- u32 conf0, conf1;
- u32 baud;
- const u32 rx_flow_en = port_variant(port)->rx_flow_en;
- u32 max_div = port_variant(port)->clkdiv_mask;
-
- termios->c_cflag &= ~CMSPAR;
-
- if (port_variant(port)->has_clkconf)
- max_div *= FIELD_MAX(ESP32S3_UART_SCLK_DIV_NUM);
-
- baud = uart_get_baud_rate(port, termios, old,
- port->uartclk / max_div,
- port->uartclk / 16);
-
- spin_lock_irqsave(&port->lock, flags);
-
- conf0 = esp32_uart_read(port, UART_CONF0_REG);
- conf0 &= ~(UART_PARITY_EN | UART_PARITY | UART_BIT_NUM | UART_STOP_BIT_NUM);
-
- conf1 = esp32_uart_read(port, UART_CONF1_REG);
- conf1 &= ~rx_flow_en;
-
- if (termios->c_cflag & PARENB) {
- conf0 |= UART_PARITY_EN;
- if (termios->c_cflag & PARODD)
- conf0 |= UART_PARITY;
- }
-
- switch (termios->c_cflag & CSIZE) {
- case CS5:
- conf0 |= FIELD_PREP(UART_BIT_NUM, UART_BIT_NUM_5);
- break;
- case CS6:
- conf0 |= FIELD_PREP(UART_BIT_NUM, UART_BIT_NUM_6);
- break;
- case CS7:
- conf0 |= FIELD_PREP(UART_BIT_NUM, UART_BIT_NUM_7);
- break;
- case CS8:
- conf0 |= FIELD_PREP(UART_BIT_NUM, UART_BIT_NUM_8);
- break;
- }
-
- if (termios->c_cflag & CSTOPB)
- conf0 |= FIELD_PREP(UART_STOP_BIT_NUM, UART_STOP_BIT_NUM_2);
- else
- conf0 |= FIELD_PREP(UART_STOP_BIT_NUM, UART_STOP_BIT_NUM_1);
-
- if (termios->c_cflag & CRTSCTS)
- conf1 |= rx_flow_en;
-
- esp32_uart_write(port, UART_CONF0_REG, conf0);
- esp32_uart_write(port, UART_CONF1_REG, conf1);
-
- if (baud) {
- esp32_uart_set_baud(port, baud);
- uart_update_timeout(port, termios->c_cflag, baud);
- } else {
- if (esp32_uart_set_baud(port, 115200)) {
- baud = 115200;
- tty_termios_encode_baud_rate(termios, baud, baud);
- uart_update_timeout(port, termios->c_cflag, baud);
- } else {
- dev_warn(port->dev,
- "unable to set speed to %d baud or the default 115200\n",
- baud);
- }
- }
- spin_unlock_irqrestore(&port->lock, flags);
-}
-
-static const char *esp32_uart_type(struct uart_port *port)
-{
- return port_variant(port)->type;
-}
-
-/* configure/auto-configure the port */
-static void esp32_uart_config_port(struct uart_port *port, int flags)
-{
- if (flags & UART_CONFIG_TYPE)
- port->type = PORT_GENERIC;
-}
-
-#ifdef CONFIG_CONSOLE_POLL
-static int esp32_uart_poll_init(struct uart_port *port)
-{
- struct esp32_port *sport = container_of(port, struct esp32_port, port);
-
- return clk_prepare_enable(sport->clk);
-}
-
-static void esp32_uart_poll_put_char(struct uart_port *port, unsigned char c)
-{
- esp32_uart_put_char_sync(port, c);
-}
-
-static int esp32_uart_poll_get_char(struct uart_port *port)
-{
- if (esp32_uart_rx_fifo_cnt(port))
- return esp32_uart_read(port, UART_FIFO_REG);
- else
- return NO_POLL_CHAR;
-
-}
-#endif
-
-static const struct uart_ops esp32_uart_pops = {
- .tx_empty = esp32_uart_tx_empty,
- .set_mctrl = esp32_uart_set_mctrl,
- .get_mctrl = esp32_uart_get_mctrl,
- .stop_tx = esp32_uart_stop_tx,
- .start_tx = esp32_uart_start_tx,
- .stop_rx = esp32_uart_stop_rx,
- .startup = esp32_uart_startup,
- .shutdown = esp32_uart_shutdown,
- .set_termios = esp32_uart_set_termios,
- .type = esp32_uart_type,
- .config_port = esp32_uart_config_port,
-#ifdef CONFIG_CONSOLE_POLL
- .poll_init = esp32_uart_poll_init,
- .poll_put_char = esp32_uart_poll_put_char,
- .poll_get_char = esp32_uart_poll_get_char,
-#endif
-};
-
-static void esp32_uart_console_putchar(struct uart_port *port, u8 c)
-{
- esp32_uart_put_char_sync(port, c);
-}
-
-static void esp32_uart_string_write(struct uart_port *port, const char *s,
- unsigned int count)
-{
- uart_console_write(port, s, count, esp32_uart_console_putchar);
-}
-
-static void
-esp32_uart_console_write(struct console *co, const char *s, unsigned int count)
-{
- struct esp32_port *sport = esp32_uart_ports[co->index];
- struct uart_port *port = &sport->port;
- unsigned long flags;
- bool locked = true;
-
- if (port->sysrq)
- locked = false;
- else if (oops_in_progress)
- locked = spin_trylock_irqsave(&port->lock, flags);
- else
- spin_lock_irqsave(&port->lock, flags);
-
- esp32_uart_string_write(port, s, count);
-
- if (locked)
- spin_unlock_irqrestore(&port->lock, flags);
-}
-
-static int __init esp32_uart_console_setup(struct console *co, char *options)
-{
- struct esp32_port *sport;
- int baud = 115200;
- int bits = 8;
- int parity = 'n';
- int flow = 'n';
- int ret;
-
- /*
- * check whether an invalid uart number has been specified, and
- * if so, search for the first available port that does have
- * console support.
- */
- if (co->index == -1 || co->index >= ARRAY_SIZE(esp32_uart_ports))
- co->index = 0;
-
- sport = esp32_uart_ports[co->index];
- if (!sport)
- return -ENODEV;
-
- ret = clk_prepare_enable(sport->clk);
- if (ret)
- return ret;
-
- if (options)
- uart_parse_options(options, &baud, &parity, &bits, &flow);
-
- return uart_set_options(&sport->port, co, baud, parity, bits, flow);
-}
-
-static int esp32_uart_console_exit(struct console *co)
-{
- struct esp32_port *sport = esp32_uart_ports[co->index];
-
- clk_disable_unprepare(sport->clk);
- return 0;
-}
-
-static struct uart_driver esp32_uart_reg;
-static struct console esp32_uart_console = {
- .name = DEV_NAME,
- .write = esp32_uart_console_write,
- .device = uart_console_device,
- .setup = esp32_uart_console_setup,
- .exit = esp32_uart_console_exit,
- .flags = CON_PRINTBUFFER,
- .index = -1,
- .data = &esp32_uart_reg,
-};
-
-static void esp32_uart_earlycon_putchar(struct uart_port *port, u8 c)
-{
- esp32_uart_put_char_sync(port, c);
-}
-
-static void esp32_uart_earlycon_write(struct console *con, const char *s,
- unsigned int n)
-{
- struct earlycon_device *dev = con->data;
-
- uart_console_write(&dev->port, s, n, esp32_uart_earlycon_putchar);
-}
-
-#ifdef CONFIG_CONSOLE_POLL
-static int esp32_uart_earlycon_read(struct console *con, char *s, unsigned int n)
-{
- struct earlycon_device *dev = con->data;
- unsigned int num_read = 0;
-
- while (num_read < n) {
- int c = esp32_uart_poll_get_char(&dev->port);
-
- if (c == NO_POLL_CHAR)
- break;
- s[num_read++] = c;
- }
- return num_read;
-}
-#endif
-
-static int __init esp32xx_uart_early_console_setup(struct earlycon_device *device,
- const char *options)
-{
- if (!device->port.membase)
- return -ENODEV;
-
- device->con->write = esp32_uart_earlycon_write;
-#ifdef CONFIG_CONSOLE_POLL
- device->con->read = esp32_uart_earlycon_read;
-#endif
- if (device->port.uartclk != BASE_BAUD * 16)
- esp32_uart_set_baud(&device->port, device->baud);
-
- return 0;
-}
-
-static int __init esp32_uart_early_console_setup(struct earlycon_device *device,
- const char *options)
-{
- device->port.private_data = (void *)&esp32_variant;
-
- return esp32xx_uart_early_console_setup(device, options);
-}
-
-OF_EARLYCON_DECLARE(esp32uart, "esp,esp32-uart",
- esp32_uart_early_console_setup);
-
-static int __init esp32s3_uart_early_console_setup(struct earlycon_device *device,
- const char *options)
-{
- device->port.private_data = (void *)&esp32s3_variant;
-
- return esp32xx_uart_early_console_setup(device, options);
-}
-
-OF_EARLYCON_DECLARE(esp32s3uart, "esp,esp32s3-uart",
- esp32s3_uart_early_console_setup);
-
-static struct uart_driver esp32_uart_reg = {
- .owner = THIS_MODULE,
- .driver_name = DRIVER_NAME,
- .dev_name = DEV_NAME,
- .nr = ARRAY_SIZE(esp32_uart_ports),
- .cons = &esp32_uart_console,
-};
-
-static int esp32_uart_probe(struct platform_device *pdev)
-{
- struct device_node *np = pdev->dev.of_node;
- struct uart_port *port;
- struct esp32_port *sport;
- struct resource *res;
- int ret;
-
- sport = devm_kzalloc(&pdev->dev, sizeof(*sport), GFP_KERNEL);
- if (!sport)
- return -ENOMEM;
-
- port = &sport->port;
-
- ret = of_alias_get_id(np, "serial");
- if (ret < 0) {
- dev_err(&pdev->dev, "failed to get alias id, errno %d\n", ret);
- return ret;
- }
- if (ret >= UART_NR) {
- dev_err(&pdev->dev, "driver limited to %d serial ports\n", UART_NR);
- return -ENOMEM;
- }
-
- port->line = ret;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res)
- return -ENODEV;
-
- port->mapbase = res->start;
- port->membase = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(port->membase))
- return PTR_ERR(port->membase);
-
- sport->clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(sport->clk))
- return PTR_ERR(sport->clk);
-
- port->uartclk = clk_get_rate(sport->clk);
- port->dev = &pdev->dev;
- port->type = PORT_GENERIC;
- port->iotype = UPIO_MEM;
- port->irq = platform_get_irq(pdev, 0);
- port->ops = &esp32_uart_pops;
- port->flags = UPF_BOOT_AUTOCONF;
- port->has_sysrq = 1;
- port->fifosize = ESP32_UART_TX_FIFO_SIZE;
- port->private_data = (void *)device_get_match_data(&pdev->dev);
-
- esp32_uart_ports[port->line] = sport;
-
- platform_set_drvdata(pdev, port);
-
- return uart_add_one_port(&esp32_uart_reg, port);
-}
-
-static void esp32_uart_remove(struct platform_device *pdev)
-{
- struct uart_port *port = platform_get_drvdata(pdev);
-
- uart_remove_one_port(&esp32_uart_reg, port);
-}
-
-
-static struct platform_driver esp32_uart_driver = {
- .probe = esp32_uart_probe,
- .remove = esp32_uart_remove,
- .driver = {
- .name = DRIVER_NAME,
- .of_match_table = esp32_uart_dt_ids,
- },
-};
-
-static int __init esp32_uart_init(void)
-{
- int ret;
-
- ret = uart_register_driver(&esp32_uart_reg);
- if (ret)
- return ret;
-
- ret = platform_driver_register(&esp32_uart_driver);
- if (ret)
- uart_unregister_driver(&esp32_uart_reg);
-
- return ret;
-}
-
-static void __exit esp32_uart_exit(void)
-{
- platform_driver_unregister(&esp32_uart_driver);
- uart_unregister_driver(&esp32_uart_reg);
-}
-
-module_init(esp32_uart_init);
-module_exit(esp32_uart_exit);
-
-MODULE_AUTHOR("Max Filippov <jcmvbkbc@gmail.com>");
-MODULE_DESCRIPTION("Espressif ESP32 UART support");
-MODULE_LICENSE("GPL");
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:
@@ -3664,6 +3668,10 @@ static const struct of_device_id of_sci_match[] __maybe_unused = {
},
#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 <linux/of.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
-#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/delay.h>
#include <linux/reset.h>
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 @@ err_unreg_devs:
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
@@ -100,6 +100,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
* @driver: tty_driver for this 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 [ <value> ~ or ESC [ <value> ; <mod> ~ 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, &param);
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++;