summaryrefslogtreecommitdiff
path: root/drivers/tty
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/tty')
-rw-r--r--drivers/tty/Kconfig18
-rw-r--r--drivers/tty/Makefile1
-rw-r--r--drivers/tty/hvc/Kconfig3
-rw-r--r--drivers/tty/ipwireless/Makefile1
-rw-r--r--drivers/tty/ipwireless/main.c8
-rw-r--r--drivers/tty/n_tty.c4
-rw-r--r--drivers/tty/serdev/Kconfig1
-rw-r--r--drivers/tty/serdev/Makefile1
-rw-r--r--drivers/tty/serial/8250/8250_fintek.c5
-rw-r--r--drivers/tty/serial/8250/8250_men_mcb.c1
-rw-r--r--drivers/tty/serial/8250/Kconfig1
-rw-r--r--drivers/tty/serial/Kconfig30
-rw-r--r--drivers/tty/serial/Makefile1
-rw-r--r--drivers/tty/serial/cpm_uart/Makefile1
-rw-r--r--drivers/tty/serial/jsm/Makefile1
-rw-r--r--drivers/tty/serial/milbeaut_usio.c621
-rw-r--r--drivers/tty/serial/sc16is7xx.c34
-rw-r--r--drivers/tty/serial/serial_core.c2
-rw-r--r--drivers/tty/serial/sn_console.c1
-rw-r--r--drivers/tty/serial/sprd_serial.c501
-rw-r--r--drivers/tty/sysrq.c6
-rw-r--r--drivers/tty/tty_jobctrl.c4
-rw-r--r--drivers/tty/tty_port.c10
-rw-r--r--drivers/tty/ttynull.c109
-rw-r--r--drivers/tty/vcc.c1
-rw-r--r--drivers/tty/vt/.gitignore1
-rw-r--r--drivers/tty/vt/consolemap.c8
-rw-r--r--drivers/tty/vt/cp437.uni1
-rw-r--r--drivers/tty/vt/defkeymap.c_shipped1
-rw-r--r--drivers/tty/vt/defkeymap.map1
-rw-r--r--drivers/tty/vt/keyboard.c35
-rw-r--r--drivers/tty/vt/vc_screen.c2
-rw-r--r--drivers/tty/vt/vt.c2
33 files changed, 1345 insertions, 72 deletions
diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig
index e0a04bfc873e..80f65767aacf 100644
--- a/drivers/tty/Kconfig
+++ b/drivers/tty/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
config TTY
bool "Enable TTY" if EXPERT
default y
@@ -83,7 +84,6 @@ config HW_CONSOLE
config VT_HW_CONSOLE_BINDING
bool "Support for binding and unbinding console drivers"
depends on HW_CONSOLE
- default n
---help---
The virtual terminal is the device that interacts with the physical
terminal through console drivers. On these systems, at least one
@@ -312,7 +312,6 @@ config N_GSM
config TRACE_ROUTER
tristate "Trace data router for MIPI P1149.7 cJTAG standard"
depends on TRACE_SINK
- default n
help
The trace router uses the Linux tty line discipline framework to
route trace data coming from a tty port (say UART for example) to
@@ -328,7 +327,6 @@ config TRACE_ROUTER
config TRACE_SINK
tristate "Trace data sink for MIPI P1149.7 cJTAG standard"
- default n
help
The trace sink uses the Linux line discipline framework to receive
trace data coming from the trace router line discipline driver
@@ -376,6 +374,20 @@ config PPC_EARLY_DEBUG_EHV_BC_HANDLE
there simply will be no early console output. This is true also
if you don't boot under a hypervisor at all.
+config NULL_TTY
+ tristate "NULL TTY driver"
+ help
+ Say Y here if you want a NULL TTY which simply discards messages.
+
+ This is useful to allow userspace applications which expect a console
+ device to work without modifications even when no console is
+ available or desired.
+
+ In order to use this driver, you should redirect the console to this
+ TTY, or boot the kernel with console=ttynull.
+
+ If unsure, say N.
+
config GOLDFISH_TTY
tristate "Goldfish TTY Driver"
depends on GOLDFISH
diff --git a/drivers/tty/Makefile b/drivers/tty/Makefile
index c72cafdf32b4..020b1cd9294f 100644
--- a/drivers/tty/Makefile
+++ b/drivers/tty/Makefile
@@ -25,6 +25,7 @@ obj-$(CONFIG_ISI) += isicom.o
obj-$(CONFIG_MOXA_INTELLIO) += moxa.o
obj-$(CONFIG_MOXA_SMARTIO) += mxser.o
obj-$(CONFIG_NOZOMI) += nozomi.o
+obj-$(CONFIG_NULL_TTY) += ttynull.o
obj-$(CONFIG_ROCKETPORT) += rocket.o
obj-$(CONFIG_SYNCLINK_GT) += synclink_gt.o
obj-$(CONFIG_SYNCLINKMP) += synclinkmp.o
diff --git a/drivers/tty/hvc/Kconfig b/drivers/tty/hvc/Kconfig
index 4293c172e120..4d22b911111f 100644
--- a/drivers/tty/hvc/Kconfig
+++ b/drivers/tty/hvc/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
if TTY
config HVC_DRIVER
@@ -24,7 +25,6 @@ config HVC_CONSOLE
config HVC_OLD_HVSI
bool "Old driver for pSeries serial port (/dev/hvsi*)"
depends on HVC_CONSOLE
- default n
config HVC_OPAL
bool "OPAL Console support"
@@ -73,7 +73,6 @@ config HVC_UDBG
bool "udbg based fake hypervisor console"
depends on PPC
select HVC_DRIVER
- default n
help
This is meant to be used during HW bring up or debugging when
no other console mechanism exist but udbg, to get you a quick
diff --git a/drivers/tty/ipwireless/Makefile b/drivers/tty/ipwireless/Makefile
index fe2e1730986b..a665d021e24d 100644
--- a/drivers/tty/ipwireless/Makefile
+++ b/drivers/tty/ipwireless/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
#
# Makefile for the IPWireless driver
#
diff --git a/drivers/tty/ipwireless/main.c b/drivers/tty/ipwireless/main.c
index 3475e841ef5c..4c18bbfe1a92 100644
--- a/drivers/tty/ipwireless/main.c
+++ b/drivers/tty/ipwireless/main.c
@@ -114,6 +114,10 @@ static int ipwireless_probe(struct pcmcia_device *p_dev, void *priv_data)
ipw->common_memory = ioremap(p_dev->resource[2]->start,
resource_size(p_dev->resource[2]));
+ if (!ipw->common_memory) {
+ ret = -ENOMEM;
+ goto exit1;
+ }
if (!request_mem_region(p_dev->resource[2]->start,
resource_size(p_dev->resource[2]),
IPWIRELESS_PCCARD_NAME)) {
@@ -134,6 +138,10 @@ static int ipwireless_probe(struct pcmcia_device *p_dev, void *priv_data)
ipw->attr_memory = ioremap(p_dev->resource[3]->start,
resource_size(p_dev->resource[3]));
+ if (!ipw->attr_memory) {
+ ret = -ENOMEM;
+ goto exit3;
+ }
if (!request_mem_region(p_dev->resource[3]->start,
resource_size(p_dev->resource[3]),
IPWIRELESS_PCCARD_NAME)) {
diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c
index 9cdb0fa3c4bf..f9c584244f72 100644
--- a/drivers/tty/n_tty.c
+++ b/drivers/tty/n_tty.c
@@ -550,9 +550,9 @@ static ssize_t process_output_block(struct tty_struct *tty,
mutex_lock(&ldata->output_lock);
space = tty_write_room(tty);
- if (!space) {
+ if (space <= 0) {
mutex_unlock(&ldata->output_lock);
- return 0;
+ return space;
}
if (nr > space)
nr = space;
diff --git a/drivers/tty/serdev/Kconfig b/drivers/tty/serdev/Kconfig
index 1dbc8352e027..46ae732bfc68 100644
--- a/drivers/tty/serdev/Kconfig
+++ b/drivers/tty/serdev/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
#
# Serial bus device driver configuration
#
diff --git a/drivers/tty/serdev/Makefile b/drivers/tty/serdev/Makefile
index 0cbdb9444d9d..078417e5b068 100644
--- a/drivers/tty/serdev/Makefile
+++ b/drivers/tty/serdev/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
serdev-objs := core.o
obj-$(CONFIG_SERIAL_DEV_BUS) += serdev.o
diff --git a/drivers/tty/serial/8250/8250_fintek.c b/drivers/tty/serial/8250/8250_fintek.c
index 79a4958b3f5c..31c91c2f8c6e 100644
--- a/drivers/tty/serial/8250/8250_fintek.c
+++ b/drivers/tty/serial/8250/8250_fintek.c
@@ -303,8 +303,9 @@ static void fintek_8250_goto_highspeed(struct uart_8250_port *uart,
}
}
-void fintek_8250_set_termios(struct uart_port *port, struct ktermios *termios,
- struct ktermios *old)
+static void fintek_8250_set_termios(struct uart_port *port,
+ struct ktermios *termios,
+ struct ktermios *old)
{
struct fintek_8250 *pdata = port->private_data;
unsigned int baud = tty_termios_baud_rate(termios);
diff --git a/drivers/tty/serial/8250/8250_men_mcb.c b/drivers/tty/serial/8250/8250_men_mcb.c
index 127017cc41d9..02c5aff58a74 100644
--- a/drivers/tty/serial/8250/8250_men_mcb.c
+++ b/drivers/tty/serial/8250/8250_men_mcb.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/module.h>
diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig
index 15c2c5463835..296115f6a4d8 100644
--- a/drivers/tty/serial/8250/Kconfig
+++ b/drivers/tty/serial/8250/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
#
# The 8250/16550 serial drivers. You shouldn't be in this list unless
# you somehow have an implicit or explicit dependency on SERIAL_8250.
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index 72966bc0ac76..14768ad0307e 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
#
# Serial device configuration
#
@@ -369,7 +370,6 @@ config SERIAL_MAX310X
depends on SPI_MASTER
select SERIAL_CORE
select REGMAP_SPI if SPI_MASTER
- default n
help
This selects support for an advanced UART from Maxim (Dallas).
Supported ICs are MAX3107, MAX3108, MAX3109, MAX14830.
@@ -652,7 +652,6 @@ config SERIAL_MUX_CONSOLE
config PDC_CONSOLE
bool "PDC software console support"
depends on PARISC && !SERIAL_MUX && VT
- default n
help
Saying Y here will enable the software based PDC console to be
used as the system console. This is useful for machines in
@@ -1109,7 +1108,6 @@ config SERIAL_QE
depends on QUICC_ENGINE
select SERIAL_CORE
select FW_LOADER
- default n
help
This driver supports the QE serial ports on Freescale embedded
PowerPC that contain a QUICC Engine.
@@ -1582,6 +1580,32 @@ config SERIAL_RDA_CONSOLE
Say 'Y' here if you wish to use the RDA8810PL UART as the system
console. Only earlycon is implemented currently.
+config SERIAL_MILBEAUT_USIO
+ tristate "Milbeaut USIO/UART serial port support"
+ depends on ARCH_MILBEAUT || (COMPILE_TEST && OF)
+ default ARCH_MILBEAUT
+ select SERIAL_CORE
+ help
+ This selects the USIO/UART IP found in Socionext Milbeaut SoCs.
+
+config SERIAL_MILBEAUT_USIO_PORTS
+ int "Maximum number of CSIO/UART ports (1-8)"
+ range 1 8
+ depends on SERIAL_MILBEAUT_USIO
+ default "4"
+
+config SERIAL_MILBEAUT_USIO_CONSOLE
+ bool "Support for console on MILBEAUT USIO/UART serial port"
+ depends on SERIAL_MILBEAUT_USIO=y
+ default y
+ select SERIAL_CORE_CONSOLE
+ select SERIAL_EARLYCON
+ help
+ Say 'Y' here if you wish to use a USIO/UART of Socionext Milbeaut
+ SoCs as the system console (the system console is the device which
+ receives all kernel messages and warnings and which allows logins in
+ single user mode).
+
endmenu
config SERIAL_MCTRL_GPIO
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index 40b702aaa85e..43ca2d0edde8 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -92,6 +92,7 @@ obj-$(CONFIG_SERIAL_PIC32) += pic32_uart.o
obj-$(CONFIG_SERIAL_MPS2_UART) += mps2-uart.o
obj-$(CONFIG_SERIAL_OWL) += owl-uart.o
obj-$(CONFIG_SERIAL_RDA) += rda-uart.o
+obj-$(CONFIG_SERIAL_MILBEAUT_USIO) += milbeaut_usio.o
# GPIOLIB helpers for modem control lines
obj-$(CONFIG_SERIAL_MCTRL_GPIO) += serial_mctrl_gpio.o
diff --git a/drivers/tty/serial/cpm_uart/Makefile b/drivers/tty/serial/cpm_uart/Makefile
index 896a5d57881c..3f3a6ed02ed4 100644
--- a/drivers/tty/serial/cpm_uart/Makefile
+++ b/drivers/tty/serial/cpm_uart/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
#
# Makefile for the Motorola 8xx FEC ethernet controller
#
diff --git a/drivers/tty/serial/jsm/Makefile b/drivers/tty/serial/jsm/Makefile
index 705d1ff6fdeb..4f2dbada7741 100644
--- a/drivers/tty/serial/jsm/Makefile
+++ b/drivers/tty/serial/jsm/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
#
# Makefile for Jasmine adapter
#
diff --git a/drivers/tty/serial/milbeaut_usio.c b/drivers/tty/serial/milbeaut_usio.c
new file mode 100644
index 000000000000..d303b7d4bb20
--- /dev/null
+++ b/drivers/tty/serial/milbeaut_usio.c
@@ -0,0 +1,621 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018 Socionext Inc.
+ */
+
+#if defined(CONFIG_SERIAL_MILBEAUT_USIO_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/clk.h>
+#include <linux/console.h>
+#include <linux/module.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/serial_core.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+
+#define USIO_NAME "mlb-usio-uart"
+#define USIO_UART_DEV_NAME "ttyUSI"
+
+static struct uart_port mlb_usio_ports[CONFIG_SERIAL_MILBEAUT_USIO_PORTS];
+
+#define RX 0
+#define TX 1
+static int mlb_usio_irq[CONFIG_SERIAL_MILBEAUT_USIO_PORTS][2];
+
+#define MLB_USIO_REG_SMR 0
+#define MLB_USIO_REG_SCR 1
+#define MLB_USIO_REG_ESCR 2
+#define MLB_USIO_REG_SSR 3
+#define MLB_USIO_REG_DR 4
+#define MLB_USIO_REG_BGR 6
+#define MLB_USIO_REG_FCR 12
+#define MLB_USIO_REG_FBYTE 14
+
+#define MLB_USIO_SMR_SOE BIT(0)
+#define MLB_USIO_SMR_SBL BIT(3)
+#define MLB_USIO_SCR_TXE BIT(0)
+#define MLB_USIO_SCR_RXE BIT(1)
+#define MLB_USIO_SCR_TBIE BIT(2)
+#define MLB_USIO_SCR_TIE BIT(3)
+#define MLB_USIO_SCR_RIE BIT(4)
+#define MLB_USIO_SCR_UPCL BIT(7)
+#define MLB_USIO_ESCR_L_8BIT 0
+#define MLB_USIO_ESCR_L_5BIT 1
+#define MLB_USIO_ESCR_L_6BIT 2
+#define MLB_USIO_ESCR_L_7BIT 3
+#define MLB_USIO_ESCR_P BIT(3)
+#define MLB_USIO_ESCR_PEN BIT(4)
+#define MLB_USIO_ESCR_FLWEN BIT(7)
+#define MLB_USIO_SSR_TBI BIT(0)
+#define MLB_USIO_SSR_TDRE BIT(1)
+#define MLB_USIO_SSR_RDRF BIT(2)
+#define MLB_USIO_SSR_ORE BIT(3)
+#define MLB_USIO_SSR_FRE BIT(4)
+#define MLB_USIO_SSR_PE BIT(5)
+#define MLB_USIO_SSR_REC BIT(7)
+#define MLB_USIO_SSR_BRK BIT(8)
+#define MLB_USIO_FCR_FE1 BIT(0)
+#define MLB_USIO_FCR_FE2 BIT(1)
+#define MLB_USIO_FCR_FCL1 BIT(2)
+#define MLB_USIO_FCR_FCL2 BIT(3)
+#define MLB_USIO_FCR_FSET BIT(4)
+#define MLB_USIO_FCR_FTIE BIT(9)
+#define MLB_USIO_FCR_FDRQ BIT(10)
+#define MLB_USIO_FCR_FRIIE BIT(11)
+
+static void mlb_usio_stop_tx(struct uart_port *port)
+{
+ writew(readw(port->membase + MLB_USIO_REG_FCR) & ~MLB_USIO_FCR_FTIE,
+ port->membase + MLB_USIO_REG_FCR);
+ writeb(readb(port->membase + MLB_USIO_REG_SCR) & ~MLB_USIO_SCR_TBIE,
+ port->membase + MLB_USIO_REG_SCR);
+}
+
+static void mlb_usio_tx_chars(struct uart_port *port)
+{
+ struct circ_buf *xmit = &port->state->xmit;
+ int count;
+
+ writew(readw(port->membase + MLB_USIO_REG_FCR) & ~MLB_USIO_FCR_FTIE,
+ port->membase + MLB_USIO_REG_FCR);
+ writeb(readb(port->membase + MLB_USIO_REG_SCR) &
+ ~(MLB_USIO_SCR_TIE | MLB_USIO_SCR_TBIE),
+ port->membase + MLB_USIO_REG_SCR);
+
+ if (port->x_char) {
+ writew(port->x_char, port->membase + MLB_USIO_REG_DR);
+ port->icount.tx++;
+ port->x_char = 0;
+ return;
+ }
+ if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
+ mlb_usio_stop_tx(port);
+ return;
+ }
+
+ count = port->fifosize -
+ (readw(port->membase + MLB_USIO_REG_FBYTE) & 0xff);
+
+ do {
+ writew(xmit->buf[xmit->tail], port->membase + MLB_USIO_REG_DR);
+
+ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+ port->icount.tx++;
+ if (uart_circ_empty(xmit))
+ break;
+
+ } while (--count > 0);
+
+ writew(readw(port->membase + MLB_USIO_REG_FCR) & ~MLB_USIO_FCR_FDRQ,
+ port->membase + MLB_USIO_REG_FCR);
+
+ writeb(readb(port->membase + MLB_USIO_REG_SCR) | MLB_USIO_SCR_TBIE,
+ port->membase + MLB_USIO_REG_SCR);
+
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(port);
+
+ if (uart_circ_empty(xmit))
+ mlb_usio_stop_tx(port);
+}
+
+static void mlb_usio_start_tx(struct uart_port *port)
+{
+ u16 fcr = readw(port->membase + MLB_USIO_REG_FCR);
+
+ writew(fcr | MLB_USIO_FCR_FTIE, port->membase + MLB_USIO_REG_FCR);
+ if (!(fcr & MLB_USIO_FCR_FDRQ))
+ return;
+
+ writeb(readb(port->membase + MLB_USIO_REG_SCR) | MLB_USIO_SCR_TBIE,
+ port->membase + MLB_USIO_REG_SCR);
+
+ if (readb(port->membase + MLB_USIO_REG_SSR) & MLB_USIO_SSR_TBI)
+ mlb_usio_tx_chars(port);
+}
+
+static void mlb_usio_stop_rx(struct uart_port *port)
+{
+ writeb(readb(port->membase + MLB_USIO_REG_SCR) & ~MLB_USIO_SCR_RIE,
+ port->membase + MLB_USIO_REG_SCR);
+}
+
+static void mlb_usio_enable_ms(struct uart_port *port)
+{
+ writeb(readb(port->membase + MLB_USIO_REG_SCR) |
+ MLB_USIO_SCR_RIE | MLB_USIO_SCR_RXE,
+ port->membase + MLB_USIO_REG_SCR);
+}
+
+static void mlb_usio_rx_chars(struct uart_port *port)
+{
+ struct tty_port *ttyport = &port->state->port;
+ unsigned long flag = 0;
+ char ch = 0;
+ u8 status;
+ int max_count = 2;
+
+ while (max_count--) {
+ status = readb(port->membase + MLB_USIO_REG_SSR);
+
+ if (!(status & MLB_USIO_SSR_RDRF))
+ break;
+
+ if (!(status & (MLB_USIO_SSR_ORE | MLB_USIO_SSR_FRE |
+ MLB_USIO_SSR_PE))) {
+ ch = readw(port->membase + MLB_USIO_REG_DR);
+ flag = TTY_NORMAL;
+ port->icount.rx++;
+ if (uart_handle_sysrq_char(port, ch))
+ continue;
+ uart_insert_char(port, status, MLB_USIO_SSR_ORE,
+ ch, flag);
+ continue;
+ }
+ if (status & MLB_USIO_SSR_PE)
+ port->icount.parity++;
+ if (status & MLB_USIO_SSR_ORE)
+ port->icount.overrun++;
+ status &= port->read_status_mask;
+ if (status & MLB_USIO_SSR_BRK) {
+ flag = TTY_BREAK;
+ ch = 0;
+ } else
+ if (status & MLB_USIO_SSR_PE) {
+ flag = TTY_PARITY;
+ ch = 0;
+ } else
+ if (status & MLB_USIO_SSR_FRE) {
+ flag = TTY_FRAME;
+ ch = 0;
+ }
+ if (flag)
+ uart_insert_char(port, status, MLB_USIO_SSR_ORE,
+ ch, flag);
+
+ writeb(readb(port->membase + MLB_USIO_REG_SSR) |
+ MLB_USIO_SSR_REC,
+ port->membase + MLB_USIO_REG_SSR);
+
+ max_count = readw(port->membase + MLB_USIO_REG_FBYTE) >> 8;
+ writew(readw(port->membase + MLB_USIO_REG_FCR) |
+ MLB_USIO_FCR_FE2 | MLB_USIO_FCR_FRIIE,
+ port->membase + MLB_USIO_REG_FCR);
+ }
+
+ tty_flip_buffer_push(ttyport);
+}
+
+static irqreturn_t mlb_usio_rx_irq(int irq, void *dev_id)
+{
+ struct uart_port *port = dev_id;
+
+ spin_lock(&port->lock);
+ mlb_usio_rx_chars(port);
+ spin_unlock(&port->lock);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t mlb_usio_tx_irq(int irq, void *dev_id)
+{
+ struct uart_port *port = dev_id;
+
+ spin_lock(&port->lock);
+ if (readb(port->membase + MLB_USIO_REG_SSR) & MLB_USIO_SSR_TBI)
+ mlb_usio_tx_chars(port);
+ spin_unlock(&port->lock);
+
+ return IRQ_HANDLED;
+}
+
+static unsigned int mlb_usio_tx_empty(struct uart_port *port)
+{
+ return (readb(port->membase + MLB_USIO_REG_SSR) & MLB_USIO_SSR_TBI) ?
+ TIOCSER_TEMT : 0;
+}
+
+static void mlb_usio_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+}
+
+static unsigned int mlb_usio_get_mctrl(struct uart_port *port)
+{
+ return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS;
+
+}
+
+static void mlb_usio_break_ctl(struct uart_port *port, int break_state)
+{
+}
+
+static int mlb_usio_startup(struct uart_port *port)
+{
+ const char *portname = to_platform_device(port->dev)->name;
+ unsigned long flags;
+ int ret, index = port->line;
+ unsigned char escr;
+
+ ret = request_irq(mlb_usio_irq[index][RX], mlb_usio_rx_irq,
+ 0, portname, port);
+ if (ret)
+ return ret;
+ ret = request_irq(mlb_usio_irq[index][TX], mlb_usio_tx_irq,
+ 0, portname, port);
+ if (ret) {
+ free_irq(mlb_usio_irq[index][RX], port);
+ return ret;
+ }
+
+ escr = readb(port->membase + MLB_USIO_REG_ESCR);
+ if (of_property_read_bool(port->dev->of_node, "auto-flow-control"))
+ escr |= MLB_USIO_ESCR_FLWEN;
+ spin_lock_irqsave(&port->lock, flags);
+ writeb(0, port->membase + MLB_USIO_REG_SCR);
+ writeb(escr, port->membase + MLB_USIO_REG_ESCR);
+ writeb(MLB_USIO_SCR_UPCL, port->membase + MLB_USIO_REG_SCR);
+ writeb(MLB_USIO_SSR_REC, port->membase + MLB_USIO_REG_SSR);
+ writew(0, port->membase + MLB_USIO_REG_FCR);
+ writew(MLB_USIO_FCR_FCL1 | MLB_USIO_FCR_FCL2,
+ port->membase + MLB_USIO_REG_FCR);
+ writew(MLB_USIO_FCR_FE1 | MLB_USIO_FCR_FE2 | MLB_USIO_FCR_FRIIE,
+ port->membase + MLB_USIO_REG_FCR);
+ writew(0, port->membase + MLB_USIO_REG_FBYTE);
+ writew(BIT(12), port->membase + MLB_USIO_REG_FBYTE);
+
+ writeb(MLB_USIO_SCR_TXE | MLB_USIO_SCR_RIE | MLB_USIO_SCR_TBIE |
+ MLB_USIO_SCR_RXE, port->membase + MLB_USIO_REG_SCR);
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ return 0;
+}
+
+static void mlb_usio_shutdown(struct uart_port *port)
+{
+ int index = port->line;
+
+ free_irq(mlb_usio_irq[index][RX], port);
+ free_irq(mlb_usio_irq[index][TX], port);
+}
+
+static void mlb_usio_set_termios(struct uart_port *port,
+ struct ktermios *termios, struct ktermios *old)
+{
+ unsigned int escr, smr = MLB_USIO_SMR_SOE;
+ unsigned long flags, baud, quot;
+
+ switch (termios->c_cflag & CSIZE) {
+ case CS5:
+ escr = MLB_USIO_ESCR_L_5BIT;
+ break;
+ case CS6:
+ escr = MLB_USIO_ESCR_L_6BIT;
+ break;
+ case CS7:
+ escr = MLB_USIO_ESCR_L_7BIT;
+ break;
+ case CS8:
+ default:
+ escr = MLB_USIO_ESCR_L_8BIT;
+ break;
+ }
+
+ if (termios->c_cflag & CSTOPB)
+ smr |= MLB_USIO_SMR_SBL;
+
+ if (termios->c_cflag & PARENB) {
+ escr |= MLB_USIO_ESCR_PEN;
+ if (termios->c_cflag & PARODD)
+ escr |= MLB_USIO_ESCR_P;
+ }
+ /* Set hard flow control */
+ if (of_property_read_bool(port->dev->of_node, "auto-flow-control") ||
+ (termios->c_cflag & CRTSCTS))
+ escr |= MLB_USIO_ESCR_FLWEN;
+
+ baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk);
+ if (baud > 1)
+ quot = port->uartclk / baud - 1;
+ else
+ quot = 0;
+
+ spin_lock_irqsave(&port->lock, flags);
+ uart_update_timeout(port, termios->c_cflag, baud);
+ port->read_status_mask = MLB_USIO_SSR_ORE | MLB_USIO_SSR_RDRF |
+ MLB_USIO_SSR_TDRE;
+ if (termios->c_iflag & INPCK)
+ port->read_status_mask |= MLB_USIO_SSR_FRE | MLB_USIO_SSR_PE;
+
+ port->ignore_status_mask = 0;
+ if (termios->c_iflag & IGNPAR)
+ port->ignore_status_mask |= MLB_USIO_SSR_FRE | MLB_USIO_SSR_PE;
+ if ((termios->c_iflag & IGNBRK) && (termios->c_iflag & IGNPAR))
+ port->ignore_status_mask |= MLB_USIO_SSR_ORE;
+ if ((termios->c_cflag & CREAD) == 0)
+ port->ignore_status_mask |= MLB_USIO_SSR_RDRF;
+
+ writeb(0, port->membase + MLB_USIO_REG_SCR);
+ writeb(MLB_USIO_SCR_UPCL, port->membase + MLB_USIO_REG_SCR);
+ writeb(MLB_USIO_SSR_REC, port->membase + MLB_USIO_REG_SSR);
+ writew(0, port->membase + MLB_USIO_REG_FCR);
+ writeb(smr, port->membase + MLB_USIO_REG_SMR);
+ writeb(escr, port->membase + MLB_USIO_REG_ESCR);
+ writew(quot, port->membase + MLB_USIO_REG_BGR);
+ writew(0, port->membase + MLB_USIO_REG_FCR);
+ writew(MLB_USIO_FCR_FCL1 | MLB_USIO_FCR_FCL2 | MLB_USIO_FCR_FE1 |
+ MLB_USIO_FCR_FE2 | MLB_USIO_FCR_FRIIE,
+ port->membase + MLB_USIO_REG_FCR);
+ writew(0, port->membase + MLB_USIO_REG_FBYTE);
+ writew(BIT(12), port->membase + MLB_USIO_REG_FBYTE);
+ writeb(MLB_USIO_SCR_RIE | MLB_USIO_SCR_RXE | MLB_USIO_SCR_TBIE |
+ MLB_USIO_SCR_TXE, port->membase + MLB_USIO_REG_SCR);
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static const char *mlb_usio_type(struct uart_port *port)
+{
+ return ((port->type == PORT_MLB_USIO) ? USIO_NAME : NULL);
+}
+
+static void mlb_usio_config_port(struct uart_port *port, int flags)
+{
+ if (flags & UART_CONFIG_TYPE)
+ port->type = PORT_MLB_USIO;
+}
+
+static const struct uart_ops mlb_usio_ops = {
+ .tx_empty = mlb_usio_tx_empty,
+ .set_mctrl = mlb_usio_set_mctrl,
+ .get_mctrl = mlb_usio_get_mctrl,
+ .stop_tx = mlb_usio_stop_tx,
+ .start_tx = mlb_usio_start_tx,
+ .stop_rx = mlb_usio_stop_rx,
+ .enable_ms = mlb_usio_enable_ms,
+ .break_ctl = mlb_usio_break_ctl,
+ .startup = mlb_usio_startup,
+ .shutdown = mlb_usio_shutdown,
+ .set_termios = mlb_usio_set_termios,
+ .type = mlb_usio_type,
+ .config_port = mlb_usio_config_port,
+};
+
+#ifdef CONFIG_SERIAL_MILBEAUT_USIO_CONSOLE
+
+static void mlb_usio_console_putchar(struct uart_port *port, int c)
+{
+ while (!(readb(port->membase + MLB_USIO_REG_SSR) & MLB_USIO_SSR_TDRE))
+ cpu_relax();
+
+ writew(c, port->membase + MLB_USIO_REG_DR);
+}
+
+static void mlb_usio_console_write(struct console *co, const char *s,
+ unsigned int count)
+{
+ struct uart_port *port = &mlb_usio_ports[co->index];
+
+ uart_console_write(port, s, count, mlb_usio_console_putchar);
+}
+
+static int __init mlb_usio_console_setup(struct console *co, char *options)
+{
+ struct uart_port *port;
+ int baud = 115200;
+ int parity = 'n';
+ int flow = 'n';
+ int bits = 8;
+
+ if (co->index >= CONFIG_SERIAL_MILBEAUT_USIO_PORTS)
+ return -ENODEV;
+
+ port = &mlb_usio_ports[co->index];
+ if (!port->membase)
+ return -ENODEV;
+
+
+ if (options)
+ uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+ if (of_property_read_bool(port->dev->of_node, "auto-flow-control"))
+ flow = 'r';
+
+ return uart_set_options(port, co, baud, parity, bits, flow);
+}
+
+
+static struct uart_driver mlb_usio_uart_driver;
+static struct console mlb_usio_console = {
+ .name = USIO_UART_DEV_NAME,
+ .write = mlb_usio_console_write,
+ .device = uart_console_device,
+ .setup = mlb_usio_console_setup,
+ .flags = CON_PRINTBUFFER,
+ .index = -1,
+ .data = &mlb_usio_uart_driver,
+};
+
+static int __init mlb_usio_console_init(void)
+{
+ register_console(&mlb_usio_console);
+ return 0;
+}
+console_initcall(mlb_usio_console_init);
+
+
+static void mlb_usio_early_console_write(struct console *co, const char *s,
+ u_int count)
+{
+ struct earlycon_device *dev = co->data;
+
+ uart_console_write(&dev->port, s, count, mlb_usio_console_putchar);
+}
+
+static int __init mlb_usio_early_console_setup(struct earlycon_device *device,
+ const char *opt)
+{
+ if (!device->port.membase)
+ return -ENODEV;
+ device->con->write = mlb_usio_early_console_write;
+ return 0;
+}
+
+OF_EARLYCON_DECLARE(mlb_usio, "socionext,milbeaut-usio-uart",
+ mlb_usio_early_console_setup);
+
+#define USIO_CONSOLE (&mlb_usio_console)
+#else
+#define USIO_CONSOLE NULL
+#endif
+
+static struct uart_driver mlb_usio_uart_driver = {
+ .owner = THIS_MODULE,
+ .driver_name = USIO_NAME,
+ .dev_name = USIO_UART_DEV_NAME,
+ .cons = USIO_CONSOLE,
+ .nr = CONFIG_SERIAL_MILBEAUT_USIO_PORTS,
+};
+
+static int mlb_usio_probe(struct platform_device *pdev)
+{
+ struct clk *clk = devm_clk_get(&pdev->dev, 0);
+ struct uart_port *port;
+ struct resource *res;
+ int index = 0;
+ int ret;
+
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "Missing clock\n");
+ return PTR_ERR(clk);
+ }
+ ret = clk_prepare_enable(clk);
+ if (ret) {
+ dev_err(&pdev->dev, "Clock enable failed: %d\n", ret);
+ return ret;
+ }
+ of_property_read_u32(pdev->dev.of_node, "index", &index);
+ port = &mlb_usio_ports[index];
+
+ port->private_data = (void *)clk;
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "Missing regs\n");
+ ret = -ENODEV;
+ goto failed;
+ }
+ port->mapbase = res->start;
+ port->membase = ioremap(res->start, (res->end - res->start + 1));
+ port->membase = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+
+ ret = platform_get_irq_byname(pdev, "rx");
+ mlb_usio_irq[index][RX] = ret;
+
+ ret = platform_get_irq_byname(pdev, "tx");
+ mlb_usio_irq[index][TX] = ret;
+
+ port->irq = mlb_usio_irq[index][RX];
+ port->uartclk = clk_get_rate(clk);
+ port->fifosize = 128;
+ port->iotype = UPIO_MEM32;
+ port->flags = UPF_BOOT_AUTOCONF | UPF_SPD_VHI;
+ port->line = index;
+ port->ops = &mlb_usio_ops;
+ port->dev = &pdev->dev;
+
+ ret = uart_add_one_port(&mlb_usio_uart_driver, port);
+ if (ret) {
+ dev_err(&pdev->dev, "Adding port failed: %d\n", ret);
+ goto failed1;
+ }
+ return 0;
+
+failed1:
+ iounmap(port->membase);
+
+failed:
+ clk_disable_unprepare(clk);
+ clk_put(clk);
+
+ return ret;
+}
+
+static int mlb_usio_remove(struct platform_device *pdev)
+{
+ struct uart_port *port = &mlb_usio_ports[pdev->id];
+ struct clk *clk = port->private_data;
+
+ uart_remove_one_port(&mlb_usio_uart_driver, port);
+ clk_disable_unprepare(clk);
+ clk_put(clk);
+
+ return 0;
+}
+
+static const struct of_device_id mlb_usio_dt_ids[] = {
+ { .compatible = "socionext,milbeaut-usio-uart" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mlb_usio_dt_ids);
+
+static struct platform_driver mlb_usio_driver = {
+ .probe = mlb_usio_probe,
+ .remove = mlb_usio_remove,
+ .driver = {
+ .name = USIO_NAME,
+ .of_match_table = mlb_usio_dt_ids,
+ },
+};
+
+static int __init mlb_usio_init(void)
+{
+ int ret = uart_register_driver(&mlb_usio_uart_driver);
+
+ if (ret) {
+ pr_err("%s: uart registration failed: %d\n", __func__, ret);
+ return ret;
+ }
+ ret = platform_driver_register(&mlb_usio_driver);
+ if (ret) {
+ uart_unregister_driver(&mlb_usio_uart_driver);
+ pr_err("%s: drv registration failed: %d\n", __func__, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void __exit mlb_usio_exit(void)
+{
+ platform_driver_unregister(&mlb_usio_driver);
+ uart_unregister_driver(&mlb_usio_uart_driver);
+}
+
+module_init(mlb_usio_init);
+module_exit(mlb_usio_exit);
+
+MODULE_AUTHOR("SOCIONEXT");
+MODULE_DESCRIPTION("MILBEAUT_USIO/UART Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c
index a31db15cd7c0..7d3ae31cc720 100644
--- a/drivers/tty/serial/sc16is7xx.c
+++ b/drivers/tty/serial/sc16is7xx.c
@@ -14,9 +14,9 @@
#include <linux/device.h>
#include <linux/gpio/driver.h>
#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
+#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/serial_core.h>
#include <linux/serial.h>
@@ -1179,7 +1179,8 @@ static int sc16is7xx_probe(struct device *dev,
struct regmap *regmap, int irq, unsigned long flags)
{
struct sched_param sched_param = { .sched_priority = MAX_RT_PRIO / 2 };
- unsigned long freq, *pfreq = dev_get_platdata(dev);
+ unsigned long freq = 0, *pfreq = dev_get_platdata(dev);
+ u32 uartclk = 0;
int i, ret;
struct sc16is7xx_port *s;
@@ -1193,10 +1194,17 @@ static int sc16is7xx_probe(struct device *dev,
return -ENOMEM;
}
+ /* Always ask for fixed clock rate from a property. */
+ device_property_read_u32(dev, "clock-frequency", &uartclk);
+
s->clk = devm_clk_get(dev, NULL);
if (IS_ERR(s->clk)) {
+ if (uartclk)
+ freq = uartclk;
if (pfreq)
freq = *pfreq;
+ if (freq)
+ dev_dbg(dev, "Clock frequency: %luHz\n", freq);
else
return PTR_ERR(s->clk);
} else {
@@ -1384,13 +1392,9 @@ static int sc16is7xx_spi_probe(struct spi_device *spi)
return ret;
if (spi->dev.of_node) {
- const struct of_device_id *of_id =
- of_match_device(sc16is7xx_dt_ids, &spi->dev);
-
- if (!of_id)
+ devtype = device_get_match_data(&spi->dev);
+ if (!devtype)
return -ENODEV;
-
- devtype = (struct sc16is7xx_devtype *)of_id->data;
} else {
const struct spi_device_id *id_entry = spi_get_device_id(spi);
@@ -1426,7 +1430,7 @@ MODULE_DEVICE_TABLE(spi, sc16is7xx_spi_id_table);
static struct spi_driver sc16is7xx_spi_uart_driver = {
.driver = {
.name = SC16IS7XX_NAME,
- .of_match_table = of_match_ptr(sc16is7xx_dt_ids),
+ .of_match_table = sc16is7xx_dt_ids,
},
.probe = sc16is7xx_spi_probe,
.remove = sc16is7xx_spi_remove,
@@ -1445,13 +1449,9 @@ static int sc16is7xx_i2c_probe(struct i2c_client *i2c,
struct regmap *regmap;
if (i2c->dev.of_node) {
- const struct of_device_id *of_id =
- of_match_device(sc16is7xx_dt_ids, &i2c->dev);
-
- if (!of_id)
+ devtype = device_get_match_data(&i2c->dev);
+ if (!devtype)
return -ENODEV;
-
- devtype = (struct sc16is7xx_devtype *)of_id->data;
} else {
devtype = (struct sc16is7xx_devtype *)id->driver_data;
flags = IRQF_TRIGGER_FALLING;
@@ -1484,7 +1484,7 @@ MODULE_DEVICE_TABLE(i2c, sc16is7xx_i2c_id_table);
static struct i2c_driver sc16is7xx_i2c_uart_driver = {
.driver = {
.name = SC16IS7XX_NAME,
- .of_match_table = of_match_ptr(sc16is7xx_dt_ids),
+ .of_match_table = sc16is7xx_dt_ids,
},
.probe = sc16is7xx_i2c_probe,
.remove = sc16is7xx_i2c_remove,
diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index 351843f847c0..69f48717546b 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -1514,7 +1514,7 @@ static void uart_set_termios(struct tty_struct *tty,
}
uart_change_speed(tty, state, old_termios);
- /* reload cflag from termios; port driver may have overriden flags */
+ /* reload cflag from termios; port driver may have overridden flags */
cflag = tty->termios.c_cflag;
/* Handle transition to B0 status */
diff --git a/drivers/tty/serial/sn_console.c b/drivers/tty/serial/sn_console.c
index fe9170731c16..283493358a62 100644
--- a/drivers/tty/serial/sn_console.c
+++ b/drivers/tty/serial/sn_console.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* C-Brick Serial Port (and console) driver for SGI Altix machines.
*
diff --git a/drivers/tty/serial/sprd_serial.c b/drivers/tty/serial/sprd_serial.c
index 1891a45ac05d..73d71a4e6c0c 100644
--- a/drivers/tty/serial/sprd_serial.c
+++ b/drivers/tty/serial/sprd_serial.c
@@ -10,6 +10,9 @@
#include <linux/clk.h>
#include <linux/console.h>
#include <linux/delay.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/dma/sprd-dma.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/kernel.h>
@@ -75,6 +78,7 @@
/* control register 1 */
#define SPRD_CTL1 0x001C
+#define SPRD_DMA_EN BIT(15)
#define RX_HW_FLOW_CTL_THLD BIT(6)
#define RX_HW_FLOW_CTL_EN BIT(7)
#define TX_HW_FLOW_CTL_EN BIT(8)
@@ -86,6 +90,7 @@
#define THLD_TX_EMPTY 0x40
#define THLD_TX_EMPTY_SHIFT 8
#define THLD_RX_FULL 0x40
+#define THLD_RX_FULL_MASK GENMASK(6, 0)
/* config baud rate register */
#define SPRD_CLKD0 0x0024
@@ -100,15 +105,38 @@
#define SPRD_IMSR_TX_FIFO_EMPTY BIT(1)
#define SPRD_IMSR_BREAK_DETECT BIT(7)
#define SPRD_IMSR_TIMEOUT BIT(13)
+#define SPRD_DEFAULT_SOURCE_CLK 26000000
+
+#define SPRD_RX_DMA_STEP 1
+#define SPRD_RX_FIFO_FULL 1
+#define SPRD_TX_FIFO_FULL 0x20
+#define SPRD_UART_RX_SIZE (UART_XMIT_SIZE / 4)
+
+struct sprd_uart_dma {
+ struct dma_chan *chn;
+ unsigned char *virt;
+ dma_addr_t phys_addr;
+ dma_cookie_t cookie;
+ u32 trans_len;
+ bool enable;
+};
struct sprd_uart_port {
struct uart_port port;
char name[16];
+ struct clk *clk;
+ struct sprd_uart_dma tx_dma;
+ struct sprd_uart_dma rx_dma;
+ dma_addr_t pos;
+ unsigned char *rx_buf_tail;
};
static struct sprd_uart_port *sprd_port[UART_NR_MAX];
static int sprd_ports_num;
+static int sprd_start_dma_rx(struct uart_port *port);
+static int sprd_tx_dma_config(struct uart_port *port);
+
static inline unsigned int serial_in(struct uart_port *port,
unsigned int offset)
{
@@ -139,45 +167,389 @@ static void sprd_set_mctrl(struct uart_port *port, unsigned int mctrl)
/* nothing to do */
}
-static void sprd_stop_tx(struct uart_port *port)
+static void sprd_stop_rx(struct uart_port *port)
{
+ struct sprd_uart_port *sp =
+ container_of(port, struct sprd_uart_port, port);
unsigned int ien, iclr;
+ if (sp->rx_dma.enable)
+ dmaengine_terminate_all(sp->rx_dma.chn);
+
iclr = serial_in(port, SPRD_ICLR);
ien = serial_in(port, SPRD_IEN);
- iclr |= SPRD_IEN_TX_EMPTY;
- ien &= ~SPRD_IEN_TX_EMPTY;
+ ien &= ~(SPRD_IEN_RX_FULL | SPRD_IEN_BREAK_DETECT);
+ iclr |= SPRD_IEN_RX_FULL | SPRD_IEN_BREAK_DETECT;
- serial_out(port, SPRD_ICLR, iclr);
serial_out(port, SPRD_IEN, ien);
+ serial_out(port, SPRD_ICLR, iclr);
}
-static void sprd_start_tx(struct uart_port *port)
+static void sprd_uart_dma_enable(struct uart_port *port, bool enable)
{
- unsigned int ien;
+ u32 val = serial_in(port, SPRD_CTL1);
- ien = serial_in(port, SPRD_IEN);
- if (!(ien & SPRD_IEN_TX_EMPTY)) {
- ien |= SPRD_IEN_TX_EMPTY;
- serial_out(port, SPRD_IEN, ien);
+ if (enable)
+ val |= SPRD_DMA_EN;
+ else
+ val &= ~SPRD_DMA_EN;
+
+ serial_out(port, SPRD_CTL1, val);
+}
+
+static void sprd_stop_tx_dma(struct uart_port *port)
+{
+ struct sprd_uart_port *sp =
+ container_of(port, struct sprd_uart_port, port);
+ struct circ_buf *xmit = &port->state->xmit;
+ struct dma_tx_state state;
+ u32 trans_len;
+
+ dmaengine_pause(sp->tx_dma.chn);
+
+ dmaengine_tx_status(sp->tx_dma.chn, sp->tx_dma.cookie, &state);
+ if (state.residue) {
+ trans_len = state.residue - sp->tx_dma.phys_addr;
+ xmit->tail = (xmit->tail + trans_len) & (UART_XMIT_SIZE - 1);
+ port->icount.tx += trans_len;
+ dma_unmap_single(port->dev, sp->tx_dma.phys_addr,
+ sp->tx_dma.trans_len, DMA_TO_DEVICE);
}
+
+ dmaengine_terminate_all(sp->tx_dma.chn);
+ sp->tx_dma.trans_len = 0;
}
-static void sprd_stop_rx(struct uart_port *port)
+static int sprd_tx_buf_remap(struct uart_port *port)
+{
+ struct sprd_uart_port *sp =
+ container_of(port, struct sprd_uart_port, port);
+ struct circ_buf *xmit = &port->state->xmit;
+
+ sp->tx_dma.trans_len =
+ CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
+
+ sp->tx_dma.phys_addr = dma_map_single(port->dev,
+ (void *)&(xmit->buf[xmit->tail]),
+ sp->tx_dma.trans_len,
+ DMA_TO_DEVICE);
+ return dma_mapping_error(port->dev, sp->tx_dma.phys_addr);
+}
+
+static void sprd_complete_tx_dma(void *data)
+{
+ struct uart_port *port = (struct uart_port *)data;
+ struct sprd_uart_port *sp =
+ container_of(port, struct sprd_uart_port, port);
+ struct circ_buf *xmit = &port->state->xmit;
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->lock, flags);
+ dma_unmap_single(port->dev, sp->tx_dma.phys_addr,
+ sp->tx_dma.trans_len, DMA_TO_DEVICE);
+
+ xmit->tail = (xmit->tail + sp->tx_dma.trans_len) & (UART_XMIT_SIZE - 1);
+ port->icount.tx += sp->tx_dma.trans_len;
+
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(port);
+
+ if (uart_circ_empty(xmit) || sprd_tx_buf_remap(port) ||
+ sprd_tx_dma_config(port))
+ sp->tx_dma.trans_len = 0;
+
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static int sprd_uart_dma_submit(struct uart_port *port,
+ struct sprd_uart_dma *ud, u32 trans_len,
+ enum dma_transfer_direction direction,
+ dma_async_tx_callback callback)
+{
+ struct dma_async_tx_descriptor *dma_des;
+ unsigned long flags;
+
+ flags = SPRD_DMA_FLAGS(SPRD_DMA_CHN_MODE_NONE,
+ SPRD_DMA_NO_TRG,
+ SPRD_DMA_FRAG_REQ,
+ SPRD_DMA_TRANS_INT);
+
+ dma_des = dmaengine_prep_slave_single(ud->chn, ud->phys_addr, trans_len,
+ direction, flags);
+ if (!dma_des)
+ return -ENODEV;
+
+ dma_des->callback = callback;
+ dma_des->callback_param = port;
+
+ ud->cookie = dmaengine_submit(dma_des);
+ if (dma_submit_error(ud->cookie))
+ return dma_submit_error(ud->cookie);
+
+ dma_async_issue_pending(ud->chn);
+
+ return 0;
+}
+
+static int sprd_tx_dma_config(struct uart_port *port)
+{
+ struct sprd_uart_port *sp =
+ container_of(port, struct sprd_uart_port, port);
+ u32 burst = sp->tx_dma.trans_len > SPRD_TX_FIFO_FULL ?
+ SPRD_TX_FIFO_FULL : sp->tx_dma.trans_len;
+ int ret;
+ struct dma_slave_config cfg = {
+ .dst_addr = port->mapbase + SPRD_TXD,
+ .src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE,
+ .dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE,
+ .src_maxburst = burst,
+ };
+
+ ret = dmaengine_slave_config(sp->tx_dma.chn, &cfg);
+ if (ret < 0)
+ return ret;
+
+ return sprd_uart_dma_submit(port, &sp->tx_dma, sp->tx_dma.trans_len,
+ DMA_MEM_TO_DEV, sprd_complete_tx_dma);
+}
+
+static void sprd_start_tx_dma(struct uart_port *port)
+{
+ struct sprd_uart_port *sp =
+ container_of(port, struct sprd_uart_port, port);
+ struct circ_buf *xmit = &port->state->xmit;
+
+ if (port->x_char) {
+ serial_out(port, SPRD_TXD, port->x_char);
+ port->icount.tx++;
+ port->x_char = 0;
+ return;
+ }
+
+ if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
+ sprd_stop_tx_dma(port);
+ return;
+ }
+
+ if (sp->tx_dma.trans_len)
+ return;
+
+ if (sprd_tx_buf_remap(port) || sprd_tx_dma_config(port))
+ sp->tx_dma.trans_len = 0;
+}
+
+static void sprd_rx_full_thld(struct uart_port *port, u32 thld)
+{
+ u32 val = serial_in(port, SPRD_CTL2);
+
+ val &= ~THLD_RX_FULL_MASK;
+ val |= thld & THLD_RX_FULL_MASK;
+ serial_out(port, SPRD_CTL2, val);
+}
+
+static int sprd_rx_alloc_buf(struct sprd_uart_port *sp)
+{
+ sp->rx_dma.virt = dma_alloc_coherent(sp->port.dev, SPRD_UART_RX_SIZE,
+ &sp->rx_dma.phys_addr, GFP_KERNEL);
+ if (!sp->rx_dma.virt)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void sprd_rx_free_buf(struct sprd_uart_port *sp)
+{
+ if (sp->rx_dma.virt)
+ dma_free_coherent(sp->port.dev, SPRD_UART_RX_SIZE,
+ sp->rx_dma.virt, sp->rx_dma.phys_addr);
+
+}
+
+static int sprd_rx_dma_config(struct uart_port *port, u32 burst)
+{
+ struct sprd_uart_port *sp =
+ container_of(port, struct sprd_uart_port, port);
+ struct dma_slave_config cfg = {
+ .src_addr = port->mapbase + SPRD_RXD,
+ .src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE,
+ .dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE,
+ .src_maxburst = burst,
+ };
+
+ return dmaengine_slave_config(sp->rx_dma.chn, &cfg);
+}
+
+static void sprd_uart_dma_rx(struct uart_port *port)
+{
+ struct sprd_uart_port *sp =
+ container_of(port, struct sprd_uart_port, port);
+ struct tty_port *tty = &port->state->port;
+
+ port->icount.rx += sp->rx_dma.trans_len;
+ tty_insert_flip_string(tty, sp->rx_buf_tail, sp->rx_dma.trans_len);
+ tty_flip_buffer_push(tty);
+}
+
+static void sprd_uart_dma_irq(struct uart_port *port)
+{
+ struct sprd_uart_port *sp =
+ container_of(port, struct sprd_uart_port, port);
+ struct dma_tx_state state;
+ enum dma_status status;
+
+ status = dmaengine_tx_status(sp->rx_dma.chn,
+ sp->rx_dma.cookie, &state);
+ if (status == DMA_ERROR)
+ sprd_stop_rx(port);
+
+ if (!state.residue && sp->pos == sp->rx_dma.phys_addr)
+ return;
+
+ if (!state.residue) {
+ sp->rx_dma.trans_len = SPRD_UART_RX_SIZE +
+ sp->rx_dma.phys_addr - sp->pos;
+ sp->pos = sp->rx_dma.phys_addr;
+ } else {
+ sp->rx_dma.trans_len = state.residue - sp->pos;
+ sp->pos = state.residue;
+ }
+
+ sprd_uart_dma_rx(port);
+ sp->rx_buf_tail += sp->rx_dma.trans_len;
+}
+
+static void sprd_complete_rx_dma(void *data)
+{
+ struct uart_port *port = (struct uart_port *)data;
+ struct sprd_uart_port *sp =
+ container_of(port, struct sprd_uart_port, port);
+ struct dma_tx_state state;
+ enum dma_status status;
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ status = dmaengine_tx_status(sp->rx_dma.chn,
+ sp->rx_dma.cookie, &state);
+ if (status != DMA_COMPLETE) {
+ sprd_stop_rx(port);
+ spin_unlock_irqrestore(&port->lock, flags);
+ return;
+ }
+
+ if (sp->pos != sp->rx_dma.phys_addr) {
+ sp->rx_dma.trans_len = SPRD_UART_RX_SIZE +
+ sp->rx_dma.phys_addr - sp->pos;
+ sprd_uart_dma_rx(port);
+ sp->rx_buf_tail += sp->rx_dma.trans_len;
+ }
+
+ if (sprd_start_dma_rx(port))
+ sprd_stop_rx(port);
+
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static int sprd_start_dma_rx(struct uart_port *port)
+{
+ struct sprd_uart_port *sp =
+ container_of(port, struct sprd_uart_port, port);
+ int ret;
+
+ if (!sp->rx_dma.enable)
+ return 0;
+
+ sp->pos = sp->rx_dma.phys_addr;
+ sp->rx_buf_tail = sp->rx_dma.virt;
+ sprd_rx_full_thld(port, SPRD_RX_FIFO_FULL);
+ ret = sprd_rx_dma_config(port, SPRD_RX_DMA_STEP);
+ if (ret)
+ return ret;
+
+ return sprd_uart_dma_submit(port, &sp->rx_dma, SPRD_UART_RX_SIZE,
+ DMA_DEV_TO_MEM, sprd_complete_rx_dma);
+}
+
+static void sprd_release_dma(struct uart_port *port)
+{
+ struct sprd_uart_port *sp =
+ container_of(port, struct sprd_uart_port, port);
+
+ sprd_uart_dma_enable(port, false);
+
+ if (sp->rx_dma.enable)
+ dma_release_channel(sp->rx_dma.chn);
+
+ if (sp->tx_dma.enable)
+ dma_release_channel(sp->tx_dma.chn);
+
+ sp->tx_dma.enable = false;
+ sp->rx_dma.enable = false;
+}
+
+static void sprd_request_dma(struct uart_port *port)
{
+ struct sprd_uart_port *sp =
+ container_of(port, struct sprd_uart_port, port);
+
+ sp->tx_dma.enable = true;
+ sp->rx_dma.enable = true;
+
+ sp->tx_dma.chn = dma_request_chan(port->dev, "tx");
+ if (IS_ERR(sp->tx_dma.chn)) {
+ dev_err(port->dev, "request TX DMA channel failed, ret = %ld\n",
+ PTR_ERR(sp->tx_dma.chn));
+ sp->tx_dma.enable = false;
+ }
+
+ sp->rx_dma.chn = dma_request_chan(port->dev, "rx");
+ if (IS_ERR(sp->rx_dma.chn)) {
+ dev_err(port->dev, "request RX DMA channel failed, ret = %ld\n",
+ PTR_ERR(sp->rx_dma.chn));
+ sp->rx_dma.enable = false;
+ }
+}
+
+static void sprd_stop_tx(struct uart_port *port)
+{
+ struct sprd_uart_port *sp = container_of(port, struct sprd_uart_port,
+ port);
unsigned int ien, iclr;
+ if (sp->tx_dma.enable) {
+ sprd_stop_tx_dma(port);
+ return;
+ }
+
iclr = serial_in(port, SPRD_ICLR);
ien = serial_in(port, SPRD_IEN);
- ien &= ~(SPRD_IEN_RX_FULL | SPRD_IEN_BREAK_DETECT);
- iclr |= SPRD_IEN_RX_FULL | SPRD_IEN_BREAK_DETECT;
+ iclr |= SPRD_IEN_TX_EMPTY;
+ ien &= ~SPRD_IEN_TX_EMPTY;
serial_out(port, SPRD_IEN, ien);
serial_out(port, SPRD_ICLR, iclr);
}
+static void sprd_start_tx(struct uart_port *port)
+{
+ struct sprd_uart_port *sp = container_of(port, struct sprd_uart_port,
+ port);
+ unsigned int ien;
+
+ if (sp->tx_dma.enable) {
+ sprd_start_tx_dma(port);
+ return;
+ }
+
+ ien = serial_in(port, SPRD_IEN);
+ if (!(ien & SPRD_IEN_TX_EMPTY)) {
+ ien |= SPRD_IEN_TX_EMPTY;
+ serial_out(port, SPRD_IEN, ien);
+ }
+}
+
/* The Sprd serial does not support this function. */
static void sprd_break_ctl(struct uart_port *port, int break_state)
{
@@ -218,9 +590,16 @@ static int handle_lsr_errors(struct uart_port *port,
static inline void sprd_rx(struct uart_port *port)
{
+ struct sprd_uart_port *sp = container_of(port, struct sprd_uart_port,
+ port);
struct tty_port *tty = &port->state->port;
unsigned int ch, flag, lsr, max_count = SPRD_TIMEOUT;
+ if (sp->rx_dma.enable) {
+ sprd_uart_dma_irq(port);
+ return;
+ }
+
while ((serial_in(port, SPRD_STS1) & SPRD_RX_FIFO_CNT_MASK) &&
max_count--) {
lsr = serial_in(port, SPRD_LSR);
@@ -304,6 +683,25 @@ static irqreturn_t sprd_handle_irq(int irq, void *dev_id)
return IRQ_HANDLED;
}
+static void sprd_uart_dma_startup(struct uart_port *port,
+ struct sprd_uart_port *sp)
+{
+ int ret;
+
+ sprd_request_dma(port);
+ if (!(sp->rx_dma.enable || sp->tx_dma.enable))
+ return;
+
+ ret = sprd_start_dma_rx(port);
+ if (ret) {
+ sp->rx_dma.enable = false;
+ dma_release_channel(sp->rx_dma.chn);
+ dev_warn(port->dev, "fail to start RX dma mode\n");
+ }
+
+ sprd_uart_dma_enable(port, true);
+}
+
static int sprd_startup(struct uart_port *port)
{
int ret = 0;
@@ -332,6 +730,9 @@ static int sprd_startup(struct uart_port *port)
/* allocate irq */
sp = container_of(port, struct sprd_uart_port, port);
snprintf(sp->name, sizeof(sp->name), "sprd_serial%d", port->line);
+
+ sprd_uart_dma_startup(port, sp);
+
ret = devm_request_irq(port->dev, port->irq, sprd_handle_irq,
IRQF_SHARED, sp->name, port);
if (ret) {
@@ -346,7 +747,9 @@ static int sprd_startup(struct uart_port *port)
/* enable interrupt */
spin_lock_irqsave(&port->lock, flags);
ien = serial_in(port, SPRD_IEN);
- ien |= SPRD_IEN_RX_FULL | SPRD_IEN_BREAK_DETECT | SPRD_IEN_TIMEOUT;
+ ien |= SPRD_IEN_BREAK_DETECT | SPRD_IEN_TIMEOUT;
+ if (!sp->rx_dma.enable)
+ ien |= SPRD_IEN_RX_FULL;
serial_out(port, SPRD_IEN, ien);
spin_unlock_irqrestore(&port->lock, flags);
@@ -355,6 +758,7 @@ static int sprd_startup(struct uart_port *port)
static void sprd_shutdown(struct uart_port *port)
{
+ sprd_release_dma(port);
serial_out(port, SPRD_IEN, 0);
serial_out(port, SPRD_ICLR, ~0);
devm_free_irq(port->dev, port->irq, port);
@@ -491,6 +895,22 @@ static int sprd_verify_port(struct uart_port *port, struct serial_struct *ser)
return 0;
}
+static void sprd_pm(struct uart_port *port, unsigned int state,
+ unsigned int oldstate)
+{
+ struct sprd_uart_port *sup =
+ container_of(port, struct sprd_uart_port, port);
+
+ switch (state) {
+ case UART_PM_STATE_ON:
+ clk_prepare_enable(sup->clk);
+ break;
+ case UART_PM_STATE_OFF:
+ clk_disable_unprepare(sup->clk);
+ break;
+ }
+}
+
static const struct uart_ops serial_sprd_ops = {
.tx_empty = sprd_tx_empty,
.get_mctrl = sprd_get_mctrl,
@@ -507,6 +927,7 @@ static const struct uart_ops serial_sprd_ops = {
.request_port = sprd_request_port,
.config_port = sprd_config_port,
.verify_port = sprd_verify_port,
+ .pm = sprd_pm,
};
#ifdef CONFIG_SERIAL_SPRD_CONSOLE
@@ -668,6 +1089,43 @@ static int sprd_remove(struct platform_device *dev)
if (!sprd_ports_num)
uart_unregister_driver(&sprd_uart_driver);
+ sprd_rx_free_buf(sup);
+
+ return 0;
+}
+
+static int sprd_clk_init(struct uart_port *uport)
+{
+ struct clk *clk_uart, *clk_parent;
+ struct sprd_uart_port *u = sprd_port[uport->line];
+
+ clk_uart = devm_clk_get(uport->dev, "uart");
+ if (IS_ERR(clk_uart)) {
+ dev_warn(uport->dev, "uart%d can't get uart clock\n",
+ uport->line);
+ clk_uart = NULL;
+ }
+
+ clk_parent = devm_clk_get(uport->dev, "source");
+ if (IS_ERR(clk_parent)) {
+ dev_warn(uport->dev, "uart%d can't get source clock\n",
+ uport->line);
+ clk_parent = NULL;
+ }
+
+ if (!clk_uart || clk_set_parent(clk_uart, clk_parent))
+ uport->uartclk = SPRD_DEFAULT_SOURCE_CLK;
+ else
+ uport->uartclk = clk_get_rate(clk_uart);
+
+ u->clk = devm_clk_get(uport->dev, "enable");
+ if (IS_ERR(u->clk)) {
+ if (PTR_ERR(u->clk) != -EPROBE_DEFER)
+ dev_err(uport->dev, "uart%d can't get enable clock\n",
+ uport->line);
+ return PTR_ERR(u->clk);
+ }
+
return 0;
}
@@ -675,7 +1133,6 @@ static int sprd_probe(struct platform_device *pdev)
{
struct resource *res;
struct uart_port *up;
- struct clk *clk;
int irq;
int index;
int ret;
@@ -704,9 +1161,9 @@ static int sprd_probe(struct platform_device *pdev)
up->ops = &serial_sprd_ops;
up->flags = UPF_BOOT_AUTOCONF;
- clk = devm_clk_get(&pdev->dev, NULL);
- if (!IS_ERR_OR_NULL(clk))
- up->uartclk = clk_get_rate(clk);
+ ret = sprd_clk_init(up);
+ if (ret)
+ return ret;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
up->membase = devm_ioremap_resource(&pdev->dev, res);
@@ -722,6 +1179,14 @@ static int sprd_probe(struct platform_device *pdev)
}
up->irq = irq;
+ /*
+ * Allocate one dma buffer to prepare for receive transfer, in case
+ * memory allocation failure at runtime.
+ */
+ ret = sprd_rx_alloc_buf(sprd_port[index]);
+ if (ret)
+ return ret;
+
if (!sprd_ports_num) {
ret = uart_register_driver(&sprd_uart_driver);
if (ret < 0) {
diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c
index fa0ce7dd9e24..59e82e6d776d 100644
--- a/drivers/tty/sysrq.c
+++ b/drivers/tty/sysrq.c
@@ -208,7 +208,7 @@ static struct sysrq_key_op sysrq_showlocks_op = {
#endif
#ifdef CONFIG_SMP
-static DEFINE_SPINLOCK(show_lock);
+static DEFINE_RAW_SPINLOCK(show_lock);
static void showacpu(void *dummy)
{
@@ -218,10 +218,10 @@ static void showacpu(void *dummy)
if (idle_cpu(smp_processor_id()))
return;
- spin_lock_irqsave(&show_lock, flags);
+ raw_spin_lock_irqsave(&show_lock, flags);
pr_info("CPU%d:\n", smp_processor_id());
show_stack(NULL, NULL);
- spin_unlock_irqrestore(&show_lock, flags);
+ raw_spin_unlock_irqrestore(&show_lock, flags);
}
static void sysrq_showregs_othercpus(struct work_struct *dummy)
diff --git a/drivers/tty/tty_jobctrl.c b/drivers/tty/tty_jobctrl.c
index c4ecd66fafef..f8ed50a16848 100644
--- a/drivers/tty/tty_jobctrl.c
+++ b/drivers/tty/tty_jobctrl.c
@@ -44,7 +44,7 @@ int __tty_check_change(struct tty_struct *tty, int sig)
tty_pgrp = tty->pgrp;
spin_unlock_irqrestore(&tty->ctrl_lock, flags);
- if (tty_pgrp && pgrp != tty->pgrp) {
+ if (tty_pgrp && pgrp != tty_pgrp) {
if (is_ignored(sig)) {
if (sig == SIGTTIN)
ret = -EIO;
@@ -313,7 +313,7 @@ void disassociate_ctty(int on_exit)
read_unlock(&tasklist_lock);
}
-/**
+/*
*
* no_tty - Ensure the current process does not have a controlling tty
*/
diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c
index a9e12b3bc31d..044c3cbdcfa4 100644
--- a/drivers/tty/tty_port.c
+++ b/drivers/tty/tty_port.c
@@ -325,7 +325,7 @@ static void tty_port_shutdown(struct tty_port *port, struct tty_struct *tty)
if (tty && C_HUPCL(tty))
tty_port_lower_dtr_rts(port);
- if (port->ops && port->ops->shutdown)
+ if (port->ops->shutdown)
port->ops->shutdown(port);
}
out:
@@ -398,7 +398,7 @@ EXPORT_SYMBOL_GPL(tty_port_tty_wakeup);
*/
int tty_port_carrier_raised(struct tty_port *port)
{
- if (!port->ops || !port->ops->carrier_raised)
+ if (port->ops->carrier_raised == NULL)
return 1;
return port->ops->carrier_raised(port);
}
@@ -414,7 +414,7 @@ EXPORT_SYMBOL(tty_port_carrier_raised);
*/
void tty_port_raise_dtr_rts(struct tty_port *port)
{
- if (port->ops && port->ops->dtr_rts)
+ if (port->ops->dtr_rts)
port->ops->dtr_rts(port, 1);
}
EXPORT_SYMBOL(tty_port_raise_dtr_rts);
@@ -429,7 +429,7 @@ EXPORT_SYMBOL(tty_port_raise_dtr_rts);
*/
void tty_port_lower_dtr_rts(struct tty_port *port)
{
- if (port->ops && port->ops->dtr_rts)
+ if (port->ops->dtr_rts)
port->ops->dtr_rts(port, 0);
}
EXPORT_SYMBOL(tty_port_lower_dtr_rts);
@@ -684,7 +684,7 @@ int tty_port_open(struct tty_port *port, struct tty_struct *tty,
if (!tty_port_initialized(port)) {
clear_bit(TTY_IO_ERROR, &tty->flags);
- if (port->ops && port->ops->activate) {
+ if (port->ops->activate) {
int retval = port->ops->activate(port, tty);
if (retval) {
mutex_unlock(&port->mutex);
diff --git a/drivers/tty/ttynull.c b/drivers/tty/ttynull.c
new file mode 100644
index 000000000000..17f05b7eb6d3
--- /dev/null
+++ b/drivers/tty/ttynull.c
@@ -0,0 +1,109 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 Axis Communications AB
+ *
+ * Based on ttyprintk.c:
+ * Copyright (C) 2010 Samo Pogacnik
+ */
+
+#include <linux/console.h>
+#include <linux/module.h>
+#include <linux/tty.h>
+
+static const struct tty_port_operations ttynull_port_ops;
+static struct tty_driver *ttynull_driver;
+static struct tty_port ttynull_port;
+
+static int ttynull_open(struct tty_struct *tty, struct file *filp)
+{
+ return tty_port_open(&ttynull_port, tty, filp);
+}
+
+static void ttynull_close(struct tty_struct *tty, struct file *filp)
+{
+ tty_port_close(&ttynull_port, tty, filp);
+}
+
+static void ttynull_hangup(struct tty_struct *tty)
+{
+ tty_port_hangup(&ttynull_port);
+}
+
+static int ttynull_write(struct tty_struct *tty, const unsigned char *buf,
+ int count)
+{
+ return count;
+}
+
+static int ttynull_write_room(struct tty_struct *tty)
+{
+ return 65536;
+}
+
+static const struct tty_operations ttynull_ops = {
+ .open = ttynull_open,
+ .close = ttynull_close,
+ .hangup = ttynull_hangup,
+ .write = ttynull_write,
+ .write_room = ttynull_write_room,
+};
+
+static struct tty_driver *ttynull_device(struct console *c, int *index)
+{
+ *index = 0;
+ return ttynull_driver;
+}
+
+static struct console ttynull_console = {
+ .name = "ttynull",
+ .device = ttynull_device,
+};
+
+static int __init ttynull_init(void)
+{
+ struct tty_driver *driver;
+ int ret;
+
+ driver = tty_alloc_driver(1,
+ TTY_DRIVER_RESET_TERMIOS |
+ TTY_DRIVER_REAL_RAW |
+ TTY_DRIVER_UNNUMBERED_NODE);
+ if (IS_ERR(driver))
+ return PTR_ERR(driver);
+
+ tty_port_init(&ttynull_port);
+ ttynull_port.ops = &ttynull_port_ops;
+
+ driver->driver_name = "ttynull";
+ driver->name = "ttynull";
+ driver->type = TTY_DRIVER_TYPE_CONSOLE;
+ driver->init_termios = tty_std_termios;
+ driver->init_termios.c_oflag = OPOST | OCRNL | ONOCR | ONLRET;
+ tty_set_operations(driver, &ttynull_ops);
+ tty_port_link_device(&ttynull_port, driver, 0);
+
+ ret = tty_register_driver(driver);
+ if (ret < 0) {
+ put_tty_driver(driver);
+ tty_port_destroy(&ttynull_port);
+ return ret;
+ }
+
+ ttynull_driver = driver;
+ register_console(&ttynull_console);
+
+ return 0;
+}
+
+static void __exit ttynull_exit(void)
+{
+ unregister_console(&ttynull_console);
+ tty_unregister_driver(ttynull_driver);
+ put_tty_driver(ttynull_driver);
+ tty_port_destroy(&ttynull_port);
+}
+
+module_init(ttynull_init);
+module_exit(ttynull_exit);
+
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/tty/vcc.c b/drivers/tty/vcc.c
index 58b454c34560..d2a1e1228c82 100644
--- a/drivers/tty/vcc.c
+++ b/drivers/tty/vcc.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/* vcc.c: sun4v virtual channel concentrator
*
* Copyright (C) 2017 Oracle. All rights reserved.
diff --git a/drivers/tty/vt/.gitignore b/drivers/tty/vt/.gitignore
index 83683a2d8e6a..9b38b85f9d9a 100644
--- a/drivers/tty/vt/.gitignore
+++ b/drivers/tty/vt/.gitignore
@@ -1,2 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
consolemap_deftbl.c
defkeymap.c
diff --git a/drivers/tty/vt/consolemap.c b/drivers/tty/vt/consolemap.c
index 7c7ada0b3ea0..b28aa0d289f8 100644
--- a/drivers/tty/vt/consolemap.c
+++ b/drivers/tty/vt/consolemap.c
@@ -542,7 +542,7 @@ int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair __user *list)
if (!ct)
return 0;
- unilist = memdup_user(list, ct * sizeof(struct unipair));
+ unilist = vmemdup_user(list, ct * sizeof(struct unipair));
if (IS_ERR(unilist))
return PTR_ERR(unilist);
@@ -641,7 +641,7 @@ int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair __user *list)
out_unlock:
console_unlock();
- kfree(unilist);
+ kvfree(unilist);
return err;
}
@@ -743,7 +743,7 @@ int con_get_unimap(struct vc_data *vc, ushort ct, ushort __user *uct, struct uni
struct uni_pagedir *p;
struct unipair *unilist;
- unilist = kmalloc_array(ct, sizeof(struct unipair), GFP_KERNEL);
+ unilist = kvmalloc_array(ct, sizeof(struct unipair), GFP_KERNEL);
if (!unilist)
return -ENOMEM;
@@ -775,7 +775,7 @@ int con_get_unimap(struct vc_data *vc, ushort ct, ushort __user *uct, struct uni
if (copy_to_user(list, unilist, min(ect, ct) * sizeof(struct unipair)))
ret = -EFAULT;
put_user(ect, uct);
- kfree(unilist);
+ kvfree(unilist);
return ret ? ret : (ect <= ct) ? 0 : -ENOMEM;
}
diff --git a/drivers/tty/vt/cp437.uni b/drivers/tty/vt/cp437.uni
index bc6163484f62..a1991904c559 100644
--- a/drivers/tty/vt/cp437.uni
+++ b/drivers/tty/vt/cp437.uni
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
#
# Unicode table for IBM Codepage 437. Note that there are many more
# substitutions that could be conceived (for example, thick-line
diff --git a/drivers/tty/vt/defkeymap.c_shipped b/drivers/tty/vt/defkeymap.c_shipped
index d2208dfe3f67..c7095fb7d2d1 100644
--- a/drivers/tty/vt/defkeymap.c_shipped
+++ b/drivers/tty/vt/defkeymap.c_shipped
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/* Do not edit this file! It was automatically generated by */
/* loadkeys --mktable defkeymap.map > defkeymap.c */
diff --git a/drivers/tty/vt/defkeymap.map b/drivers/tty/vt/defkeymap.map
index 50b30cace261..37f1ac6ddfb9 100644
--- a/drivers/tty/vt/defkeymap.map
+++ b/drivers/tty/vt/defkeymap.map
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
# Default kernel keymap. This uses 7 modifier combinations.
keymaps 0-2,4-5,8,12
# Change the above line into
diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c
index 88312c6c92cc..515fc095e3b4 100644
--- a/drivers/tty/vt/keyboard.c
+++ b/drivers/tty/vt/keyboard.c
@@ -123,6 +123,7 @@ static const int NR_TYPES = ARRAY_SIZE(max_vals);
static struct input_handler kbd_handler;
static DEFINE_SPINLOCK(kbd_event_lock);
static DEFINE_SPINLOCK(led_lock);
+static DEFINE_SPINLOCK(func_buf_lock); /* guard 'func_buf' and friends */
static unsigned long key_down[BITS_TO_LONGS(KEY_CNT)]; /* keyboard key bitmap */
static unsigned char shift_down[NR_SHIFT]; /* shift state counters.. */
static bool dead_key_next;
@@ -1449,7 +1450,7 @@ static void kbd_keycode(unsigned int keycode, int down, int hw_raw)
KBD_UNICODE, &param);
if (rc != NOTIFY_STOP)
if (down && !raw_mode)
- to_utf8(vc, keysym);
+ k_unicode(vc, keysym, !down);
return;
}
@@ -1990,11 +1991,12 @@ int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm)
char *p;
u_char *q;
u_char __user *up;
- int sz;
+ int sz, fnw_sz;
int delta;
char *first_free, *fj, *fnw;
int i, j, k;
int ret;
+ unsigned long flags;
if (!capable(CAP_SYS_TTY_CONFIG))
perm = 0;
@@ -2037,7 +2039,14 @@ int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm)
goto reterr;
}
+ fnw = NULL;
+ fnw_sz = 0;
+ /* race aginst other writers */
+ again:
+ spin_lock_irqsave(&func_buf_lock, flags);
q = func_table[i];
+
+ /* fj pointer to next entry after 'q' */
first_free = funcbufptr + (funcbufsize - funcbufleft);
for (j = i+1; j < MAX_NR_FUNC && !func_table[j]; j++)
;
@@ -2045,10 +2054,12 @@ int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm)
fj = func_table[j];
else
fj = first_free;
-
+ /* buffer usage increase by new entry */
delta = (q ? -strlen(q) : 1) + strlen(kbs->kb_string);
+
if (delta <= funcbufleft) { /* it fits in current buf */
if (j < MAX_NR_FUNC) {
+ /* make enough space for new entry at 'fj' */
memmove(fj + delta, fj, first_free - fj);
for (k = j; k < MAX_NR_FUNC; k++)
if (func_table[k])
@@ -2061,20 +2072,28 @@ int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm)
sz = 256;
while (sz < funcbufsize - funcbufleft + delta)
sz <<= 1;
- fnw = kmalloc(sz, GFP_KERNEL);
- if(!fnw) {
- ret = -ENOMEM;
- goto reterr;
+ if (fnw_sz != sz) {
+ spin_unlock_irqrestore(&func_buf_lock, flags);
+ kfree(fnw);
+ fnw = kmalloc(sz, GFP_KERNEL);
+ fnw_sz = sz;
+ if (!fnw) {
+ ret = -ENOMEM;
+ goto reterr;
+ }
+ goto again;
}
if (!q)
func_table[i] = fj;
+ /* copy data before insertion point to new location */
if (fj > funcbufptr)
memmove(fnw, funcbufptr, fj - funcbufptr);
for (k = 0; k < j; k++)
if (func_table[k])
func_table[k] = fnw + (func_table[k] - funcbufptr);
+ /* copy data after insertion point to new location */
if (first_free > fj) {
memmove(fnw + (fj - funcbufptr) + delta, fj, first_free - fj);
for (k = j; k < MAX_NR_FUNC; k++)
@@ -2087,7 +2106,9 @@ int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm)
funcbufleft = funcbufleft - delta + sz - funcbufsize;
funcbufsize = sz;
}
+ /* finally insert item itself */
strcpy(func_table[i], kbs->kb_string);
+ spin_unlock_irqrestore(&func_buf_lock, flags);
break;
}
ret = 0;
diff --git a/drivers/tty/vt/vc_screen.c b/drivers/tty/vt/vc_screen.c
index 160f46115aaa..1f042346e722 100644
--- a/drivers/tty/vt/vc_screen.c
+++ b/drivers/tty/vt/vc_screen.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Provide access to virtual console memory.
- * /dev/vcs0: the screen as it is being viewed right now (possibly scrolled)
+ * /dev/vcs: the screen as it is being viewed right now (possibly scrolled)
* /dev/vcsN: the screen of /dev/ttyN (1 <= N <= 63)
* [minor: N]
*
diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index 650c66886c80..693b3b4176f5 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -4179,8 +4179,6 @@ void do_blank_screen(int entering_gfx)
return;
}
- if (blank_state != blank_normal_wait)
- return;
blank_state = blank_off;
/* don't blank graphics */