mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-15 23:41:35 -04:00
Merge tag 'tty-7.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty
Pull tty/serial updates from Greg KH: "Here is the set of tty and serial driver changes for 7.1-rc1. Not much here this cycle, biggest thing is the removal of an old driver that never got any actual hardware support (esp32), and the second try to moving the tty ports to their own workqueues (first try was in 7.0-rc1 but was reverted due to problems) Otherwise it's just a small set of driver updates and some vt modifier key enhancements. All have been in linux-next for a while with no reported issues" * tag 'tty-7.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty: (35 commits) tty: serial: ip22zilog: Fix section mispatch warning hvc/xen: Check console connection flag serial: sh-sci: Add support for RZ/G3L RSCI dt-bindings: serial: renesas,rsci: Document RZ/G3L SoC tty: atmel_serial: update outdated reference to atmel_tasklet_func() serial: xilinx_uartps: Drop unused include serial: qcom-geni: drop stray newline format specifier serial: 8250: loongson: Enable building on MIPS Loongson64 dt-bindings: serial: 8250: Add Loongson 3A4000 uart compatible serial: 8250_fintek: Add support for F81214E tty: tty_port: add workqueue to flip TTY buffer vt: support ITU-T T.416 color subparameters serial: qcom-geni: Fix RTS behavior with flow control tty: serial: imx: keep dma request disabled before dma transfer setup tty: serial: 8250: Add SystemBase Multi I/O cards serial: pic32_uart: allow driver to be compiled on all architectures with COMPILE_TEST serial: tegra: remove Kconfig dependency on APB DMA controller dt-bindings: serial: amlogic,meson-uart: Add compatible string for A9 dt-bindings: serial: atmel,at91-usart: add microchip,lan9691-usart serial: auart: check clk_enable() return in console write ...
This commit is contained in:
@@ -182,6 +182,7 @@ properties:
|
||||
- const: ns16550a
|
||||
- items:
|
||||
- enum:
|
||||
- loongson,ls3a4000-uart
|
||||
- loongson,ls3a5000-uart
|
||||
- loongson,ls3a6000-uart
|
||||
- loongson,ls2k2000-uart
|
||||
|
||||
@@ -56,6 +56,7 @@ properties:
|
||||
items:
|
||||
- enum:
|
||||
- amlogic,a4-uart
|
||||
- amlogic,a9-uart
|
||||
- amlogic,s6-uart
|
||||
- amlogic,s7-uart
|
||||
- amlogic,s7d-uart
|
||||
|
||||
@@ -24,6 +24,7 @@ properties:
|
||||
- const: atmel,at91sam9260-usart
|
||||
- items:
|
||||
- enum:
|
||||
- microchip,lan9691-usart
|
||||
- microchip,sam9x60-usart
|
||||
- microchip,sam9x7-usart
|
||||
- microchip,sama7d65-usart
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 },
|
||||
};
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
/*
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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");
|
||||
@@ -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");
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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__ */
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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, ¶m);
|
||||
if (rc == NOTIFY_STOP || !key_map) {
|
||||
|
||||
@@ -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++;
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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 [ <value> ~ */
|
||||
#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 */
|
||||
|
||||
Reference in New Issue
Block a user