summaryrefslogtreecommitdiff
path: root/drivers/tty
diff options
context:
space:
mode:
authorDmitry Torokhov <dmitry.torokhov@gmail.com>2017-11-13 17:10:13 -0800
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2017-11-13 17:10:13 -0800
commitc25141062a82ae8bddced1b3ce2b57a1c0efabe0 (patch)
tree105edf10059bc0c4f2f00338b0c861b813d1bb1a /drivers/tty
parent26dd633e437dca218547ccbeacc71fe8a620b6f6 (diff)
parentc1b433e04ef9c0a1c4d65bfe918472ffa334dff4 (diff)
Merge branch 'next' into for-linus
Prepare input updates for 4.15 merge window.
Diffstat (limited to 'drivers/tty')
-rw-r--r--drivers/tty/Kconfig8
-rw-r--r--drivers/tty/Makefile5
-rw-r--r--drivers/tty/amiserial.c23
-rw-r--r--drivers/tty/cyclades.c21
-rw-r--r--drivers/tty/ehv_bytechan.c2
-rw-r--r--drivers/tty/goldfish.c234
-rw-r--r--drivers/tty/hvc/Kconfig4
-rw-r--r--drivers/tty/hvc/Makefile1
-rw-r--r--drivers/tty/hvc/hvc_irq.c1
-rw-r--r--drivers/tty/hvc/hvc_iucv.c1
-rw-r--r--drivers/tty/hvc/hvc_opal.c16
-rw-r--r--drivers/tty/hvc/hvc_vio.c20
-rw-r--r--drivers/tty/hvc/hvcs.c14
-rw-r--r--drivers/tty/hvc/hvsi_lib.c1
-rw-r--r--drivers/tty/ipwireless/hardware.c1
-rw-r--r--drivers/tty/ipwireless/hardware.h1
-rw-r--r--drivers/tty/ipwireless/main.h1
-rw-r--r--drivers/tty/ipwireless/network.c3
-rw-r--r--drivers/tty/ipwireless/network.h1
-rw-r--r--drivers/tty/ipwireless/setup_protocol.h1
-rw-r--r--drivers/tty/ipwireless/tty.c1
-rw-r--r--drivers/tty/ipwireless/tty.h1
-rw-r--r--drivers/tty/isicom.c2
-rw-r--r--drivers/tty/mips_ejtag_fdc.c2
-rw-r--r--drivers/tty/moxa.c2
-rw-r--r--drivers/tty/moxa.h1
-rw-r--r--drivers/tty/mxser.c18
-rw-r--r--drivers/tty/mxser.h1
-rw-r--r--drivers/tty/n_gsm.c67
-rw-r--r--drivers/tty/n_null.c80
-rw-r--r--drivers/tty/pty.c85
-rw-r--r--drivers/tty/rocket.c27
-rw-r--r--drivers/tty/rocket.h1
-rw-r--r--drivers/tty/rocket_int.h1
-rw-r--r--drivers/tty/serdev/core.c12
-rw-r--r--drivers/tty/serdev/serdev-ttyport.c2
-rw-r--r--drivers/tty/serial/21285.c2
-rw-r--r--drivers/tty/serial/8250/8250.h3
-rw-r--r--drivers/tty/serial/8250/8250_aspeed_vuart.c326
-rw-r--r--drivers/tty/serial/8250/8250_bcm2835aux.c2
-rw-r--r--drivers/tty/serial/8250/8250_core.c16
-rw-r--r--drivers/tty/serial/8250/8250_dw.c2
-rw-r--r--drivers/tty/serial/8250/8250_early.c8
-rw-r--r--drivers/tty/serial/8250/8250_exar.c191
-rw-r--r--drivers/tty/serial/8250/8250_gsc.c8
-rw-r--r--drivers/tty/serial/8250/8250_ingenic.c8
-rw-r--r--drivers/tty/serial/8250/8250_men_mcb.c118
-rw-r--r--drivers/tty/serial/8250/8250_mtk.c5
-rw-r--r--drivers/tty/serial/8250/8250_of.c66
-rw-r--r--drivers/tty/serial/8250/8250_omap.c27
-rw-r--r--drivers/tty/serial/8250/8250_pci.c43
-rw-r--r--drivers/tty/serial/8250/8250_port.c93
-rw-r--r--drivers/tty/serial/8250/8250_uniphier.c63
-rw-r--r--drivers/tty/serial/8250/Kconfig21
-rw-r--r--drivers/tty/serial/8250/Makefile3
-rw-r--r--drivers/tty/serial/Kconfig35
-rw-r--r--drivers/tty/serial/Makefile2
-rw-r--r--drivers/tty/serial/amba-pl010.c33
-rw-r--r--drivers/tty/serial/amba-pl011.c43
-rw-r--r--drivers/tty/serial/amba-pl011.h1
-rw-r--r--drivers/tty/serial/apbuart.c2
-rw-r--r--drivers/tty/serial/apbuart.h1
-rw-r--r--drivers/tty/serial/arc_uart.c4
-rw-r--r--drivers/tty/serial/atmel_serial.c164
-rw-r--r--drivers/tty/serial/bcm63xx_uart.c5
-rw-r--r--drivers/tty/serial/cpm_uart/cpm_uart_core.c2
-rw-r--r--drivers/tty/serial/cpm_uart/cpm_uart_cpm1.h1
-rw-r--r--drivers/tty/serial/cpm_uart/cpm_uart_cpm2.h1
-rw-r--r--drivers/tty/serial/crisv10.c1
-rw-r--r--drivers/tty/serial/crisv10.h1
-rw-r--r--drivers/tty/serial/dz.h1
-rw-r--r--drivers/tty/serial/earlycon.c7
-rw-r--r--drivers/tty/serial/etraxfs-uart.c1
-rw-r--r--drivers/tty/serial/fsl_lpuart.c406
-rw-r--r--drivers/tty/serial/imx.c51
-rw-r--r--drivers/tty/serial/ioc3_serial.c4
-rw-r--r--drivers/tty/serial/ioc4_serial.c4
-rw-r--r--drivers/tty/serial/ip22zilog.h1
-rw-r--r--drivers/tty/serial/jsm/jsm_driver.c2
-rw-r--r--drivers/tty/serial/m32r_sio.c2
-rw-r--r--drivers/tty/serial/meson_uart.c155
-rw-r--r--drivers/tty/serial/mpc52xx_uart.c14
-rw-r--r--drivers/tty/serial/mpsc.c10
-rw-r--r--drivers/tty/serial/msm_serial.c19
-rw-r--r--drivers/tty/serial/mux.c16
-rw-r--r--drivers/tty/serial/omap-serial.c13
-rw-r--r--drivers/tty/serial/owl-uart.c766
-rw-r--r--drivers/tty/serial/pch_uart.c43
-rw-r--r--drivers/tty/serial/pmac_zilog.c4
-rw-r--r--drivers/tty/serial/pmac_zilog.h1
-rw-r--r--drivers/tty/serial/sccnxp.c26
-rw-r--r--drivers/tty/serial/serial-tegra.c2
-rw-r--r--drivers/tty/serial/serial_core.c52
-rw-r--r--drivers/tty/serial/sh-sci.c60
-rw-r--r--drivers/tty/serial/sh-sci.h1
-rw-r--r--drivers/tty/serial/sirfsoc_uart.c2
-rw-r--r--drivers/tty/serial/sprd_serial.c8
-rw-r--r--drivers/tty/serial/st-asc.c3
-rw-r--r--drivers/tty/serial/stm32-usart.c125
-rw-r--r--drivers/tty/serial/stm32-usart.h37
-rw-r--r--drivers/tty/serial/sunsab.c2
-rw-r--r--drivers/tty/serial/sunsab.h1
-rw-r--r--drivers/tty/serial/sunsu.c6
-rw-r--r--drivers/tty/serial/sunzilog.h1
-rw-r--r--drivers/tty/serial/ucc_uart.c2
-rw-r--r--drivers/tty/serial/xilinx_uartps.c12
-rw-r--r--drivers/tty/serial/zs.h1
-rw-r--r--drivers/tty/synclink.c4
-rw-r--r--drivers/tty/synclink_gt.c6
-rw-r--r--drivers/tty/synclinkmp.c4
-rw-r--r--drivers/tty/sysrq.c1
-rw-r--r--drivers/tty/tty_buffer.c26
-rw-r--r--drivers/tty/tty_io.c169
-rw-r--r--drivers/tty/tty_ldisc.c57
-rw-r--r--drivers/tty/tty_mutex.c1
-rw-r--r--drivers/tty/vcc.c1155
-rw-r--r--drivers/tty/vt/Makefile1
-rw-r--r--drivers/tty/vt/consolemap.c56
-rw-r--r--drivers/tty/vt/keyboard.c3
-rw-r--r--drivers/tty/vt/selection.c1
-rw-r--r--drivers/tty/vt/vc_screen.c1
-rw-r--r--drivers/tty/vt/vt.c8
-rw-r--r--drivers/tty/vt/vt_ioctl.c9
123 files changed, 4458 insertions, 868 deletions
diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig
index 95103054c0e4..cc2b4d9433ed 100644
--- a/drivers/tty/Kconfig
+++ b/drivers/tty/Kconfig
@@ -392,6 +392,9 @@ config PPC_EARLY_DEBUG_EHV_BC_HANDLE
config GOLDFISH_TTY
tristate "Goldfish TTY Driver"
depends on GOLDFISH
+ select SERIAL_CORE
+ select SERIAL_CORE_CONSOLE
+ select SERIAL_EARLYCON
help
Console and system TTY driver for the Goldfish virtual platform.
@@ -455,4 +458,9 @@ config MIPS_EJTAG_FDC_KGDB_CHAN
help
FDC channel number to use for KGDB.
+config VCC
+ tristate "Sun Virtual Console Concentrator"
+ depends on SUN_LDOMS
+ help
+ Support for Sun logical domain consoles.
endif # TTY
diff --git a/drivers/tty/Makefile b/drivers/tty/Makefile
index f02becdb3e33..8ce3a8661b31 100644
--- a/drivers/tty/Makefile
+++ b/drivers/tty/Makefile
@@ -1,6 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_TTY) += tty_io.o n_tty.o tty_ioctl.o tty_ldisc.o \
tty_buffer.o tty_port.o tty_mutex.o \
- tty_ldsem.o tty_baudrate.o tty_jobctrl.o
+ tty_ldsem.o tty_baudrate.o tty_jobctrl.o \
+ n_null.o
obj-$(CONFIG_LEGACY_PTYS) += pty.o
obj-$(CONFIG_UNIX98_PTYS) += pty.o
obj-$(CONFIG_AUDIT) += tty_audit.o
@@ -32,5 +34,6 @@ obj-$(CONFIG_PPC_EPAPR_HV_BYTECHAN) += ehv_bytechan.o
obj-$(CONFIG_GOLDFISH_TTY) += goldfish.o
obj-$(CONFIG_DA_TTY) += metag_da.o
obj-$(CONFIG_MIPS_EJTAG_FDC_TTY) += mips_ejtag_fdc.o
+obj-$(CONFIG_VCC) += vcc.o
obj-y += ipwireless/
diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c
index dea16bb8c46a..9820e20993db 100644
--- a/drivers/tty/amiserial.c
+++ b/drivers/tty/amiserial.c
@@ -570,18 +570,6 @@ static int startup(struct tty_struct *tty, struct serial_state *info)
info->xmit.head = info->xmit.tail = 0;
/*
- * Set up the tty->alt_speed kludge
- */
- if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
- tty->alt_speed = 57600;
- if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
- tty->alt_speed = 115200;
- if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
- tty->alt_speed = 230400;
- if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
- tty->alt_speed = 460800;
-
- /*
* and set the speed of the serial port
*/
change_speed(tty, info, NULL);
@@ -1084,14 +1072,9 @@ static int set_serial_info(struct tty_struct *tty, struct serial_state *state,
check_and_exit:
if (tty_port_initialized(port)) {
if (change_spd) {
- if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
- tty->alt_speed = 57600;
- if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
- tty->alt_speed = 115200;
- if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
- tty->alt_speed = 230400;
- if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
- tty->alt_speed = 460800;
+ /* warn about deprecation unless clearing */
+ if (new_serial.flags & ASYNC_SPD_MASK)
+ dev_warn_ratelimited(tty->dev, "use of SPD flags is deprecated\n");
change_speed(tty, state, NULL);
}
} else
diff --git a/drivers/tty/cyclades.c b/drivers/tty/cyclades.c
index 104f09c58163..d272bc4e7fb5 100644
--- a/drivers/tty/cyclades.c
+++ b/drivers/tty/cyclades.c
@@ -1975,18 +1975,6 @@ static void cy_set_line_char(struct cyclades_port *info, struct tty_struct *tty)
cflag = tty->termios.c_cflag;
iflag = tty->termios.c_iflag;
- /*
- * Set up the tty->alt_speed kludge
- */
- if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
- tty->alt_speed = 57600;
- if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
- tty->alt_speed = 115200;
- if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
- tty->alt_speed = 230400;
- if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
- tty->alt_speed = 460800;
-
card = info->card;
channel = info->line - card->first_line;
@@ -2295,12 +2283,16 @@ cy_set_serial_info(struct cyclades_port *info, struct tty_struct *tty,
struct serial_struct __user *new_info)
{
struct serial_struct new_serial;
+ int old_flags;
int ret;
if (copy_from_user(&new_serial, new_info, sizeof(new_serial)))
return -EFAULT;
mutex_lock(&info->port.mutex);
+
+ old_flags = info->port.flags;
+
if (!capable(CAP_SYS_ADMIN)) {
if (new_serial.close_delay != info->port.close_delay ||
new_serial.baud_base != info->baud ||
@@ -2332,6 +2324,11 @@ cy_set_serial_info(struct cyclades_port *info, struct tty_struct *tty,
check_and_exit:
if (tty_port_initialized(&info->port)) {
+ if ((new_serial.flags ^ old_flags) & ASYNC_SPD_MASK) {
+ /* warn about deprecation unless clearing */
+ if (new_serial.flags & ASYNC_SPD_MASK)
+ dev_warn_ratelimited(tty->dev, "use of SPD flags is deprecated\n");
+ }
cy_set_line_char(info, tty);
ret = 0;
} else {
diff --git a/drivers/tty/ehv_bytechan.c b/drivers/tty/ehv_bytechan.c
index 61fe8d6fd24e..a1c7125cb968 100644
--- a/drivers/tty/ehv_bytechan.c
+++ b/drivers/tty/ehv_bytechan.c
@@ -122,7 +122,7 @@ static int find_console_handle(void)
stdout_irq = irq_of_parse_and_map(np, 0);
if (stdout_irq == NO_IRQ) {
- pr_err("ehv-bc: no 'interrupts' property in %s node\n", np->full_name);
+ pr_err("ehv-bc: no 'interrupts' property in %pOF node\n", np);
return 0;
}
diff --git a/drivers/tty/goldfish.c b/drivers/tty/goldfish.c
index 996bd473dd03..381e981dee06 100644
--- a/drivers/tty/goldfish.c
+++ b/drivers/tty/goldfish.c
@@ -1,6 +1,7 @@
/*
* Copyright (C) 2007 Google, Inc.
* Copyright (C) 2012 Intel, Inc.
+ * Copyright (C) 2017 Imagination Technologies Ltd.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -22,21 +23,23 @@
#include <linux/io.h>
#include <linux/module.h>
#include <linux/goldfish.h>
-
-enum {
- GOLDFISH_TTY_PUT_CHAR = 0x00,
- GOLDFISH_TTY_BYTES_READY = 0x04,
- GOLDFISH_TTY_CMD = 0x08,
-
- GOLDFISH_TTY_DATA_PTR = 0x10,
- GOLDFISH_TTY_DATA_LEN = 0x14,
- GOLDFISH_TTY_DATA_PTR_HIGH = 0x18,
-
- GOLDFISH_TTY_CMD_INT_DISABLE = 0,
- GOLDFISH_TTY_CMD_INT_ENABLE = 1,
- GOLDFISH_TTY_CMD_WRITE_BUFFER = 2,
- GOLDFISH_TTY_CMD_READ_BUFFER = 3,
-};
+#include <linux/mm.h>
+#include <linux/dma-mapping.h>
+#include <linux/serial_core.h>
+
+/* Goldfish tty register's offsets */
+#define GOLDFISH_TTY_REG_BYTES_READY 0x04
+#define GOLDFISH_TTY_REG_CMD 0x08
+#define GOLDFISH_TTY_REG_DATA_PTR 0x10
+#define GOLDFISH_TTY_REG_DATA_LEN 0x14
+#define GOLDFISH_TTY_REG_DATA_PTR_HIGH 0x18
+#define GOLDFISH_TTY_REG_VERSION 0x20
+
+/* Goldfish tty commands */
+#define GOLDFISH_TTY_CMD_INT_DISABLE 0
+#define GOLDFISH_TTY_CMD_INT_ENABLE 1
+#define GOLDFISH_TTY_CMD_WRITE_BUFFER 2
+#define GOLDFISH_TTY_CMD_READ_BUFFER 3
struct goldfish_tty {
struct tty_port port;
@@ -45,6 +48,8 @@ struct goldfish_tty {
u32 irq;
int opencount;
struct console console;
+ u32 version;
+ struct device *dev;
};
static DEFINE_MUTEX(goldfish_tty_lock);
@@ -53,38 +58,107 @@ static u32 goldfish_tty_line_count = 8;
static u32 goldfish_tty_current_line_count;
static struct goldfish_tty *goldfish_ttys;
-static void goldfish_tty_do_write(int line, const char *buf, unsigned count)
+static void do_rw_io(struct goldfish_tty *qtty,
+ unsigned long address,
+ unsigned int count,
+ int is_write)
{
unsigned long irq_flags;
- struct goldfish_tty *qtty = &goldfish_ttys[line];
void __iomem *base = qtty->base;
+
spin_lock_irqsave(&qtty->lock, irq_flags);
- gf_write_ptr(buf, base + GOLDFISH_TTY_DATA_PTR,
- base + GOLDFISH_TTY_DATA_PTR_HIGH);
- writel(count, base + GOLDFISH_TTY_DATA_LEN);
- writel(GOLDFISH_TTY_CMD_WRITE_BUFFER, base + GOLDFISH_TTY_CMD);
+ gf_write_ptr((void *)address, base + GOLDFISH_TTY_REG_DATA_PTR,
+ base + GOLDFISH_TTY_REG_DATA_PTR_HIGH);
+ writel(count, base + GOLDFISH_TTY_REG_DATA_LEN);
+
+ if (is_write)
+ writel(GOLDFISH_TTY_CMD_WRITE_BUFFER,
+ base + GOLDFISH_TTY_REG_CMD);
+ else
+ writel(GOLDFISH_TTY_CMD_READ_BUFFER,
+ base + GOLDFISH_TTY_REG_CMD);
+
spin_unlock_irqrestore(&qtty->lock, irq_flags);
}
+static void goldfish_tty_rw(struct goldfish_tty *qtty,
+ unsigned long addr,
+ unsigned int count,
+ int is_write)
+{
+ dma_addr_t dma_handle;
+ enum dma_data_direction dma_dir;
+
+ dma_dir = (is_write ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+ if (qtty->version > 0) {
+ /*
+ * Goldfish TTY for Ranchu platform uses
+ * physical addresses and DMA for read/write operations
+ */
+ unsigned long addr_end = addr + count;
+
+ while (addr < addr_end) {
+ unsigned long pg_end = (addr & PAGE_MASK) + PAGE_SIZE;
+ unsigned long next =
+ pg_end < addr_end ? pg_end : addr_end;
+ unsigned long avail = next - addr;
+
+ /*
+ * Map the buffer's virtual address to the DMA address
+ * so the buffer can be accessed by the device.
+ */
+ dma_handle = dma_map_single(qtty->dev, (void *)addr,
+ avail, dma_dir);
+
+ if (dma_mapping_error(qtty->dev, dma_handle)) {
+ dev_err(qtty->dev, "tty: DMA mapping error.\n");
+ return;
+ }
+ do_rw_io(qtty, dma_handle, avail, is_write);
+
+ /*
+ * Unmap the previously mapped region after
+ * the completion of the read/write operation.
+ */
+ dma_unmap_single(qtty->dev, dma_handle, avail, dma_dir);
+
+ addr += avail;
+ }
+ } else {
+ /*
+ * Old style Goldfish TTY used on the Goldfish platform
+ * uses virtual addresses.
+ */
+ do_rw_io(qtty, addr, count, is_write);
+ }
+}
+
+static void goldfish_tty_do_write(int line, const char *buf,
+ unsigned int count)
+{
+ struct goldfish_tty *qtty = &goldfish_ttys[line];
+ unsigned long address = (unsigned long)(void *)buf;
+
+ goldfish_tty_rw(qtty, address, count, 1);
+}
+
static irqreturn_t goldfish_tty_interrupt(int irq, void *dev_id)
{
struct goldfish_tty *qtty = dev_id;
void __iomem *base = qtty->base;
- unsigned long irq_flags;
+ unsigned long address;
unsigned char *buf;
u32 count;
- count = readl(base + GOLDFISH_TTY_BYTES_READY);
+ count = readl(base + GOLDFISH_TTY_REG_BYTES_READY);
if (count == 0)
return IRQ_NONE;
count = tty_prepare_flip_string(&qtty->port, &buf, count);
- spin_lock_irqsave(&qtty->lock, irq_flags);
- gf_write_ptr(buf, base + GOLDFISH_TTY_DATA_PTR,
- base + GOLDFISH_TTY_DATA_PTR_HIGH);
- writel(count, base + GOLDFISH_TTY_DATA_LEN);
- writel(GOLDFISH_TTY_CMD_READ_BUFFER, base + GOLDFISH_TTY_CMD);
- spin_unlock_irqrestore(&qtty->lock, irq_flags);
+
+ address = (unsigned long)(void *)buf;
+ goldfish_tty_rw(qtty, address, count, 0);
+
tty_schedule_flip(&qtty->port);
return IRQ_HANDLED;
}
@@ -93,7 +167,7 @@ static int goldfish_tty_activate(struct tty_port *port, struct tty_struct *tty)
{
struct goldfish_tty *qtty = container_of(port, struct goldfish_tty,
port);
- writel(GOLDFISH_TTY_CMD_INT_ENABLE, qtty->base + GOLDFISH_TTY_CMD);
+ writel(GOLDFISH_TTY_CMD_INT_ENABLE, qtty->base + GOLDFISH_TTY_REG_CMD);
return 0;
}
@@ -101,7 +175,7 @@ static void goldfish_tty_shutdown(struct tty_port *port)
{
struct goldfish_tty *qtty = container_of(port, struct goldfish_tty,
port);
- writel(GOLDFISH_TTY_CMD_INT_DISABLE, qtty->base + GOLDFISH_TTY_CMD);
+ writel(GOLDFISH_TTY_CMD_INT_DISABLE, qtty->base + GOLDFISH_TTY_REG_CMD);
}
static int goldfish_tty_open(struct tty_struct *tty, struct file *filp)
@@ -136,7 +210,7 @@ static int goldfish_tty_chars_in_buffer(struct tty_struct *tty)
{
struct goldfish_tty *qtty = &goldfish_ttys[tty->index];
void __iomem *base = qtty->base;
- return readl(base + GOLDFISH_TTY_BYTES_READY);
+ return readl(base + GOLDFISH_TTY_REG_BYTES_READY);
}
static void goldfish_tty_console_write(struct console *co, const char *b,
@@ -227,7 +301,7 @@ static void goldfish_tty_delete_driver(void)
static int goldfish_tty_probe(struct platform_device *pdev)
{
struct goldfish_tty *qtty;
- int ret = -EINVAL;
+ int ret = -ENODEV;
struct resource *r;
struct device *ttydev;
void __iomem *base;
@@ -235,16 +309,22 @@ static int goldfish_tty_probe(struct platform_device *pdev)
unsigned int line;
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (r == NULL)
- return -EINVAL;
+ if (!r) {
+ pr_err("goldfish_tty: No MEM resource available!\n");
+ return -ENOMEM;
+ }
base = ioremap(r->start, 0x1000);
- if (base == NULL)
- pr_err("goldfish_tty: unable to remap base\n");
+ if (!base) {
+ pr_err("goldfish_tty: Unable to ioremap base!\n");
+ return -ENOMEM;
+ }
r = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (r == NULL)
+ if (!r) {
+ pr_err("goldfish_tty: No IRQ resource available!\n");
goto err_unmap;
+ }
irq = r->start;
@@ -255,13 +335,17 @@ static int goldfish_tty_probe(struct platform_device *pdev)
else
line = pdev->id;
- if (line >= goldfish_tty_line_count)
- goto err_create_driver_failed;
+ if (line >= goldfish_tty_line_count) {
+ pr_err("goldfish_tty: Reached maximum tty number of %d.\n",
+ goldfish_tty_current_line_count);
+ ret = -ENOMEM;
+ goto err_unlock;
+ }
if (goldfish_tty_current_line_count == 0) {
ret = goldfish_tty_create_driver();
if (ret)
- goto err_create_driver_failed;
+ goto err_unlock;
}
goldfish_tty_current_line_count++;
@@ -271,17 +355,45 @@ static int goldfish_tty_probe(struct platform_device *pdev)
qtty->port.ops = &goldfish_port_ops;
qtty->base = base;
qtty->irq = irq;
+ qtty->dev = &pdev->dev;
+
+ /*
+ * Goldfish TTY device used by the Goldfish emulator
+ * should identify itself with 0, forcing the driver
+ * to use virtual addresses. Goldfish TTY device
+ * on Ranchu emulator (qemu2) returns 1 here and
+ * driver will use physical addresses.
+ */
+ qtty->version = readl(base + GOLDFISH_TTY_REG_VERSION);
+
+ /*
+ * Goldfish TTY device on Ranchu emulator (qemu2)
+ * will use DMA for read/write IO operations.
+ */
+ if (qtty->version > 0) {
+ /*
+ * Initialize dma_mask to 32-bits.
+ */
+ if (!pdev->dev.dma_mask)
+ pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask;
+ ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
+ if (ret) {
+ dev_err(&pdev->dev, "No suitable DMA available.\n");
+ goto err_dec_line_count;
+ }
+ }
- writel(GOLDFISH_TTY_CMD_INT_DISABLE, base + GOLDFISH_TTY_CMD);
+ writel(GOLDFISH_TTY_CMD_INT_DISABLE, base + GOLDFISH_TTY_REG_CMD);
ret = request_irq(irq, goldfish_tty_interrupt, IRQF_SHARED,
- "goldfish_tty", qtty);
- if (ret)
- goto err_request_irq_failed;
-
+ "goldfish_tty", qtty);
+ if (ret) {
+ pr_err("goldfish_tty: No IRQ available!\n");
+ goto err_dec_line_count;
+ }
ttydev = tty_port_register_device(&qtty->port, goldfish_tty_driver,
- line, &pdev->dev);
+ line, &pdev->dev);
if (IS_ERR(ttydev)) {
ret = PTR_ERR(ttydev);
goto err_tty_register_device_failed;
@@ -301,11 +413,11 @@ static int goldfish_tty_probe(struct platform_device *pdev)
err_tty_register_device_failed:
free_irq(irq, qtty);
-err_request_irq_failed:
+err_dec_line_count:
goldfish_tty_current_line_count--;
if (goldfish_tty_current_line_count == 0)
goldfish_tty_delete_driver();
-err_create_driver_failed:
+err_unlock:
mutex_unlock(&goldfish_tty_lock);
err_unmap:
iounmap(base);
@@ -330,6 +442,30 @@ static int goldfish_tty_remove(struct platform_device *pdev)
return 0;
}
+static void gf_early_console_putchar(struct uart_port *port, int ch)
+{
+ __raw_writel(ch, port->membase);
+}
+
+static void gf_early_write(struct console *con, const char *s, unsigned int n)
+{
+ struct earlycon_device *dev = con->data;
+
+ uart_console_write(&dev->port, s, n, gf_early_console_putchar);
+}
+
+static int __init gf_earlycon_setup(struct earlycon_device *device,
+ const char *opt)
+{
+ if (!device->port.membase)
+ return -ENODEV;
+
+ device->con->write = gf_early_write;
+ return 0;
+}
+
+OF_EARLYCON_DECLARE(early_gf_tty, "google,goldfish-tty", gf_earlycon_setup);
+
static const struct of_device_id goldfish_tty_of_match[] = {
{ .compatible = "google,goldfish-tty", },
{},
diff --git a/drivers/tty/hvc/Kconfig b/drivers/tty/hvc/Kconfig
index 574da15fe618..fec457edad14 100644
--- a/drivers/tty/hvc/Kconfig
+++ b/drivers/tty/hvc/Kconfig
@@ -4,7 +4,7 @@ config HVC_DRIVER
bool
help
Generic "hypervisor virtual console" infrastructure for various
- hypervisors (pSeries, iSeries, Xen, lguest).
+ hypervisors (pSeries, iSeries, Xen).
It will automatically be selected if one of the back-end console drivers
is selected.
@@ -44,7 +44,7 @@ config HVC_RTAS
config HVC_IUCV
bool "z/VM IUCV Hypervisor console support (VM only)"
- depends on S390
+ depends on S390 && NET
select HVC_DRIVER
select IUCV
default y
diff --git a/drivers/tty/hvc/Makefile b/drivers/tty/hvc/Makefile
index 6a2702be76d1..0b02ec7f1dfd 100644
--- a/drivers/tty/hvc/Makefile
+++ b/drivers/tty/hvc/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_HVC_CONSOLE) += hvc_vio.o hvsi_lib.o
obj-$(CONFIG_HVC_OPAL) += hvc_opal.o hvsi_lib.o
obj-$(CONFIG_HVC_OLD_HVSI) += hvsi.o
diff --git a/drivers/tty/hvc/hvc_irq.c b/drivers/tty/hvc/hvc_irq.c
index bc7a96874637..4b255dfef2cc 100644
--- a/drivers/tty/hvc/hvc_irq.c
+++ b/drivers/tty/hvc/hvc_irq.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright IBM Corp. 2001,2008
*
diff --git a/drivers/tty/hvc/hvc_iucv.c b/drivers/tty/hvc/hvc_iucv.c
index 8b70a1627356..a74680729825 100644
--- a/drivers/tty/hvc/hvc_iucv.c
+++ b/drivers/tty/hvc/hvc_iucv.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* z/VM IUCV hypervisor console (HVC) device driver
*
diff --git a/drivers/tty/hvc/hvc_opal.c b/drivers/tty/hvc/hvc_opal.c
index 510799311099..16331a90c1e8 100644
--- a/drivers/tty/hvc/hvc_opal.c
+++ b/drivers/tty/hvc/hvc_opal.c
@@ -179,8 +179,8 @@ static int hvc_opal_probe(struct platform_device *dev)
proto = HV_PROTOCOL_HVSI;
ops = &hvc_opal_hvsi_ops;
} else {
- pr_err("hvc_opal: Unknown protocol for %s\n",
- dev->dev.of_node->full_name);
+ pr_err("hvc_opal: Unknown protocol for %pOF\n",
+ dev->dev.of_node);
return -ENXIO;
}
@@ -204,14 +204,14 @@ static int hvc_opal_probe(struct platform_device *dev)
/* Instanciate now to establish a mapping index==vtermno */
hvc_instantiate(termno, termno, ops);
} else {
- pr_err("hvc_opal: Device %s has duplicate terminal number #%d\n",
- dev->dev.of_node->full_name, termno);
+ pr_err("hvc_opal: Device %pOF has duplicate terminal number #%d\n",
+ dev->dev.of_node, termno);
return -ENXIO;
}
- pr_info("hvc%d: %s protocol on %s%s\n", termno,
+ pr_info("hvc%d: %s protocol on %pOF%s\n", termno,
proto == HV_PROTOCOL_RAW ? "raw" : "hvsi",
- dev->dev.of_node->full_name,
+ dev->dev.of_node,
boot ? " (boot console)" : "");
irq = irq_of_parse_and_map(dev->dev.of_node, 0);
@@ -222,8 +222,8 @@ static int hvc_opal_probe(struct platform_device *dev)
}
if (!irq) {
- pr_err("hvc_opal: Unable to map interrupt for device %s\n",
- dev->dev.of_node->full_name);
+ pr_err("hvc_opal: Unable to map interrupt for device %pOF\n",
+ dev->dev.of_node);
return irq;
}
diff --git a/drivers/tty/hvc/hvc_vio.c b/drivers/tty/hvc/hvc_vio.c
index b05dc5086627..a1d272ac82bb 100644
--- a/drivers/tty/hvc/hvc_vio.c
+++ b/drivers/tty/hvc/hvc_vio.c
@@ -53,7 +53,7 @@
static const char hvc_driver_name[] = "hvc_console";
-static struct vio_device_id hvc_driver_table[] = {
+static const struct vio_device_id hvc_driver_table[] = {
{"serial", "hvterm1"},
#ifndef HVC_OLD_HVSI
{"serial", "hvterm-protocol"},
@@ -312,12 +312,12 @@ static int hvc_vio_probe(struct vio_dev *vdev,
proto = HV_PROTOCOL_HVSI;
ops = &hvterm_hvsi_ops;
} else {
- pr_err("hvc_vio: Unknown protocol for %s\n", vdev->dev.of_node->full_name);
+ pr_err("hvc_vio: Unknown protocol for %pOF\n", vdev->dev.of_node);
return -ENXIO;
}
- pr_devel("hvc_vio_probe() device %s, using %s protocol\n",
- vdev->dev.of_node->full_name,
+ pr_devel("hvc_vio_probe() device %pOF, using %s protocol\n",
+ vdev->dev.of_node,
proto == HV_PROTOCOL_RAW ? "raw" : "hvsi");
/* Is it our boot one ? */
@@ -442,6 +442,14 @@ void __init hvc_vio_init_early(void)
#ifdef CONFIG_PPC_EARLY_DEBUG_LPAR
void __init udbg_init_debug_lpar(void)
{
+ /*
+ * If we're running as a hypervisor then we definitely can't call the
+ * hypervisor to print debug output (we *are* the hypervisor), so don't
+ * register if we detect that MSR_HV=1.
+ */
+ if (mfmsr() & MSR_HV)
+ return;
+
hvterm_privs[0] = &hvterm_priv0;
hvterm_priv0.termno = 0;
hvterm_priv0.proto = HV_PROTOCOL_RAW;
@@ -455,6 +463,10 @@ void __init udbg_init_debug_lpar(void)
#ifdef CONFIG_PPC_EARLY_DEBUG_LPAR_HVSI
void __init udbg_init_debug_lpar_hvsi(void)
{
+ /* See comment above in udbg_init_debug_lpar() */
+ if (mfmsr() & MSR_HV)
+ return;
+
hvterm_privs[0] = &hvterm_priv0;
hvterm_priv0.termno = CONFIG_PPC_EARLY_DEBUG_HVSI_VTERMNO;
hvterm_priv0.proto = HV_PROTOCOL_HVSI;
diff --git a/drivers/tty/hvc/hvcs.c b/drivers/tty/hvc/hvcs.c
index 99bb875178d7..63c29fe9d21f 100644
--- a/drivers/tty/hvc/hvcs.c
+++ b/drivers/tty/hvc/hvcs.c
@@ -189,7 +189,7 @@ MODULE_VERSION(HVCS_DRIVER_VERSION);
* that will cause echoing or we'll go into recursive loop echoing chars back
* and forth with the console drivers.
*/
-static struct ktermios hvcs_tty_termios = {
+static const struct ktermios hvcs_tty_termios = {
.c_iflag = IGNBRK | IGNPAR,
.c_oflag = OPOST,
.c_cflag = B38400 | CS8 | CREAD | HUPCL,
@@ -484,13 +484,13 @@ static struct attribute_group hvcs_attr_group = {
.attrs = hvcs_attrs,
};
-static ssize_t hvcs_rescan_show(struct device_driver *ddp, char *buf)
+static ssize_t rescan_show(struct device_driver *ddp, char *buf)
{
/* A 1 means it is updating, a 0 means it is done updating */
return snprintf(buf, PAGE_SIZE, "%d\n", hvcs_rescan_status);
}
-static ssize_t hvcs_rescan_store(struct device_driver *ddp, const char * buf,
+static ssize_t rescan_store(struct device_driver *ddp, const char * buf,
size_t count)
{
if ((simple_strtol(buf, NULL, 0) != 1)
@@ -505,8 +505,7 @@ static ssize_t hvcs_rescan_store(struct device_driver *ddp, const char * buf,
return count;
}
-static DRIVER_ATTR(rescan,
- S_IRUGO | S_IWUSR, hvcs_rescan_show, hvcs_rescan_store);
+static DRIVER_ATTR_RW(rescan);
static void hvcs_kick(void)
{
@@ -676,7 +675,7 @@ static int khvcsd(void *unused)
return 0;
}
-static struct vio_device_id hvcs_driver_table[] = {
+static const struct vio_device_id hvcs_driver_table[] = {
{"serial-server", "hvterm2"},
{ "", "" }
};
@@ -1242,8 +1241,7 @@ static void hvcs_close(struct tty_struct *tty, struct file *filp)
free_irq(irq, hvcsd);
return;
} else if (hvcsd->port.count < 0) {
- printk(KERN_ERR "HVCS: vty-server@%X open_count: %d"
- " is missmanaged.\n",
+ printk(KERN_ERR "HVCS: vty-server@%X open_count: %d is mismanaged.\n",
hvcsd->vdev->unit_address, hvcsd->port.count);
}
diff --git a/drivers/tty/hvc/hvsi_lib.c b/drivers/tty/hvc/hvsi_lib.c
index a270f04588d7..09289c8154ae 100644
--- a/drivers/tty/hvc/hvsi_lib.c
+++ b/drivers/tty/hvc/hvsi_lib.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/slab.h>
diff --git a/drivers/tty/ipwireless/hardware.c b/drivers/tty/ipwireless/hardware.c
index df0204b6148f..a6b8240af6cd 100644
--- a/drivers/tty/ipwireless/hardware.c
+++ b/drivers/tty/ipwireless/hardware.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* IPWireless 3G PCMCIA Network Driver
*
diff --git a/drivers/tty/ipwireless/hardware.h b/drivers/tty/ipwireless/hardware.h
index 90a8590e43b0..e524a8fcc2ad 100644
--- a/drivers/tty/ipwireless/hardware.h
+++ b/drivers/tty/ipwireless/hardware.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* IPWireless 3G PCMCIA Network Driver
*
diff --git a/drivers/tty/ipwireless/main.h b/drivers/tty/ipwireless/main.h
index f2cbb116bccb..73818bb64416 100644
--- a/drivers/tty/ipwireless/main.h
+++ b/drivers/tty/ipwireless/main.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* IPWireless 3G PCMCIA Network Driver
*
diff --git a/drivers/tty/ipwireless/network.c b/drivers/tty/ipwireless/network.c
index c0dfb642383b..695439c03147 100644
--- a/drivers/tty/ipwireless/network.c
+++ b/drivers/tty/ipwireless/network.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* IPWireless 3G PCMCIA Network Driver
*
@@ -355,7 +356,7 @@ static struct sk_buff *ipw_packet_received_skb(unsigned char *data,
if (skb == NULL)
return NULL;
skb_reserve(skb, 2);
- memcpy(skb_put(skb, length), data, length);
+ skb_put_data(skb, data, length);
return skb;
}
diff --git a/drivers/tty/ipwireless/network.h b/drivers/tty/ipwireless/network.h
index 561f765b3334..784932a59a73 100644
--- a/drivers/tty/ipwireless/network.h
+++ b/drivers/tty/ipwireless/network.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* IPWireless 3G PCMCIA Network Driver
*
diff --git a/drivers/tty/ipwireless/setup_protocol.h b/drivers/tty/ipwireless/setup_protocol.h
index 002c34e72521..d4a7ae257ca5 100644
--- a/drivers/tty/ipwireless/setup_protocol.h
+++ b/drivers/tty/ipwireless/setup_protocol.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* IPWireless 3G PCMCIA Network Driver
*
diff --git a/drivers/tty/ipwireless/tty.c b/drivers/tty/ipwireless/tty.c
index 2685d59d2724..1ef751c27ac6 100644
--- a/drivers/tty/ipwireless/tty.c
+++ b/drivers/tty/ipwireless/tty.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* IPWireless 3G PCMCIA Network Driver
*
diff --git a/drivers/tty/ipwireless/tty.h b/drivers/tty/ipwireless/tty.h
index 747b2d637860..ec698d9f338b 100644
--- a/drivers/tty/ipwireless/tty.h
+++ b/drivers/tty/ipwireless/tty.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* IPWireless 3G PCMCIA Network Driver
*
diff --git a/drivers/tty/isicom.c b/drivers/tty/isicom.c
index b70187b46d9d..61ecdd6b2fc2 100644
--- a/drivers/tty/isicom.c
+++ b/drivers/tty/isicom.c
@@ -150,7 +150,7 @@
static int isicom_probe(struct pci_dev *, const struct pci_device_id *);
static void isicom_remove(struct pci_dev *);
-static struct pci_device_id isicom_pci_tbl[] = {
+static const struct pci_device_id isicom_pci_tbl[] = {
{ PCI_DEVICE(VENDOR_ID, 0x2028) },
{ PCI_DEVICE(VENDOR_ID, 0x2051) },
{ PCI_DEVICE(VENDOR_ID, 0x2052) },
diff --git a/drivers/tty/mips_ejtag_fdc.c b/drivers/tty/mips_ejtag_fdc.c
index 234123b0c642..a2dab3fb8751 100644
--- a/drivers/tty/mips_ejtag_fdc.c
+++ b/drivers/tty/mips_ejtag_fdc.c
@@ -1110,7 +1110,7 @@ out:
return ret;
}
-static struct mips_cdmm_device_id mips_ejtag_fdc_tty_ids[] = {
+static const struct mips_cdmm_device_id mips_ejtag_fdc_tty_ids[] = {
{ .type = 0xfd },
{ }
};
diff --git a/drivers/tty/moxa.c b/drivers/tty/moxa.c
index 3b251f4e5df0..7f3d4cb0341b 100644
--- a/drivers/tty/moxa.c
+++ b/drivers/tty/moxa.c
@@ -88,7 +88,7 @@ static char *moxa_brdname[] =
};
#ifdef CONFIG_PCI
-static struct pci_device_id moxa_pcibrds[] = {
+static const struct pci_device_id moxa_pcibrds[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_C218),
.driver_data = MOXA_BOARD_C218_PCI },
{ PCI_DEVICE(PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_C320),
diff --git a/drivers/tty/moxa.h b/drivers/tty/moxa.h
index 87d16ce57be7..8ce89fd36c7b 100644
--- a/drivers/tty/moxa.h
+++ b/drivers/tty/moxa.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef MOXA_H_FILE
#define MOXA_H_FILE
diff --git a/drivers/tty/mxser.c b/drivers/tty/mxser.c
index 8bd6fb6d9391..7dd38047ba23 100644
--- a/drivers/tty/mxser.c
+++ b/drivers/tty/mxser.c
@@ -145,7 +145,7 @@ static const struct mxser_cardinfo mxser_cards[] = {
/* driver_data correspond to the lines in the structure above
see also ISA probe function before you change something */
-static struct pci_device_id mxser_pcibrds[] = {
+static const struct pci_device_id mxser_pcibrds[] = {
{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_C168), .driver_data = 3 },
{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_C104), .driver_data = 4 },
{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP132), .driver_data = 8 },
@@ -246,11 +246,11 @@ struct mxser_port {
unsigned char err_shadow;
struct async_icount icount; /* kernel counters for 4 input interrupts */
- int timeout;
+ unsigned int timeout;
int read_status_mask;
int ignore_status_mask;
- int xmit_fifo_size;
+ unsigned int xmit_fifo_size;
int xmit_head;
int xmit_tail;
int xmit_cnt;
@@ -572,8 +572,9 @@ static void mxser_dtr_rts(struct tty_port *port, int on)
static int mxser_set_baud(struct tty_struct *tty, long newspd)
{
struct mxser_port *info = tty->driver_data;
- int quot = 0, baud;
+ unsigned int quot = 0, baud;
unsigned char cval;
+ u64 timeout;
if (!info->ioaddr)
return -1;
@@ -594,8 +595,13 @@ static int mxser_set_baud(struct tty_struct *tty, long newspd)
quot = 0;
}
- info->timeout = ((info->xmit_fifo_size * HZ * 10 * quot) / info->baud_base);
- info->timeout += HZ / 50; /* Add .02 seconds of slop */
+ /*
+ * worst case (128 * 1000 * 10 * 18432) needs 35 bits, so divide in the
+ * u64 domain
+ */
+ timeout = (u64)info->xmit_fifo_size * HZ * 10 * quot;
+ do_div(timeout, info->baud_base);
+ info->timeout = timeout + HZ / 50; /* Add .02 seconds of slop */
if (quot) {
info->MCR |= UART_MCR_DTR;
diff --git a/drivers/tty/mxser.h b/drivers/tty/mxser.h
index 0bf794313ffd..e6cb15626567 100644
--- a/drivers/tty/mxser.h
+++ b/drivers/tty/mxser.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _MXSER_H
#define _MXSER_H
diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c
index 2667a205a5ab..0a3c9665e015 100644
--- a/drivers/tty/n_gsm.c
+++ b/drivers/tty/n_gsm.c
@@ -2015,6 +2015,33 @@ static void gsm_error(struct gsm_mux *gsm,
gsm->io_error++;
}
+static int gsm_disconnect(struct gsm_mux *gsm)
+{
+ struct gsm_dlci *dlci = gsm->dlci[0];
+ struct gsm_control *gc;
+
+ if (!dlci)
+ return 0;
+
+ /* In theory disconnecting DLCI 0 is sufficient but for some
+ modems this is apparently not the case. */
+ gc = gsm_control_send(gsm, CMD_CLD, NULL, 0);
+ if (gc)
+ gsm_control_wait(gsm, gc);
+
+ del_timer_sync(&gsm->t2_timer);
+ /* Now we are sure T2 has stopped */
+
+ gsm_dlci_begin_close(dlci);
+ wait_event_interruptible(gsm->event,
+ dlci->state == DLCI_CLOSED);
+
+ if (signal_pending(current))
+ return -EINTR;
+
+ return 0;
+}
+
/**
* gsm_cleanup_mux - generic GSM protocol cleanup
* @gsm: our mux
@@ -2029,7 +2056,6 @@ static void gsm_cleanup_mux(struct gsm_mux *gsm)
int i;
struct gsm_dlci *dlci = gsm->dlci[0];
struct gsm_msg *txq, *ntxq;
- struct gsm_control *gc;
gsm->dead = 1;
@@ -2045,21 +2071,11 @@ static void gsm_cleanup_mux(struct gsm_mux *gsm)
if (i == MAX_MUX)
return;
- /* In theory disconnecting DLCI 0 is sufficient but for some
- modems this is apparently not the case. */
- if (dlci) {
- gc = gsm_control_send(gsm, CMD_CLD, NULL, 0);
- if (gc)
- gsm_control_wait(gsm, gc);
- }
del_timer_sync(&gsm->t2_timer);
/* Now we are sure T2 has stopped */
- if (dlci) {
+ if (dlci)
dlci->dead = 1;
- gsm_dlci_begin_close(dlci);
- wait_event_interruptible(gsm->event,
- dlci->state == DLCI_CLOSED);
- }
+
/* Free up any link layer users */
mutex_lock(&gsm->mutex);
for (i = 0; i < NUM_DLCI; i++)
@@ -2519,12 +2535,12 @@ static int gsmld_config(struct tty_struct *tty, struct gsm_mux *gsm,
*/
if (need_close || need_restart) {
- gsm_dlci_begin_close(gsm->dlci[0]);
- /* This will timeout if the link is down due to N2 expiring */
- wait_event_interruptible(gsm->event,
- gsm->dlci[0]->state == DLCI_CLOSED);
- if (signal_pending(current))
- return -EINTR;
+ int ret;
+
+ ret = gsm_disconnect(gsm);
+
+ if (ret)
+ return ret;
}
if (need_restart)
gsm_cleanup_mux(gsm);
@@ -2591,6 +2607,14 @@ static int gsmld_ioctl(struct tty_struct *tty, struct file *file,
}
}
+#ifdef CONFIG_COMPAT
+static long gsmld_compat_ioctl(struct tty_struct *tty, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return gsmld_ioctl(tty, file, cmd, arg);
+}
+#endif
+
/*
* Network interface
*
@@ -2688,7 +2712,7 @@ static void gsm_mux_rx_netchar(struct gsm_dlci *dlci,
return;
}
skb_reserve(skb, NET_IP_ALIGN);
- memcpy(skb_put(skb, size), in_buf, size);
+ skb_put_data(skb, in_buf, size);
skb->dev = net;
skb->protocol = htons(ETH_P_IP);
@@ -2802,6 +2826,9 @@ static struct tty_ldisc_ops tty_ldisc_packet = {
.flush_buffer = gsmld_flush_buffer,
.read = gsmld_read,
.write = gsmld_write,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = gsmld_compat_ioctl,
+#endif
.ioctl = gsmld_ioctl,
.poll = gsmld_poll,
.receive_buf = gsmld_receive_buf,
diff --git a/drivers/tty/n_null.c b/drivers/tty/n_null.c
new file mode 100644
index 000000000000..d63261c36e42
--- /dev/null
+++ b/drivers/tty/n_null.c
@@ -0,0 +1,80 @@
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/tty.h>
+#include <linux/module.h>
+
+/*
+ * n_null.c - Null line discipline used in the failure path
+ *
+ * Copyright (C) Intel 2017
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+static int n_null_open(struct tty_struct *tty)
+{
+ return 0;
+}
+
+static void n_null_close(struct tty_struct *tty)
+{
+}
+
+static ssize_t n_null_read(struct tty_struct *tty, struct file *file,
+ unsigned char __user * buf, size_t nr)
+{
+ return -EOPNOTSUPP;
+}
+
+static ssize_t n_null_write(struct tty_struct *tty, struct file *file,
+ const unsigned char *buf, size_t nr)
+{
+ return -EOPNOTSUPP;
+}
+
+static void n_null_receivebuf(struct tty_struct *tty,
+ const unsigned char *cp, char *fp,
+ int cnt)
+{
+}
+
+static struct tty_ldisc_ops null_ldisc = {
+ .owner = THIS_MODULE,
+ .magic = TTY_LDISC_MAGIC,
+ .name = "n_null",
+ .open = n_null_open,
+ .close = n_null_close,
+ .read = n_null_read,
+ .write = n_null_write,
+ .receive_buf = n_null_receivebuf
+};
+
+static int __init n_null_init(void)
+{
+ BUG_ON(tty_register_ldisc(N_NULL, &null_ldisc));
+ return 0;
+}
+
+static void __exit n_null_exit(void)
+{
+ tty_unregister_ldisc(N_NULL);
+}
+
+module_init(n_null_init);
+module_exit(n_null_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Alan Cox");
+MODULE_ALIAS_LDISC(N_NULL);
+MODULE_DESCRIPTION("Null ldisc driver");
diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c
index 65799575c666..64338442050e 100644
--- a/drivers/tty/pty.c
+++ b/drivers/tty/pty.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 1991, 1992 Linus Torvalds
*
@@ -24,6 +25,9 @@
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/poll.h>
+#include <linux/mount.h>
+#include <linux/file.h>
+#include <linux/ioctl.h>
#undef TTY_DEBUG_HANGUP
#ifdef TTY_DEBUG_HANGUP
@@ -481,6 +485,16 @@ static int pty_bsd_ioctl(struct tty_struct *tty,
return -ENOIOCTLCMD;
}
+static long pty_bsd_compat_ioctl(struct tty_struct *tty,
+ unsigned int cmd, unsigned long arg)
+{
+ /*
+ * PTY ioctls don't require any special translation between 32-bit and
+ * 64-bit userspace, they are already compatible.
+ */
+ return pty_bsd_ioctl(tty, cmd, arg);
+}
+
static int legacy_count = CONFIG_LEGACY_PTY_COUNT;
/*
* not really modular, but the easiest way to keep compat with existing
@@ -502,6 +516,7 @@ static const struct tty_operations master_pty_ops_bsd = {
.chars_in_buffer = pty_chars_in_buffer,
.unthrottle = pty_unthrottle,
.ioctl = pty_bsd_ioctl,
+ .compat_ioctl = pty_bsd_compat_ioctl,
.cleanup = pty_cleanup,
.resize = pty_resize,
.remove = pty_remove
@@ -585,9 +600,58 @@ static inline void legacy_pty_init(void) { }
/* Unix98 devices */
#ifdef CONFIG_UNIX98_PTYS
-
static struct cdev ptmx_cdev;
+/**
+ * ptm_open_peer - open the peer of a pty
+ * @master: the open struct file of the ptmx device node
+ * @tty: the master of the pty being opened
+ * @flags: the flags for open
+ *
+ * Provide a race free way for userspace to open the slave end of a pty
+ * (where they have the master fd and cannot access or trust the mount
+ * namespace /dev/pts was mounted inside).
+ */
+int ptm_open_peer(struct file *master, struct tty_struct *tty, int flags)
+{
+ int fd = -1;
+ struct file *filp;
+ int retval = -EINVAL;
+ struct path path;
+
+ if (tty->driver != ptm_driver)
+ return -EIO;
+
+ fd = get_unused_fd_flags(0);
+ if (fd < 0) {
+ retval = fd;
+ goto err;
+ }
+
+ /* Compute the slave's path */
+ path.mnt = devpts_mntget(master, tty->driver_data);
+ if (IS_ERR(path.mnt)) {
+ retval = PTR_ERR(path.mnt);
+ goto err_put;
+ }
+ path.dentry = tty->link->driver_data;
+
+ filp = dentry_open(&path, flags, current_cred());
+ mntput(path.mnt);
+ if (IS_ERR(filp)) {
+ retval = PTR_ERR(filp);
+ goto err_put;
+ }
+
+ fd_install(fd, filp);
+ return fd;
+
+err_put:
+ put_unused_fd(fd);
+err:
+ return retval;
+}
+
static int pty_unix98_ioctl(struct tty_struct *tty,
unsigned int cmd, unsigned long arg)
{
@@ -609,6 +673,16 @@ static int pty_unix98_ioctl(struct tty_struct *tty,
return -ENOIOCTLCMD;
}
+static long pty_unix98_compat_ioctl(struct tty_struct *tty,
+ unsigned int cmd, unsigned long arg)
+{
+ /*
+ * PTY ioctls don't require any special translation between 32-bit and
+ * 64-bit userspace, they are already compatible.
+ */
+ return pty_unix98_ioctl(tty, cmd, arg);
+}
+
/**
* ptm_unix98_lookup - find a pty master
* @driver: ptm driver
@@ -669,6 +743,11 @@ static void pty_unix98_remove(struct tty_driver *driver, struct tty_struct *tty)
}
}
+static void pty_show_fdinfo(struct tty_struct *tty, struct seq_file *m)
+{
+ seq_printf(m, "tty-index:\t%d\n", tty->index);
+}
+
static const struct tty_operations ptm_unix98_ops = {
.lookup = ptm_unix98_lookup,
.install = pty_unix98_install,
@@ -681,8 +760,10 @@ static const struct tty_operations ptm_unix98_ops = {
.chars_in_buffer = pty_chars_in_buffer,
.unthrottle = pty_unthrottle,
.ioctl = pty_unix98_ioctl,
+ .compat_ioctl = pty_unix98_compat_ioctl,
.resize = pty_resize,
- .cleanup = pty_cleanup
+ .cleanup = pty_cleanup,
+ .show_fdinfo = pty_show_fdinfo,
};
static const struct tty_operations pty_unix98_ops = {
diff --git a/drivers/tty/rocket.c b/drivers/tty/rocket.c
index b51a877da986..20d79a6007d5 100644
--- a/drivers/tty/rocket.c
+++ b/drivers/tty/rocket.c
@@ -947,18 +947,6 @@ static int rp_open(struct tty_struct *tty, struct file *filp)
tty_port_set_initialized(&info->port, 1);
- /*
- * Set up the tty->alt_speed kludge
- */
- if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_HI)
- tty->alt_speed = 57600;
- if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_VHI)
- tty->alt_speed = 115200;
- if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_SHI)
- tty->alt_speed = 230400;
- if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_WARP)
- tty->alt_speed = 460800;
-
configure_r_port(tty, info, NULL);
if (C_BAUD(tty)) {
sSetDTR(cp);
@@ -1219,23 +1207,20 @@ static int set_config(struct tty_struct *tty, struct r_port *info,
return -EPERM;
}
info->flags = ((info->flags & ~ROCKET_USR_MASK) | (new_serial.flags & ROCKET_USR_MASK));
- configure_r_port(tty, info, NULL);
mutex_unlock(&info->port.mutex);
return 0;
}
+ if ((new_serial.flags ^ info->flags) & ROCKET_SPD_MASK) {
+ /* warn about deprecation, unless clearing */
+ if (new_serial.flags & ROCKET_SPD_MASK)
+ dev_warn_ratelimited(tty->dev, "use of SPD flags is deprecated\n");
+ }
+
info->flags = ((info->flags & ~ROCKET_FLAGS) | (new_serial.flags & ROCKET_FLAGS));
info->port.close_delay = new_serial.close_delay;
info->port.closing_wait = new_serial.closing_wait;
- if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_HI)
- tty->alt_speed = 57600;
- if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_VHI)
- tty->alt_speed = 115200;
- if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_SHI)
- tty->alt_speed = 230400;
- if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_WARP)
- tty->alt_speed = 460800;
mutex_unlock(&info->port.mutex);
configure_r_port(tty, info, NULL);
diff --git a/drivers/tty/rocket.h b/drivers/tty/rocket.h
index c11a9392f219..d0560203f215 100644
--- a/drivers/tty/rocket.h
+++ b/drivers/tty/rocket.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* rocket.h --- the exported interface of the rocket driver to its configuration program.
*
diff --git a/drivers/tty/rocket_int.h b/drivers/tty/rocket_int.h
index ef1e1be6b26d..727e50dbb92f 100644
--- a/drivers/tty/rocket_int.h
+++ b/drivers/tty/rocket_int.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* rocket_int.h --- internal header file for rocket.c
*
diff --git a/drivers/tty/serdev/core.c b/drivers/tty/serdev/core.c
index f71b47334149..c68fb3a8ea1c 100644
--- a/drivers/tty/serdev/core.c
+++ b/drivers/tty/serdev/core.c
@@ -262,11 +262,13 @@ static ssize_t modalias_show(struct device *dev,
{
return of_device_modalias(dev, buf, PAGE_SIZE);
}
+DEVICE_ATTR_RO(modalias);
-static struct device_attribute serdev_device_attrs[] = {
- __ATTR_RO(modalias),
- __ATTR_NULL
+static struct attribute *serdev_device_attrs[] = {
+ &dev_attr_modalias.attr,
+ NULL,
};
+ATTRIBUTE_GROUPS(serdev_device);
static struct bus_type serdev_bus_type = {
.name = "serial",
@@ -274,7 +276,7 @@ static struct bus_type serdev_bus_type = {
.probe = serdev_drv_probe,
.remove = serdev_drv_remove,
.uevent = serdev_uevent,
- .dev_attrs = serdev_device_attrs,
+ .dev_groups = serdev_device_groups,
};
/**
@@ -361,7 +363,7 @@ static int of_serdev_register_devices(struct serdev_controller *ctrl)
if (!of_get_property(node, "compatible", NULL))
continue;
- dev_dbg(&ctrl->dev, "adding child %s\n", node->full_name);
+ dev_dbg(&ctrl->dev, "adding child %pOF\n", node);
serdev = serdev_device_alloc(ctrl);
if (!serdev)
diff --git a/drivers/tty/serdev/serdev-ttyport.c b/drivers/tty/serdev/serdev-ttyport.c
index d0a021c93986..302018d67efa 100644
--- a/drivers/tty/serdev/serdev-ttyport.c
+++ b/drivers/tty/serdev/serdev-ttyport.c
@@ -148,7 +148,7 @@ static unsigned int ttyport_set_baudrate(struct serdev_controller *ctrl, unsigne
/* tty_set_termios() return not checked as it is always 0 */
tty_set_termios(tty, &ktermios);
- return speed;
+ return ktermios.c_ospeed;
}
static void ttyport_set_flow_control(struct serdev_controller *ctrl, bool enable)
diff --git a/drivers/tty/serial/21285.c b/drivers/tty/serial/21285.c
index 9b208bd686e6..804632b4a929 100644
--- a/drivers/tty/serial/21285.c
+++ b/drivers/tty/serial/21285.c
@@ -334,7 +334,7 @@ static int serial21285_verify_port(struct uart_port *port, struct serial_struct
return ret;
}
-static struct uart_ops serial21285_ops = {
+static const struct uart_ops serial21285_ops = {
.tx_empty = serial21285_tx_empty,
.get_mctrl = serial21285_get_mctrl,
.set_mctrl = serial21285_set_mctrl,
diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h
index ce8d4ffcc425..b2bdc35f7495 100644
--- a/drivers/tty/serial/8250/8250.h
+++ b/drivers/tty/serial/8250/8250.h
@@ -81,6 +81,9 @@ struct serial8250_config {
#define UART_CAP_HFIFO (1 << 14) /* UART has a "hidden" FIFO */
#define UART_CAP_RPM (1 << 15) /* Runtime PM is active while idle */
#define UART_CAP_IRDA (1 << 16) /* UART supports IrDA line discipline */
+#define UART_CAP_MINI (1 << 17) /* Mini UART on BCM283X family lacks:
+ * STOP PARITY EPAR SPAR WLEN5 WLEN6
+ */
#define UART_BUG_QUOT (1 << 0) /* UART has buggy quot LSB */
#define UART_BUG_TXEN (1 << 1) /* UART has buggy TX IIR status */
diff --git a/drivers/tty/serial/8250/8250_aspeed_vuart.c b/drivers/tty/serial/8250/8250_aspeed_vuart.c
new file mode 100644
index 000000000000..33a801353114
--- /dev/null
+++ b/drivers/tty/serial/8250/8250_aspeed_vuart.c
@@ -0,0 +1,326 @@
+/*
+ * Serial Port driver for Aspeed VUART device
+ *
+ * Copyright (C) 2016 Jeremy Kerr <jk@ozlabs.org>, IBM Corp.
+ * Copyright (C) 2006 Arnd Bergmann <arnd@arndb.de>, IBM Corp.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/clk.h>
+
+#include "8250.h"
+
+#define ASPEED_VUART_GCRA 0x20
+#define ASPEED_VUART_GCRA_VUART_EN BIT(0)
+#define ASPEED_VUART_GCRA_DISABLE_HOST_TX_DISCARD BIT(5)
+#define ASPEED_VUART_GCRB 0x24
+#define ASPEED_VUART_GCRB_HOST_SIRQ_MASK GENMASK(7, 4)
+#define ASPEED_VUART_GCRB_HOST_SIRQ_SHIFT 4
+#define ASPEED_VUART_ADDRL 0x28
+#define ASPEED_VUART_ADDRH 0x2c
+
+struct aspeed_vuart {
+ struct device *dev;
+ void __iomem *regs;
+ struct clk *clk;
+ int line;
+};
+
+/*
+ * The VUART is basically two UART 'front ends' connected by their FIFO
+ * (no actual serial line in between). One is on the BMC side (management
+ * controller) and one is on the host CPU side.
+ *
+ * It allows the BMC to provide to the host a "UART" that pipes into
+ * the BMC itself and can then be turned by the BMC into a network console
+ * of some sort for example.
+ *
+ * This driver is for the BMC side. The sysfs files allow the BMC
+ * userspace which owns the system configuration policy, to specify
+ * at what IO port and interrupt number the host side will appear
+ * to the host on the Host <-> BMC LPC bus. It could be different on a
+ * different system (though most of them use 3f8/4).
+ */
+
+static ssize_t lpc_address_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct aspeed_vuart *vuart = dev_get_drvdata(dev);
+ u16 addr;
+
+ addr = (readb(vuart->regs + ASPEED_VUART_ADDRH) << 8) |
+ (readb(vuart->regs + ASPEED_VUART_ADDRL));
+
+ return snprintf(buf, PAGE_SIZE - 1, "0x%x\n", addr);
+}
+
+static ssize_t lpc_address_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct aspeed_vuart *vuart = dev_get_drvdata(dev);
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 0, &val);
+ if (err)
+ return err;
+
+ writeb(val >> 8, vuart->regs + ASPEED_VUART_ADDRH);
+ writeb(val >> 0, vuart->regs + ASPEED_VUART_ADDRL);
+
+ return count;
+}
+
+static DEVICE_ATTR_RW(lpc_address);
+
+static ssize_t sirq_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct aspeed_vuart *vuart = dev_get_drvdata(dev);
+ u8 reg;
+
+ reg = readb(vuart->regs + ASPEED_VUART_GCRB);
+ reg &= ASPEED_VUART_GCRB_HOST_SIRQ_MASK;
+ reg >>= ASPEED_VUART_GCRB_HOST_SIRQ_SHIFT;
+
+ return snprintf(buf, PAGE_SIZE - 1, "%u\n", reg);
+}
+
+static ssize_t sirq_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct aspeed_vuart *vuart = dev_get_drvdata(dev);
+ unsigned long val;
+ int err;
+ u8 reg;
+
+ err = kstrtoul(buf, 0, &val);
+ if (err)
+ return err;
+
+ val <<= ASPEED_VUART_GCRB_HOST_SIRQ_SHIFT;
+ val &= ASPEED_VUART_GCRB_HOST_SIRQ_MASK;
+
+ reg = readb(vuart->regs + ASPEED_VUART_GCRB);
+ reg &= ~ASPEED_VUART_GCRB_HOST_SIRQ_MASK;
+ reg |= val;
+ writeb(reg, vuart->regs + ASPEED_VUART_GCRB);
+
+ return count;
+}
+
+static DEVICE_ATTR_RW(sirq);
+
+static struct attribute *aspeed_vuart_attrs[] = {
+ &dev_attr_sirq.attr,
+ &dev_attr_lpc_address.attr,
+ NULL,
+};
+
+static const struct attribute_group aspeed_vuart_attr_group = {
+ .attrs = aspeed_vuart_attrs,
+};
+
+static void aspeed_vuart_set_enabled(struct aspeed_vuart *vuart, bool enabled)
+{
+ u8 reg = readb(vuart->regs + ASPEED_VUART_GCRA);
+
+ if (enabled)
+ reg |= ASPEED_VUART_GCRA_VUART_EN;
+ else
+ reg &= ~ASPEED_VUART_GCRA_VUART_EN;
+
+ writeb(reg, vuart->regs + ASPEED_VUART_GCRA);
+}
+
+static void aspeed_vuart_set_host_tx_discard(struct aspeed_vuart *vuart,
+ bool discard)
+{
+ u8 reg;
+
+ reg = readb(vuart->regs + ASPEED_VUART_GCRA);
+
+ /* If the DISABLE_HOST_TX_DISCARD bit is set, discard is disabled */
+ if (!discard)
+ reg |= ASPEED_VUART_GCRA_DISABLE_HOST_TX_DISCARD;
+ else
+ reg &= ~ASPEED_VUART_GCRA_DISABLE_HOST_TX_DISCARD;
+
+ writeb(reg, vuart->regs + ASPEED_VUART_GCRA);
+}
+
+static int aspeed_vuart_startup(struct uart_port *uart_port)
+{
+ struct uart_8250_port *uart_8250_port = up_to_u8250p(uart_port);
+ struct aspeed_vuart *vuart = uart_8250_port->port.private_data;
+ int rc;
+
+ rc = serial8250_do_startup(uart_port);
+ if (rc)
+ return rc;
+
+ aspeed_vuart_set_host_tx_discard(vuart, false);
+
+ return 0;
+}
+
+static void aspeed_vuart_shutdown(struct uart_port *uart_port)
+{
+ struct uart_8250_port *uart_8250_port = up_to_u8250p(uart_port);
+ struct aspeed_vuart *vuart = uart_8250_port->port.private_data;
+
+ aspeed_vuart_set_host_tx_discard(vuart, true);
+
+ serial8250_do_shutdown(uart_port);
+}
+
+static int aspeed_vuart_probe(struct platform_device *pdev)
+{
+ struct uart_8250_port port;
+ struct aspeed_vuart *vuart;
+ struct device_node *np;
+ struct resource *res;
+ u32 clk, prop;
+ int rc;
+
+ np = pdev->dev.of_node;
+
+ vuart = devm_kzalloc(&pdev->dev, sizeof(*vuart), GFP_KERNEL);
+ if (!vuart)
+ return -ENOMEM;
+
+ vuart->dev = &pdev->dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ vuart->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(vuart->regs))
+ return PTR_ERR(vuart->regs);
+
+ memset(&port, 0, sizeof(port));
+ port.port.private_data = vuart;
+ port.port.membase = vuart->regs;
+ port.port.mapbase = res->start;
+ port.port.mapsize = resource_size(res);
+ port.port.startup = aspeed_vuart_startup;
+ port.port.shutdown = aspeed_vuart_shutdown;
+ port.port.dev = &pdev->dev;
+
+ rc = sysfs_create_group(&vuart->dev->kobj, &aspeed_vuart_attr_group);
+ if (rc < 0)
+ return rc;
+
+ if (of_property_read_u32(np, "clock-frequency", &clk)) {
+ vuart->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(vuart->clk)) {
+ dev_warn(&pdev->dev,
+ "clk or clock-frequency not defined\n");
+ rc = PTR_ERR(vuart->clk);
+ goto err_sysfs_remove;
+ }
+
+ rc = clk_prepare_enable(vuart->clk);
+ if (rc < 0)
+ goto err_sysfs_remove;
+
+ clk = clk_get_rate(vuart->clk);
+ }
+
+ /* If current-speed was set, then try not to change it. */
+ if (of_property_read_u32(np, "current-speed", &prop) == 0)
+ port.port.custom_divisor = clk / (16 * prop);
+
+ /* Check for shifted address mapping */
+ if (of_property_read_u32(np, "reg-offset", &prop) == 0)
+ port.port.mapbase += prop;
+
+ /* Check for registers offset within the devices address range */
+ if (of_property_read_u32(np, "reg-shift", &prop) == 0)
+ port.port.regshift = prop;
+
+ /* Check for fifo size */
+ if (of_property_read_u32(np, "fifo-size", &prop) == 0)
+ port.port.fifosize = prop;
+
+ /* Check for a fixed line number */
+ rc = of_alias_get_id(np, "serial");
+ if (rc >= 0)
+ port.port.line = rc;
+
+ port.port.irq = irq_of_parse_and_map(np, 0);
+ port.port.irqflags = IRQF_SHARED;
+ port.port.iotype = UPIO_MEM;
+ port.port.type = PORT_16550A;
+ port.port.uartclk = clk;
+ port.port.flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF
+ | UPF_FIXED_PORT | UPF_FIXED_TYPE | UPF_NO_THRE_TEST;
+
+ if (of_property_read_bool(np, "no-loopback-test"))
+ port.port.flags |= UPF_SKIP_TEST;
+
+ if (port.port.fifosize)
+ port.capabilities = UART_CAP_FIFO;
+
+ if (of_property_read_bool(np, "auto-flow-control"))
+ port.capabilities |= UART_CAP_AFE;
+
+ rc = serial8250_register_8250_port(&port);
+ if (rc < 0)
+ goto err_clk_disable;
+
+ vuart->line = rc;
+
+ aspeed_vuart_set_enabled(vuart, true);
+ aspeed_vuart_set_host_tx_discard(vuart, true);
+ platform_set_drvdata(pdev, vuart);
+
+ return 0;
+
+err_clk_disable:
+ clk_disable_unprepare(vuart->clk);
+ irq_dispose_mapping(port.port.irq);
+err_sysfs_remove:
+ sysfs_remove_group(&vuart->dev->kobj, &aspeed_vuart_attr_group);
+ return rc;
+}
+
+static int aspeed_vuart_remove(struct platform_device *pdev)
+{
+ struct aspeed_vuart *vuart = platform_get_drvdata(pdev);
+
+ aspeed_vuart_set_enabled(vuart, false);
+ serial8250_unregister_port(vuart->line);
+ sysfs_remove_group(&vuart->dev->kobj, &aspeed_vuart_attr_group);
+ clk_disable_unprepare(vuart->clk);
+
+ return 0;
+}
+
+static const struct of_device_id aspeed_vuart_table[] = {
+ { .compatible = "aspeed,ast2400-vuart" },
+ { .compatible = "aspeed,ast2500-vuart" },
+ { },
+};
+
+static struct platform_driver aspeed_vuart_driver = {
+ .driver = {
+ .name = "aspeed-vuart",
+ .of_match_table = aspeed_vuart_table,
+ },
+ .probe = aspeed_vuart_probe,
+ .remove = aspeed_vuart_remove,
+};
+
+module_platform_driver(aspeed_vuart_driver);
+
+MODULE_AUTHOR("Jeremy Kerr <jk@ozlabs.org>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Driver for Aspeed VUART device");
diff --git a/drivers/tty/serial/8250/8250_bcm2835aux.c b/drivers/tty/serial/8250/8250_bcm2835aux.c
index e10f1244409b..a23c7da42ea8 100644
--- a/drivers/tty/serial/8250/8250_bcm2835aux.c
+++ b/drivers/tty/serial/8250/8250_bcm2835aux.c
@@ -39,7 +39,7 @@ static int bcm2835aux_serial_probe(struct platform_device *pdev)
/* initialize data */
spin_lock_init(&data->uart.port.lock);
- data->uart.capabilities = UART_CAP_FIFO;
+ data->uart.capabilities = UART_CAP_FIFO | UART_CAP_MINI;
data->uart.port.dev = &pdev->dev;
data->uart.port.regshift = 2;
data->uart.port.type = PORT_16550;
diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
index 1aab3010fbfa..d29b512a7d9f 100644
--- a/drivers/tty/serial/8250/8250_core.c
+++ b/drivers/tty/serial/8250/8250_core.c
@@ -497,6 +497,11 @@ static void univ8250_rsa_support(struct uart_ops *ops)
#define univ8250_rsa_support(x) do { } while (0)
#endif /* CONFIG_SERIAL_8250_RSA */
+static inline void serial8250_apply_quirks(struct uart_8250_port *up)
+{
+ up->port.quirks |= skip_txen_test ? UPQ_NO_TXEN_TEST : 0;
+}
+
static void __init serial8250_isa_init_ports(void)
{
struct uart_8250_port *up;
@@ -577,9 +582,7 @@ serial8250_register_ports(struct uart_driver *drv, struct device *dev)
up->port.dev = dev;
- if (skip_txen_test)
- up->port.flags |= UPF_NO_TXEN_TEST;
-
+ serial8250_apply_quirks(up);
uart_add_one_port(drv, &up->port);
}
}
@@ -1006,9 +1009,6 @@ int serial8250_register_8250_port(struct uart_8250_port *up)
if (up->port.dev)
uart->port.dev = up->port.dev;
- if (skip_txen_test)
- uart->port.flags |= UPF_NO_TXEN_TEST;
-
if (up->port.flags & UPF_FIXED_TYPE)
uart->port.type = up->port.type;
@@ -1048,6 +1048,7 @@ int serial8250_register_8250_port(struct uart_8250_port *up)
serial8250_isa_config(0, &uart->port,
&uart->capabilities);
+ serial8250_apply_quirks(uart);
ret = uart_add_one_port(&serial8250_reg,
&uart->port);
if (ret == 0)
@@ -1092,11 +1093,10 @@ void serial8250_unregister_port(int line)
uart_remove_one_port(&serial8250_reg, &uart->port);
if (serial8250_isa_devs) {
uart->port.flags &= ~UPF_BOOT_AUTOCONF;
- if (skip_txen_test)
- uart->port.flags |= UPF_NO_TXEN_TEST;
uart->port.type = PORT_UNKNOWN;
uart->port.dev = &serial8250_isa_devs->dev;
uart->capabilities = 0;
+ serial8250_apply_quirks(uart);
uart_add_one_port(&serial8250_reg, &uart->port);
} else {
uart->port.dev = NULL;
diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c
index 787b1160d3a5..7e638997bfc2 100644
--- a/drivers/tty/serial/8250/8250_dw.c
+++ b/drivers/tty/serial/8250/8250_dw.c
@@ -529,7 +529,7 @@ static int dw8250_probe(struct platform_device *pdev)
}
}
- data->rst = devm_reset_control_get_optional(dev, NULL);
+ data->rst = devm_reset_control_get_optional_exclusive(dev, NULL);
if (IS_ERR(data->rst)) {
err = PTR_ERR(data->rst);
goto err_pclk;
diff --git a/drivers/tty/serial/8250/8250_early.c b/drivers/tty/serial/8250/8250_early.c
index 82fc48eca1df..af72ec32e404 100644
--- a/drivers/tty/serial/8250/8250_early.c
+++ b/drivers/tty/serial/8250/8250_early.c
@@ -37,7 +37,7 @@
#include <asm/io.h>
#include <asm/serial.h>
-static unsigned int __init serial8250_early_in(struct uart_port *port, int offset)
+static unsigned int serial8250_early_in(struct uart_port *port, int offset)
{
int reg_offset = offset;
offset <<= port->regshift;
@@ -60,7 +60,7 @@ static unsigned int __init serial8250_early_in(struct uart_port *port, int offse
}
}
-static void __init serial8250_early_out(struct uart_port *port, int offset, int value)
+static void serial8250_early_out(struct uart_port *port, int offset, int value)
{
int reg_offset = offset;
offset <<= port->regshift;
@@ -89,7 +89,7 @@ static void __init serial8250_early_out(struct uart_port *port, int offset, int
#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
-static void __init serial_putc(struct uart_port *port, int c)
+static void serial_putc(struct uart_port *port, int c)
{
unsigned int status;
@@ -103,7 +103,7 @@ static void __init serial_putc(struct uart_port *port, int c)
}
}
-static void __init early_serial8250_write(struct console *console,
+static void early_serial8250_write(struct console *console,
const char *s, unsigned int count)
{
struct earlycon_device *device = console->data;
diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c
index 1270ff163f63..c55624703fdf 100644
--- a/drivers/tty/serial/8250/8250_exar.c
+++ b/drivers/tty/serial/8250/8250_exar.c
@@ -9,10 +9,13 @@
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License.
*/
+#include <linux/acpi.h>
+#include <linux/dmi.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
+#include <linux/property.h>
#include <linux/serial_core.h>
#include <linux/serial_reg.h>
#include <linux/slab.h>
@@ -60,8 +63,50 @@
#define UART_EXAR_MPIOSEL_15_8 0x99 /* MPIOSEL[15:8] */
#define UART_EXAR_MPIOOD_15_8 0x9a /* MPIOOD[15:8] */
+#define UART_EXAR_RS485_DLY(x) ((x) << 4)
+
+/*
+ * IOT2040 MPIO wiring semantics:
+ *
+ * MPIO Port Function
+ * ---- ---- --------
+ * 0 2 Mode bit 0
+ * 1 2 Mode bit 1
+ * 2 2 Terminate bus
+ * 3 - <reserved>
+ * 4 3 Mode bit 0
+ * 5 3 Mode bit 1
+ * 6 3 Terminate bus
+ * 7 - <reserved>
+ * 8 2 Enable
+ * 9 3 Enable
+ * 10 - Red LED
+ * 11..15 - <unused>
+ */
+
+/* IOT2040 MPIOs 0..7 */
+#define IOT2040_UART_MODE_RS232 0x01
+#define IOT2040_UART_MODE_RS485 0x02
+#define IOT2040_UART_MODE_RS422 0x03
+#define IOT2040_UART_TERMINATE_BUS 0x04
+
+#define IOT2040_UART1_MASK 0x0f
+#define IOT2040_UART2_SHIFT 4
+
+#define IOT2040_UARTS_DEFAULT_MODE 0x11 /* both RS232 */
+#define IOT2040_UARTS_GPIO_LO_MODE 0x88 /* reserved pins as input */
+
+/* IOT2040 MPIOs 8..15 */
+#define IOT2040_UARTS_ENABLE 0x03
+#define IOT2040_UARTS_GPIO_HI_MODE 0xF8 /* enable & LED as outputs */
+
struct exar8250;
+struct exar8250_platform {
+ int (*rs485_config)(struct uart_port *, struct serial_rs485 *);
+ int (*register_gpio)(struct pci_dev *, struct uart_8250_port *);
+};
+
/**
* struct exar8250_board - board information
* @num_ports: number of serial ports
@@ -109,7 +154,6 @@ pci_fastcom335_setup(struct exar8250 *priv, struct pci_dev *pcidev,
u8 __iomem *p;
int err;
- port->port.flags |= UPF_EXAR_EFR;
port->port.uartclk = baud * 16;
err = default_setup(priv, pcidev, idx, offset, port);
@@ -171,24 +215,32 @@ pci_xr17c154_setup(struct exar8250 *priv, struct pci_dev *pcidev,
return default_setup(priv, pcidev, idx, offset, port);
}
-static void setup_gpio(u8 __iomem *p)
+static void setup_gpio(struct pci_dev *pcidev, u8 __iomem *p)
{
+ /*
+ * The Commtech adapters required the MPIOs to be driven low. The Exar
+ * devices will export them as GPIOs, so we pre-configure them safely
+ * as inputs.
+ */
+ u8 dir = pcidev->vendor == PCI_VENDOR_ID_EXAR ? 0xff : 0x00;
+
writeb(0x00, p + UART_EXAR_MPIOINT_7_0);
writeb(0x00, p + UART_EXAR_MPIOLVL_7_0);
writeb(0x00, p + UART_EXAR_MPIO3T_7_0);
writeb(0x00, p + UART_EXAR_MPIOINV_7_0);
- writeb(0x00, p + UART_EXAR_MPIOSEL_7_0);
+ writeb(dir, p + UART_EXAR_MPIOSEL_7_0);
writeb(0x00, p + UART_EXAR_MPIOOD_7_0);
writeb(0x00, p + UART_EXAR_MPIOINT_15_8);
writeb(0x00, p + UART_EXAR_MPIOLVL_15_8);
writeb(0x00, p + UART_EXAR_MPIO3T_15_8);
writeb(0x00, p + UART_EXAR_MPIOINV_15_8);
- writeb(0x00, p + UART_EXAR_MPIOSEL_15_8);
+ writeb(dir, p + UART_EXAR_MPIOSEL_15_8);
writeb(0x00, p + UART_EXAR_MPIOOD_15_8);
}
static void *
-xr17v35x_register_gpio(struct pci_dev *pcidev)
+__xr17v35x_register_gpio(struct pci_dev *pcidev,
+ const struct property_entry *properties)
{
struct platform_device *pdev;
@@ -196,8 +248,11 @@ xr17v35x_register_gpio(struct pci_dev *pcidev)
if (!pdev)
return NULL;
- platform_set_drvdata(pdev, pcidev);
- if (platform_device_add(pdev) < 0) {
+ pdev->dev.parent = &pcidev->dev;
+ ACPI_COMPANION_SET(&pdev->dev, ACPI_COMPANION(&pcidev->dev));
+
+ if (platform_device_add_properties(pdev, properties) < 0 ||
+ platform_device_add(pdev) < 0) {
platform_device_put(pdev);
return NULL;
}
@@ -205,17 +260,131 @@ xr17v35x_register_gpio(struct pci_dev *pcidev)
return pdev;
}
+static const struct property_entry exar_gpio_properties[] = {
+ PROPERTY_ENTRY_U32("exar,first-pin", 0),
+ PROPERTY_ENTRY_U32("ngpios", 16),
+ { }
+};
+
+static int xr17v35x_register_gpio(struct pci_dev *pcidev,
+ struct uart_8250_port *port)
+{
+ if (pcidev->vendor == PCI_VENDOR_ID_EXAR)
+ port->port.private_data =
+ __xr17v35x_register_gpio(pcidev, exar_gpio_properties);
+
+ return 0;
+}
+
+static const struct exar8250_platform exar8250_default_platform = {
+ .register_gpio = xr17v35x_register_gpio,
+};
+
+static int iot2040_rs485_config(struct uart_port *port,
+ struct serial_rs485 *rs485)
+{
+ bool is_rs485 = !!(rs485->flags & SER_RS485_ENABLED);
+ u8 __iomem *p = port->membase;
+ u8 mask = IOT2040_UART1_MASK;
+ u8 mode, value;
+
+ if (is_rs485) {
+ if (rs485->flags & SER_RS485_RX_DURING_TX)
+ mode = IOT2040_UART_MODE_RS422;
+ else
+ mode = IOT2040_UART_MODE_RS485;
+
+ if (rs485->flags & SER_RS485_TERMINATE_BUS)
+ mode |= IOT2040_UART_TERMINATE_BUS;
+ } else {
+ mode = IOT2040_UART_MODE_RS232;
+ }
+
+ if (port->line == 3) {
+ mask <<= IOT2040_UART2_SHIFT;
+ mode <<= IOT2040_UART2_SHIFT;
+ }
+
+ value = readb(p + UART_EXAR_MPIOLVL_7_0);
+ value &= ~mask;
+ value |= mode;
+ writeb(value, p + UART_EXAR_MPIOLVL_7_0);
+
+ value = readb(p + UART_EXAR_FCTR);
+ if (is_rs485)
+ value |= UART_FCTR_EXAR_485;
+ else
+ value &= ~UART_FCTR_EXAR_485;
+ writeb(value, p + UART_EXAR_FCTR);
+
+ if (is_rs485)
+ writeb(UART_EXAR_RS485_DLY(4), p + UART_MSR);
+
+ port->rs485 = *rs485;
+
+ return 0;
+}
+
+static const struct property_entry iot2040_gpio_properties[] = {
+ PROPERTY_ENTRY_U32("exar,first-pin", 10),
+ PROPERTY_ENTRY_U32("ngpios", 1),
+ { }
+};
+
+static int iot2040_register_gpio(struct pci_dev *pcidev,
+ struct uart_8250_port *port)
+{
+ u8 __iomem *p = port->port.membase;
+
+ writeb(IOT2040_UARTS_DEFAULT_MODE, p + UART_EXAR_MPIOLVL_7_0);
+ writeb(IOT2040_UARTS_GPIO_LO_MODE, p + UART_EXAR_MPIOSEL_7_0);
+ writeb(IOT2040_UARTS_ENABLE, p + UART_EXAR_MPIOLVL_15_8);
+ writeb(IOT2040_UARTS_GPIO_HI_MODE, p + UART_EXAR_MPIOSEL_15_8);
+
+ port->port.private_data =
+ __xr17v35x_register_gpio(pcidev, iot2040_gpio_properties);
+
+ return 0;
+}
+
+static const struct exar8250_platform iot2040_platform = {
+ .rs485_config = iot2040_rs485_config,
+ .register_gpio = iot2040_register_gpio,
+};
+
+static const struct dmi_system_id exar_platforms[] = {
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_NAME, "SIMATIC IOT2000"),
+ DMI_EXACT_MATCH(DMI_BOARD_ASSET_TAG,
+ "6ES7647-0AA00-1YA2"),
+ },
+ .driver_data = (void *)&iot2040_platform,
+ },
+ {}
+};
+
static int
pci_xr17v35x_setup(struct exar8250 *priv, struct pci_dev *pcidev,
struct uart_8250_port *port, int idx)
{
const struct exar8250_board *board = priv->board;
+ const struct exar8250_platform *platform;
+ const struct dmi_system_id *dmi_match;
unsigned int offset = idx * 0x400;
unsigned int baud = 7812500;
u8 __iomem *p;
int ret;
+ dmi_match = dmi_first_match(exar_platforms);
+ if (dmi_match)
+ platform = dmi_match->driver_data;
+ else
+ platform = &exar8250_default_platform;
+
port->port.uartclk = baud * 16;
+ port->port.rs485_config = platform->rs485_config;
+
/*
* Setup the uart clock for the devices on expansion slot to
* half the clock speed of the main chip (which is 125MHz)
@@ -236,12 +405,12 @@ pci_xr17v35x_setup(struct exar8250 *priv, struct pci_dev *pcidev,
if (idx == 0) {
/* Setup Multipurpose Input/Output pins. */
- setup_gpio(p);
+ setup_gpio(pcidev, p);
- port->port.private_data = xr17v35x_register_gpio(pcidev);
+ ret = platform->register_gpio(pcidev, port);
}
- return 0;
+ return ret;
}
static void pci_xr17v35x_exit(struct pci_dev *pcidev)
@@ -432,7 +601,7 @@ static const struct exar8250_board pbn_exar_XR17V8358 = {
(kernel_ulong_t)&bd \
}
-static struct pci_device_id exar_pci_tbl[] = {
+static const struct pci_device_id exar_pci_tbl[] = {
CONNECT_DEVICE(XR17C152, UART_2_232, pbn_connect),
CONNECT_DEVICE(XR17C154, UART_4_232, pbn_connect),
CONNECT_DEVICE(XR17C158, UART_8_232, pbn_connect),
diff --git a/drivers/tty/serial/8250/8250_gsc.c b/drivers/tty/serial/8250/8250_gsc.c
index 63306de4390d..df2931e1e086 100644
--- a/drivers/tty/serial/8250/8250_gsc.c
+++ b/drivers/tty/serial/8250/8250_gsc.c
@@ -80,7 +80,7 @@ static int __init serial_init_chip(struct parisc_device *dev)
return 0;
}
-static struct parisc_device_id serial_tbl[] = {
+static const struct parisc_device_id serial_tbl[] __initconst = {
{ HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x00075 },
{ HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0008c },
{ HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0008d },
@@ -94,7 +94,7 @@ static struct parisc_device_id serial_tbl[] = {
* which only knows about Lasi and then a second which will find all the
* other serial ports. HPUX ignores this problem.
*/
-static struct parisc_device_id lasi_tbl[] = {
+static const struct parisc_device_id lasi_tbl[] __initconst = {
{ HPHW_FIO, HVERSION_REV_ANY_ID, 0x03B, 0x0008C }, /* C1xx/C1xxL */
{ HPHW_FIO, HVERSION_REV_ANY_ID, 0x03C, 0x0008C }, /* B132L */
{ HPHW_FIO, HVERSION_REV_ANY_ID, 0x03D, 0x0008C }, /* B160L */
@@ -110,13 +110,13 @@ static struct parisc_device_id lasi_tbl[] = {
MODULE_DEVICE_TABLE(parisc, serial_tbl);
-static struct parisc_driver lasi_driver = {
+static struct parisc_driver lasi_driver __refdata = {
.name = "serial_1",
.id_table = lasi_tbl,
.probe = serial_init_chip,
};
-static struct parisc_driver serial_driver = {
+static struct parisc_driver serial_driver __refdata = {
.name = "serial",
.id_table = serial_tbl,
.probe = serial_init_chip,
diff --git a/drivers/tty/serial/8250/8250_ingenic.c b/drivers/tty/serial/8250/8250_ingenic.c
index 4d9dc10e265c..464389b28900 100644
--- a/drivers/tty/serial/8250/8250_ingenic.c
+++ b/drivers/tty/serial/8250/8250_ingenic.c
@@ -50,17 +50,17 @@ static const struct of_device_id of_match[];
static struct earlycon_device *early_device;
-static uint8_t __init early_in(struct uart_port *port, int offset)
+static uint8_t early_in(struct uart_port *port, int offset)
{
return readl(port->membase + (offset << 2));
}
-static void __init early_out(struct uart_port *port, int offset, uint8_t value)
+static void early_out(struct uart_port *port, int offset, uint8_t value)
{
writel(value, port->membase + (offset << 2));
}
-static void __init ingenic_early_console_putc(struct uart_port *port, int c)
+static void ingenic_early_console_putc(struct uart_port *port, int c)
{
uint8_t lsr;
@@ -71,7 +71,7 @@ static void __init ingenic_early_console_putc(struct uart_port *port, int c)
early_out(port, UART_TX, c);
}
-static void __init ingenic_early_console_write(struct console *console,
+static void ingenic_early_console_write(struct console *console,
const char *s, unsigned int count)
{
uart_console_write(&early_device->port, s, count,
diff --git a/drivers/tty/serial/8250/8250_men_mcb.c b/drivers/tty/serial/8250/8250_men_mcb.c
new file mode 100644
index 000000000000..308977807994
--- /dev/null
+++ b/drivers/tty/serial/8250/8250_men_mcb.c
@@ -0,0 +1,118 @@
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mcb.h>
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/serial_8250.h>
+#include <uapi/linux/serial_core.h>
+
+struct serial_8250_men_mcb_data {
+ struct uart_8250_port uart;
+ int line;
+};
+
+/*
+ * The Z125 16550-compatible UART has no fixed base clock assigned
+ * So, depending on the board we're on, we need to adjust the
+ * parameter in order to really set the correct baudrate, and
+ * do so if possible without user interaction
+ */
+static u32 men_z125_lookup_uartclk(struct mcb_device *mdev)
+{
+ /* use default value if board is not available below */
+ u32 clkval = 1041666;
+
+ dev_info(&mdev->dev, "%s on board %s\n",
+ dev_name(&mdev->dev),
+ mdev->bus->name);
+ if (strncmp(mdev->bus->name, "F075", 4) == 0)
+ clkval = 1041666;
+ else if (strncmp(mdev->bus->name, "F216", 4) == 0)
+ clkval = 1843200;
+ else if (strncmp(mdev->bus->name, "G215", 4) == 0)
+ clkval = 1843200;
+ else
+ dev_info(&mdev->dev,
+ "board not detected, using default uartclk\n");
+
+ clkval = clkval << 4;
+
+ return clkval;
+}
+
+static int serial_8250_men_mcb_probe(struct mcb_device *mdev,
+ const struct mcb_device_id *id)
+{
+ struct serial_8250_men_mcb_data *data;
+ struct resource *mem;
+
+ data = devm_kzalloc(&mdev->dev,
+ sizeof(struct serial_8250_men_mcb_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ mcb_set_drvdata(mdev, data);
+ data->uart.port.dev = mdev->dma_dev;
+ spin_lock_init(&data->uart.port.lock);
+
+ data->uart.port.type = PORT_16550;
+ data->uart.port.flags = UPF_SKIP_TEST | UPF_SHARE_IRQ | UPF_FIXED_TYPE;
+ data->uart.port.iotype = UPIO_MEM;
+ data->uart.port.uartclk = men_z125_lookup_uartclk(mdev);
+ data->uart.port.regshift = 0;
+ data->uart.port.fifosize = 60;
+
+ mem = mcb_get_resource(mdev, IORESOURCE_MEM);
+ if (mem == NULL)
+ return -ENXIO;
+
+ data->uart.port.irq = mcb_get_irq(mdev);
+
+ data->uart.port.membase = devm_ioremap_resource(&mdev->dev, mem);
+ if (IS_ERR(data->uart.port.membase))
+ return PTR_ERR_OR_ZERO(data->uart.port.membase);
+
+ data->uart.port.mapbase = (unsigned long) mem->start;
+ data->uart.port.iobase = data->uart.port.mapbase;
+
+ /* ok, register the port */
+ data->line = serial8250_register_8250_port(&data->uart);
+ if (data->line < 0)
+ return data->line;
+
+ dev_info(&mdev->dev, "found 16Z125 UART: ttyS%d\n", data->line);
+
+ return 0;
+}
+
+static void serial_8250_men_mcb_remove(struct mcb_device *mdev)
+{
+ struct serial_8250_men_mcb_data *data = mcb_get_drvdata(mdev);
+
+ if (data)
+ serial8250_unregister_port(data->line);
+}
+
+static const struct mcb_device_id serial_8250_men_mcb_ids[] = {
+ { .device = 0x7d },
+ { }
+};
+MODULE_DEVICE_TABLE(mcb, serial_8250_men_mcb_ids);
+
+static struct mcb_driver mcb_driver = {
+ .driver = {
+ .name = "8250_men_mcb",
+ .owner = THIS_MODULE,
+ },
+ .probe = serial_8250_men_mcb_probe,
+ .remove = serial_8250_men_mcb_remove,
+ .id_table = serial_8250_men_mcb_ids,
+};
+module_mcb_driver(mcb_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MEN 16z125 8250 UART driver");
+MODULE_AUTHOR("Michael Moese <michael.moese@men.de");
+MODULE_ALIAS("mcb:16z125");
diff --git a/drivers/tty/serial/8250/8250_mtk.c b/drivers/tty/serial/8250/8250_mtk.c
index ce0cc471bfc3..fb45770d47aa 100644
--- a/drivers/tty/serial/8250/8250_mtk.c
+++ b/drivers/tty/serial/8250/8250_mtk.c
@@ -171,10 +171,7 @@ static int mtk8250_probe_of(struct platform_device *pdev, struct uart_port *p,
}
data->bus_clk = devm_clk_get(&pdev->dev, "bus");
- if (IS_ERR(data->bus_clk))
- return PTR_ERR(data->bus_clk);
-
- return 0;
+ return PTR_ERR_OR_ZERO(data->bus_clk);
}
static int mtk8250_probe(struct platform_device *pdev)
diff --git a/drivers/tty/serial/8250/8250_of.c b/drivers/tty/serial/8250/8250_of.c
index 1cbadafc6889..1222c005fb98 100644
--- a/drivers/tty/serial/8250/8250_of.c
+++ b/drivers/tty/serial/8250/8250_of.c
@@ -18,12 +18,15 @@
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
+#include <linux/pm_runtime.h>
#include <linux/clk.h>
+#include <linux/reset.h>
#include "8250.h"
struct of_serial_info {
struct clk *clk;
+ struct reset_control *rst;
int type;
int line;
};
@@ -63,6 +66,10 @@ static int of_platform_serial_setup(struct platform_device *ofdev,
int ret;
memset(port, 0, sizeof *port);
+
+ pm_runtime_enable(&ofdev->dev);
+ pm_runtime_get_sync(&ofdev->dev);
+
if (of_property_read_u32(np, "clock-frequency", &clk)) {
/* Get clk rate through clk driver if present */
@@ -70,12 +77,13 @@ static int of_platform_serial_setup(struct platform_device *ofdev,
if (IS_ERR(info->clk)) {
dev_warn(&ofdev->dev,
"clk or clock-frequency not defined\n");
- return PTR_ERR(info->clk);
+ ret = PTR_ERR(info->clk);
+ goto err_pmruntime;
}
ret = clk_prepare_enable(info->clk);
if (ret < 0)
- return ret;
+ goto err_pmruntime;
clk = clk_get_rate(info->clk);
}
@@ -86,7 +94,7 @@ static int of_platform_serial_setup(struct platform_device *ofdev,
ret = of_address_to_resource(np, 0, &resource);
if (ret) {
dev_warn(&ofdev->dev, "invalid address\n");
- goto out;
+ goto err_unprepare;
}
spin_lock_init(&port->lock);
@@ -128,16 +136,23 @@ static int of_platform_serial_setup(struct platform_device *ofdev,
dev_warn(&ofdev->dev, "unsupported reg-io-width (%d)\n",
prop);
ret = -EINVAL;
- goto out;
+ goto err_dispose;
}
}
+ info->rst = devm_reset_control_get_optional_shared(&ofdev->dev, NULL);
+ if (IS_ERR(info->rst))
+ goto err_dispose;
+ ret = reset_control_deassert(info->rst);
+ if (ret)
+ goto err_dispose;
+
port->type = type;
port->uartclk = clk;
port->flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_IOREMAP
| UPF_FIXED_PORT | UPF_FIXED_TYPE;
- if (of_find_property(np, "no-loopback-test", NULL))
+ if (of_property_read_bool(np, "no-loopback-test"))
port->flags |= UPF_SKIP_TEST;
port->dev = &ofdev->dev;
@@ -158,9 +173,13 @@ static int of_platform_serial_setup(struct platform_device *ofdev,
port->handle_irq = fsl8250_handle_irq;
return 0;
-out:
- if (info->clk)
- clk_disable_unprepare(info->clk);
+err_dispose:
+ irq_dispose_mapping(port->irq);
+err_unprepare:
+ clk_disable_unprepare(info->clk);
+err_pmruntime:
+ pm_runtime_put_sync(&ofdev->dev);
+ pm_runtime_disable(&ofdev->dev);
return ret;
}
@@ -181,7 +200,7 @@ static int of_platform_serial_probe(struct platform_device *ofdev)
if (!match)
return -EINVAL;
- if (of_find_property(ofdev->dev.of_node, "used-by-rtas", NULL))
+ if (of_property_read_bool(ofdev->dev.of_node, "used-by-rtas"))
return -EBUSY;
info = kzalloc(sizeof(*info), GFP_KERNEL);
@@ -192,7 +211,7 @@ static int of_platform_serial_probe(struct platform_device *ofdev)
memset(&port8250, 0, sizeof(port8250));
ret = of_platform_serial_setup(ofdev, port_type, &port8250.port, info);
if (ret)
- goto out;
+ goto err_free;
if (port8250.port.fifosize)
port8250.capabilities = UART_CAP_FIFO;
@@ -208,15 +227,19 @@ static int of_platform_serial_probe(struct platform_device *ofdev)
ret = serial8250_register_8250_port(&port8250);
if (ret < 0)
- goto out;
+ goto err_dispose;
info->type = port_type;
info->line = ret;
platform_set_drvdata(ofdev, info);
return 0;
-out:
- kfree(info);
+err_dispose:
irq_dispose_mapping(port8250.port.irq);
+ pm_runtime_put_sync(&ofdev->dev);
+ pm_runtime_disable(&ofdev->dev);
+ clk_disable_unprepare(info->clk);
+err_free:
+ kfree(info);
return ret;
}
@@ -229,8 +252,10 @@ static int of_platform_serial_remove(struct platform_device *ofdev)
serial8250_unregister_port(info->line);
- if (info->clk)
- clk_disable_unprepare(info->clk);
+ reset_control_assert(info->rst);
+ pm_runtime_put_sync(&ofdev->dev);
+ pm_runtime_disable(&ofdev->dev);
+ clk_disable_unprepare(info->clk);
kfree(info);
return 0;
}
@@ -244,9 +269,10 @@ static int of_serial_suspend(struct device *dev)
serial8250_suspend_port(info->line);
- if (info->clk && (!uart_console(port) || console_suspend_enabled))
+ if (!uart_console(port) || console_suspend_enabled) {
+ pm_runtime_put_sync(dev);
clk_disable_unprepare(info->clk);
-
+ }
return 0;
}
@@ -256,8 +282,10 @@ static int of_serial_resume(struct device *dev)
struct uart_8250_port *port8250 = serial8250_get_port(info->line);
struct uart_port *port = &port8250->port;
- if (info->clk && (!uart_console(port) || console_suspend_enabled))
+ if (!uart_console(port) || console_suspend_enabled) {
+ pm_runtime_get_sync(dev);
clk_prepare_enable(info->clk);
+ }
serial8250_resume_port(info->line);
@@ -285,6 +313,8 @@ static const struct of_device_id of_platform_serial_table[] = {
.data = (void *)PORT_ALTR_16550_F64, },
{ .compatible = "altr,16550-FIFO128",
.data = (void *)PORT_ALTR_16550_F128, },
+ { .compatible = "mediatek,mtk-btif",
+ .data = (void *)PORT_MTK_BTIF, },
{ .compatible = "mrvl,mmp-uart",
.data = (void *)PORT_XSCALE, },
{ .compatible = "ti,da830-uart", .data = (void *)PORT_DA830, },
diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c
index e7e64913a748..833771bca0a5 100644
--- a/drivers/tty/serial/8250/8250_omap.c
+++ b/drivers/tty/serial/8250/8250_omap.c
@@ -613,6 +613,10 @@ static int omap_8250_startup(struct uart_port *port)
up->lsr_saved_flags = 0;
up->msr_saved_flags = 0;
+ /* Disable DMA for console UART */
+ if (uart_console(port))
+ up->dma = NULL;
+
if (up->dma) {
ret = serial8250_request_dma(up);
if (ret) {
@@ -782,8 +786,27 @@ unlock:
static void __dma_rx_complete(void *param)
{
- __dma_rx_do_complete(param);
- omap_8250_rx_dma(param);
+ struct uart_8250_port *p = param;
+ struct uart_8250_dma *dma = p->dma;
+ struct dma_tx_state state;
+ unsigned long flags;
+
+ spin_lock_irqsave(&p->port.lock, flags);
+
+ /*
+ * If the tx status is not DMA_COMPLETE, then this is a delayed
+ * completion callback. A previous RX timeout flush would have
+ * already pushed the data, so exit.
+ */
+ if (dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state) !=
+ DMA_COMPLETE) {
+ spin_unlock_irqrestore(&p->port.lock, flags);
+ return;
+ }
+ __dma_rx_do_complete(p);
+ omap_8250_rx_dma(p);
+
+ spin_unlock_irqrestore(&p->port.lock, flags);
}
static void omap_8250_rx_dma_flush(struct uart_8250_port *p)
diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c
index 00e51a064388..0c101a7470b0 100644
--- a/drivers/tty/serial/8250/8250_pci.c
+++ b/drivers/tty/serial/8250/8250_pci.c
@@ -1548,7 +1548,7 @@ static int skip_tx_en_setup(struct serial_private *priv,
const struct pciserial_board *board,
struct uart_8250_port *port, int idx)
{
- port->port.flags |= UPF_NO_TXEN_TEST;
+ port->port.quirks |= UPQ_NO_TXEN_TEST;
dev_dbg(&priv->dev->dev,
"serial8250: skipping TxEn test for device [%04x:%04x] subsystem [%04x:%04x]\n",
priv->dev->vendor, priv->dev->device,
@@ -3384,17 +3384,8 @@ static const struct pci_device_id blacklist[] = {
{ PCI_VDEVICE(COMMTECH, PCI_ANY_ID), },
};
-/*
- * Given a complete unknown PCI device, try to use some heuristics to
- * guess what the configuration might be, based on the pitiful PCI
- * serial specs. Returns 0 on success, 1 on failure.
- */
-static int
-serial_pci_guess_board(struct pci_dev *dev, struct pciserial_board *board)
+static int serial_pci_is_class_communication(struct pci_dev *dev)
{
- const struct pci_device_id *bldev;
- int num_iomem, num_port, first_port = -1, i;
-
/*
* If it is not a communications device or the programming
* interface is greater than 6, give up.
@@ -3407,6 +3398,13 @@ serial_pci_guess_board(struct pci_dev *dev, struct pciserial_board *board)
(dev->class & 0xff) > 6)
return -ENODEV;
+ return 0;
+}
+
+static int serial_pci_is_blacklisted(struct pci_dev *dev)
+{
+ const struct pci_device_id *bldev;
+
/*
* Do not access blacklisted devices that are known not to
* feature serial ports or are handled by other modules.
@@ -3419,6 +3417,19 @@ serial_pci_guess_board(struct pci_dev *dev, struct pciserial_board *board)
return -ENODEV;
}
+ return 0;
+}
+
+/*
+ * Given a complete unknown PCI device, try to use some heuristics to
+ * guess what the configuration might be, based on the pitiful PCI
+ * serial specs. Returns 0 on success, -ENODEV on failure.
+ */
+static int
+serial_pci_guess_board(struct pci_dev *dev, struct pciserial_board *board)
+{
+ int num_iomem, num_port, first_port = -1, i;
+
num_iomem = num_port = 0;
for (i = 0; i < PCI_NUM_BAR_RESOURCES; i++) {
if (pci_resource_flags(dev, i) & IORESOURCE_IO) {
@@ -3639,6 +3650,14 @@ pciserial_init_one(struct pci_dev *dev, const struct pci_device_id *ent)
board = &pci_boards[ent->driver_data];
+ rc = serial_pci_is_class_communication(dev);
+ if (rc)
+ return rc;
+
+ rc = serial_pci_is_blacklisted(dev);
+ if (rc)
+ return rc;
+
rc = pcim_enable_device(dev);
pci_save_state(dev);
if (rc)
@@ -3723,7 +3742,7 @@ static int pciserial_resume_one(struct device *dev)
static SIMPLE_DEV_PM_OPS(pciserial_pm_ops, pciserial_suspend_one,
pciserial_resume_one);
-static struct pci_device_id serial_pci_tbl[] = {
+static const struct pci_device_id serial_pci_tbl[] = {
/* Advantech use PCI_DEVICE_ID_ADVANTECH_PCI3620 (0x3620) as 'PCI_SUBVENDOR_ID' */
{ PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_PCI3620,
PCI_DEVICE_ID_ADVANTECH_PCI3620, 0x0001, 0, 0,
diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c
index 68fd045a7025..f0cc04f62b67 100644
--- a/drivers/tty/serial/8250/8250_port.c
+++ b/drivers/tty/serial/8250/8250_port.c
@@ -37,7 +37,7 @@
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/pm_runtime.h>
-#include <linux/timer.h>
+#include <linux/ktime.h>
#include <asm/io.h>
#include <asm/irq.h>
@@ -289,6 +289,14 @@ static const struct serial8250_config uart_config[] = {
.rxtrig_bytes = {1, 4, 8, 14},
.flags = UART_CAP_FIFO | UART_CAP_AFE,
},
+ [PORT_MTK_BTIF] = {
+ .name = "MediaTek BTIF",
+ .fifo_size = 16,
+ .tx_loadsz = 16,
+ .fcr = UART_FCR_ENABLE_FIFO |
+ UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT,
+ .flags = UART_CAP_FIFO,
+ },
};
/* Uart divisor latch read */
@@ -553,8 +561,8 @@ static inline void serial8250_em485_rts_after_send(struct uart_8250_port *p)
serial8250_out_MCR(p, mcr);
}
-static void serial8250_em485_handle_start_tx(unsigned long arg);
-static void serial8250_em485_handle_stop_tx(unsigned long arg);
+static enum hrtimer_restart serial8250_em485_handle_start_tx(struct hrtimer *t);
+static enum hrtimer_restart serial8250_em485_handle_stop_tx(struct hrtimer *t);
void serial8250_clear_and_reinit_fifos(struct uart_8250_port *p)
{
@@ -609,12 +617,14 @@ int serial8250_em485_init(struct uart_8250_port *p)
if (!p->em485)
return -ENOMEM;
- setup_timer(&p->em485->stop_tx_timer,
- serial8250_em485_handle_stop_tx, (unsigned long)p);
- setup_timer(&p->em485->start_tx_timer,
- serial8250_em485_handle_start_tx, (unsigned long)p);
+ hrtimer_init(&p->em485->stop_tx_timer, CLOCK_MONOTONIC,
+ HRTIMER_MODE_REL);
+ hrtimer_init(&p->em485->start_tx_timer, CLOCK_MONOTONIC,
+ HRTIMER_MODE_REL);
+ p->em485->stop_tx_timer.function = &serial8250_em485_handle_stop_tx;
+ p->em485->start_tx_timer.function = &serial8250_em485_handle_start_tx;
+ p->em485->port = p;
p->em485->active_timer = NULL;
-
serial8250_em485_rts_after_send(p);
return 0;
@@ -639,8 +649,8 @@ void serial8250_em485_destroy(struct uart_8250_port *p)
if (!p->em485)
return;
- del_timer(&p->em485->start_tx_timer);
- del_timer(&p->em485->stop_tx_timer);
+ hrtimer_cancel(&p->em485->start_tx_timer);
+ hrtimer_cancel(&p->em485->stop_tx_timer);
kfree(p->em485);
p->em485 = NULL;
@@ -1435,22 +1445,33 @@ static void __do_stop_tx_rs485(struct uart_8250_port *p)
serial_port_out(&p->port, UART_IER, p->ier);
}
}
-
-static void serial8250_em485_handle_stop_tx(unsigned long arg)
+static enum hrtimer_restart serial8250_em485_handle_stop_tx(struct hrtimer *t)
{
- struct uart_8250_port *p = (struct uart_8250_port *)arg;
- struct uart_8250_em485 *em485 = p->em485;
+ struct uart_8250_em485 *em485;
+ struct uart_8250_port *p;
unsigned long flags;
+ em485 = container_of(t, struct uart_8250_em485, stop_tx_timer);
+ p = em485->port;
+
serial8250_rpm_get(p);
spin_lock_irqsave(&p->port.lock, flags);
- if (em485 &&
- em485->active_timer == &em485->stop_tx_timer) {
+ if (em485->active_timer == &em485->stop_tx_timer) {
__do_stop_tx_rs485(p);
em485->active_timer = NULL;
}
spin_unlock_irqrestore(&p->port.lock, flags);
serial8250_rpm_put(p);
+ return HRTIMER_NORESTART;
+}
+
+static void start_hrtimer_ms(struct hrtimer *hrt, unsigned long msec)
+{
+ long sec = msec / 1000;
+ long nsec = (msec % 1000) * 1000000;
+ ktime_t t = ktime_set(sec, nsec);
+
+ hrtimer_start(hrt, t, HRTIMER_MODE_REL);
}
static void __stop_tx_rs485(struct uart_8250_port *p)
@@ -1463,8 +1484,8 @@ static void __stop_tx_rs485(struct uart_8250_port *p)
*/
if (p->port.rs485.delay_rts_after_send > 0) {
em485->active_timer = &em485->stop_tx_timer;
- mod_timer(&em485->stop_tx_timer, jiffies +
- p->port.rs485.delay_rts_after_send * HZ / 1000);
+ start_hrtimer_ms(&em485->stop_tx_timer,
+ p->port.rs485.delay_rts_after_send);
} else {
__do_stop_tx_rs485(p);
}
@@ -1494,8 +1515,8 @@ static inline void __stop_tx(struct uart_8250_port *p)
if ((lsr & BOTH_EMPTY) != BOTH_EMPTY)
return;
- del_timer(&em485->start_tx_timer);
em485->active_timer = NULL;
+ hrtimer_cancel(&em485->start_tx_timer);
__stop_tx_rs485(p);
}
@@ -1558,8 +1579,9 @@ static inline void start_tx_rs485(struct uart_port *port)
if (!(up->port.rs485.flags & SER_RS485_RX_DURING_TX))
serial8250_stop_rx(&up->port);
- del_timer(&em485->stop_tx_timer);
em485->active_timer = NULL;
+ if (hrtimer_is_queued(&em485->stop_tx_timer))
+ hrtimer_cancel(&em485->stop_tx_timer);
mcr = serial8250_in_MCR(up);
if (!!(up->port.rs485.flags & SER_RS485_RTS_ON_SEND) !=
@@ -1572,8 +1594,8 @@ static inline void start_tx_rs485(struct uart_port *port)
if (up->port.rs485.delay_rts_before_send > 0) {
em485->active_timer = &em485->start_tx_timer;
- mod_timer(&em485->start_tx_timer, jiffies +
- up->port.rs485.delay_rts_before_send * HZ / 1000);
+ start_hrtimer_ms(&em485->start_tx_timer,
+ up->port.rs485.delay_rts_before_send);
return;
}
}
@@ -1581,19 +1603,22 @@ static inline void start_tx_rs485(struct uart_port *port)
__start_tx(port);
}
-static void serial8250_em485_handle_start_tx(unsigned long arg)
+static enum hrtimer_restart serial8250_em485_handle_start_tx(struct hrtimer *t)
{
- struct uart_8250_port *p = (struct uart_8250_port *)arg;
- struct uart_8250_em485 *em485 = p->em485;
+ struct uart_8250_em485 *em485;
+ struct uart_8250_port *p;
unsigned long flags;
+ em485 = container_of(t, struct uart_8250_em485, start_tx_timer);
+ p = em485->port;
+
spin_lock_irqsave(&p->port.lock, flags);
- if (em485 &&
- em485->active_timer == &em485->start_tx_timer) {
+ if (em485->active_timer == &em485->start_tx_timer) {
__start_tx(&p->port);
em485->active_timer = NULL;
}
spin_unlock_irqrestore(&p->port.lock, flags);
+ return HRTIMER_NORESTART;
}
static void serial8250_start_tx(struct uart_port *port)
@@ -1764,6 +1789,10 @@ void serial8250_tx_chars(struct uart_8250_port *up)
if ((up->capabilities & UART_CAP_HFIFO) &&
(serial_in(up, UART_LSR) & BOTH_EMPTY) != BOTH_EMPTY)
break;
+ /* The BCM2835 MINI UART THRE bit is really a not-full bit. */
+ if ((up->capabilities & UART_CAP_MINI) &&
+ !(serial_in(up, UART_LSR) & UART_LSR_THRE))
+ break;
} while (--count > 0);
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
@@ -2228,7 +2257,7 @@ int serial8250_do_startup(struct uart_port *port)
}
}
- if (port->irq) {
+ if (port->irq && !(up->port.flags & UPF_NO_THRE_TEST)) {
unsigned char iir1;
/*
* Test for UARTs that do not reassert THRE when the
@@ -2300,7 +2329,7 @@ int serial8250_do_startup(struct uart_port *port)
* test if we receive TX irq. This way, we'll never enable
* UART_BUG_TXEN.
*/
- if (up->port.flags & UPF_NO_TXEN_TEST)
+ if (up->port.quirks & UPQ_NO_TXEN_TEST)
goto dont_test_tx_en;
/*
@@ -2585,6 +2614,12 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
unsigned long flags;
unsigned int baud, quot, frac = 0;
+ if (up->capabilities & UART_CAP_MINI) {
+ termios->c_cflag &= ~(CSTOPB | PARENB | PARODD | CMSPAR);
+ if ((termios->c_cflag & CSIZE) == CS5 ||
+ (termios->c_cflag & CSIZE) == CS6)
+ termios->c_cflag = (termios->c_cflag & ~CSIZE) | CS7;
+ }
cval = serial8250_compute_lcr(up, termios->c_cflag);
baud = serial8250_get_baud_rate(port, termios, old);
diff --git a/drivers/tty/serial/8250/8250_uniphier.c b/drivers/tty/serial/8250/8250_uniphier.c
index 746680ebf90c..8a10b10e27aa 100644
--- a/drivers/tty/serial/8250/8250_uniphier.c
+++ b/drivers/tty/serial/8250/8250_uniphier.c
@@ -29,12 +29,13 @@
* - MMIO32 (regshift = 2)
* - FCR is not at 2, but 3
* - LCR and MCR are not at 3 and 4, they share 4
+ * - No SCR (Instead, CHAR can be used as a scratch register)
* - Divisor latch at 9, no divisor latch access bit
*/
#define UNIPHIER_UART_REGSHIFT 2
-/* bit[15:8] = CHAR (not used), bit[7:0] = FCR */
+/* bit[15:8] = CHAR, bit[7:0] = FCR */
#define UNIPHIER_UART_CHAR_FCR (3 << (UNIPHIER_UART_REGSHIFT))
/* bit[15:8] = LCR, bit[7:0] = MCR */
#define UNIPHIER_UART_LCR_MCR (4 << (UNIPHIER_UART_REGSHIFT))
@@ -72,13 +73,18 @@ OF_EARLYCON_DECLARE(uniphier, "socionext,uniphier-uart",
/*
* The register map is slightly different from that of 8250.
- * IO callbacks must be overridden for correct access to FCR, LCR, and MCR.
+ * IO callbacks must be overridden for correct access to FCR, LCR, MCR and SCR.
*/
static unsigned int uniphier_serial_in(struct uart_port *p, int offset)
{
unsigned int valshift = 0;
switch (offset) {
+ case UART_SCR:
+ /* No SCR for this hardware. Use CHAR as a scratch register */
+ valshift = 8;
+ offset = UNIPHIER_UART_CHAR_FCR;
+ break;
case UART_LCR:
valshift = 8;
/* fall through */
@@ -91,8 +97,8 @@ static unsigned int uniphier_serial_in(struct uart_port *p, int offset)
}
/*
- * The return value must be masked with 0xff because LCR and MCR reside
- * in the same register that must be accessed by 32-bit write/read.
+ * The return value must be masked with 0xff because some registers
+ * share the same offset that must be accessed by 32-bit write/read.
* 8 or 16 bit access to this hardware result in unexpected behavior.
*/
return (readl(p->membase + offset) >> valshift) & 0xff;
@@ -101,9 +107,13 @@ static unsigned int uniphier_serial_in(struct uart_port *p, int offset)
static void uniphier_serial_out(struct uart_port *p, int offset, int value)
{
unsigned int valshift = 0;
- bool normal = true;
+ bool normal = false;
switch (offset) {
+ case UART_SCR:
+ /* No SCR for this hardware. Use CHAR as a scratch register */
+ valshift = 8;
+ /* fall through */
case UART_FCR:
offset = UNIPHIER_UART_CHAR_FCR;
break;
@@ -114,10 +124,10 @@ static void uniphier_serial_out(struct uart_port *p, int offset, int value)
/* fall through */
case UART_MCR:
offset = UNIPHIER_UART_LCR_MCR;
- normal = false;
break;
default:
offset <<= UNIPHIER_UART_REGSHIFT;
+ normal = true;
break;
}
@@ -169,7 +179,7 @@ static int uniphier_of_serial_setup(struct device *dev, struct uart_port *port,
dev_err(dev, "failed to get alias id\n");
return ret;
}
- port->line = priv->line = ret;
+ port->line = ret;
/* Get clk rate through clk driver */
priv->clk = devm_clk_get(dev, NULL);
@@ -249,8 +259,8 @@ static int uniphier_uart_probe(struct platform_device *pdev)
up.dl_read = uniphier_serial_dl_read;
up.dl_write = uniphier_serial_dl_write;
- ret = serial8250_register_8250_port(&up);
- if (ret < 0) {
+ priv->line = serial8250_register_8250_port(&up);
+ if (priv->line < 0) {
dev_err(dev, "failed to register 8250 port\n");
clk_disable_unprepare(priv->clk);
return ret;
@@ -271,6 +281,40 @@ static int uniphier_uart_remove(struct platform_device *pdev)
return 0;
}
+static int __maybe_unused uniphier_uart_suspend(struct device *dev)
+{
+ struct uniphier8250_priv *priv = dev_get_drvdata(dev);
+ struct uart_8250_port *up = serial8250_get_port(priv->line);
+
+ serial8250_suspend_port(priv->line);
+
+ if (!uart_console(&up->port) || console_suspend_enabled)
+ clk_disable_unprepare(priv->clk);
+
+ return 0;
+}
+
+static int __maybe_unused uniphier_uart_resume(struct device *dev)
+{
+ struct uniphier8250_priv *priv = dev_get_drvdata(dev);
+ struct uart_8250_port *up = serial8250_get_port(priv->line);
+ int ret;
+
+ if (!uart_console(&up->port) || console_suspend_enabled) {
+ ret = clk_prepare_enable(priv->clk);
+ if (ret)
+ return ret;
+ }
+
+ serial8250_resume_port(priv->line);
+
+ return 0;
+}
+
+static const struct dev_pm_ops uniphier_uart_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(uniphier_uart_suspend, uniphier_uart_resume)
+};
+
static const struct of_device_id uniphier_uart_match[] = {
{ .compatible = "socionext,uniphier-uart" },
{ /* sentinel */ }
@@ -283,6 +327,7 @@ static struct platform_driver uniphier_uart_platform_driver = {
.driver = {
.name = "uniphier-uart",
.of_match_table = uniphier_uart_match,
+ .pm = &uniphier_uart_pm_ops,
},
};
module_platform_driver(uniphier_uart_platform_driver);
diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig
index 0e3f529d50e9..a5c0ef1e7695 100644
--- a/drivers/tty/serial/8250/Kconfig
+++ b/drivers/tty/serial/8250/Kconfig
@@ -155,6 +155,17 @@ config SERIAL_8250_CS
If unsure, say N.
+config SERIAL_8250_MEN_MCB
+ tristate "MEN Z125 UART device support"
+ depends on MCB && SERIAL_8250
+ help
+ This enables support for FPGA based UARTs found on many MEN
+ boards. This driver enables support for the Z125 UARTs.
+
+ To compile this driver as a module, chose M here: the
+ module will be called 8250_men_mcb.
+
+
config SERIAL_8250_NR_UARTS
int "Maximum number of 8250/16550 serial ports"
depends on SERIAL_8250
@@ -224,6 +235,16 @@ config SERIAL_8250_ACCENT
To compile this driver as a module, choose M here: the module
will be called 8250_accent.
+config SERIAL_8250_ASPEED_VUART
+ tristate "Aspeed Virtual UART"
+ depends on SERIAL_8250
+ depends on OF
+ help
+ If you want to use the virtual UART (VUART) device on Aspeed
+ BMC platforms, enable this option. This enables the 16550A-
+ compatible device on the local LPC bus, giving a UART device
+ with no physical RS232 connections.
+
config SERIAL_8250_BOCA
tristate "Support Boca cards"
depends on SERIAL_8250 != n && ISA && SERIAL_8250_MANY_PORTS
diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile
index 2f30f9ecdb1b..18751bc63a84 100644
--- a/drivers/tty/serial/8250/Makefile
+++ b/drivers/tty/serial/8250/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
#
# Makefile for the 8250 serial device drivers.
#
@@ -14,6 +15,7 @@ obj-$(CONFIG_SERIAL_8250_EXAR) += 8250_exar.o
obj-$(CONFIG_SERIAL_8250_HP300) += 8250_hp300.o
obj-$(CONFIG_SERIAL_8250_CS) += serial_cs.o
obj-$(CONFIG_SERIAL_8250_ACORN) += 8250_acorn.o
+obj-$(CONFIG_SERIAL_8250_ASPEED_VUART) += 8250_aspeed_vuart.o
obj-$(CONFIG_SERIAL_8250_BCM2835AUX) += 8250_bcm2835aux.o
obj-$(CONFIG_SERIAL_8250_CONSOLE) += 8250_early.o
obj-$(CONFIG_SERIAL_8250_FOURPORT) += 8250_fourport.o
@@ -22,6 +24,7 @@ obj-$(CONFIG_SERIAL_8250_BOCA) += 8250_boca.o
obj-$(CONFIG_SERIAL_8250_EXAR_ST16C554) += 8250_exar_st16c554.o
obj-$(CONFIG_SERIAL_8250_HUB6) += 8250_hub6.o
obj-$(CONFIG_SERIAL_8250_FSL) += 8250_fsl.o
+obj-$(CONFIG_SERIAL_8250_MEN_MCB) += 8250_men_mcb.o
obj-$(CONFIG_SERIAL_8250_DW) += 8250_dw.o
obj-$(CONFIG_SERIAL_8250_EM) += 8250_em.o
obj-$(CONFIG_SERIAL_8250_OMAP) += 8250_omap.o
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index 5c8850f7a2a0..b788fee54249 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -114,32 +114,32 @@ config SERIAL_SB1250_DUART_CONSOLE
If unsure, say Y.
config SERIAL_ATMEL
- bool "AT91 / AT32 on-chip serial port support"
+ bool "AT91 on-chip serial port support"
depends on HAS_DMA
- depends on ARCH_AT91 || AVR32 || COMPILE_TEST
+ depends on ARCH_AT91 || COMPILE_TEST
select SERIAL_CORE
select SERIAL_MCTRL_GPIO if GPIOLIB
help
This enables the driver for the on-chip UARTs of the Atmel
- AT91 and AT32 processors.
+ AT91 processors.
config SERIAL_ATMEL_CONSOLE
- bool "Support for console on AT91 / AT32 serial port"
+ bool "Support for console on AT91 serial port"
depends on SERIAL_ATMEL=y
select SERIAL_CORE_CONSOLE
help
Say Y here if you wish to use an on-chip UART on a Atmel
- AT91 or AT32 processor as the system console (the system
+ AT91 processor 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).
config SERIAL_ATMEL_PDC
- bool "Support DMA transfers on AT91 / AT32 serial port"
+ bool "Support DMA transfers on AT91 serial port"
depends on SERIAL_ATMEL
default y
help
Say Y here if you wish to use the PDC to do DMA transfers to
- and from the Atmel AT91 / AT32 serial port. In order to
+ and from the Atmel AT91 serial port. In order to
actually use DMA transfers, make sure that the use_dma_tx
and use_dma_rx members in the atmel_uart_data struct is set
appropriately for each port.
@@ -152,7 +152,7 @@ config SERIAL_ATMEL_TTYAT
bool "Install as device ttyATn instead of ttySn"
depends on SERIAL_ATMEL=y
help
- Say Y here if you wish to have the internal AT91 / AT32 UARTs
+ Say Y here if you wish to have the internal AT91 UARTs
appear as /dev/ttyATn (major 204, minor starting at 154)
instead of the normal /dev/ttySn (major 4, minor starting at
64). This is necessary if you also want other UARTs, such as
@@ -1688,6 +1688,25 @@ config SERIAL_MVEBU_CONSOLE
and warnings and which allows logins in single user mode)
Otherwise, say 'N'.
+config SERIAL_OWL
+ tristate "Actions Semi Owl serial port support"
+ depends on ARCH_ACTIONS || COMPILE_TEST
+ select SERIAL_CORE
+ help
+ This driver is for Actions Semiconductor S500/S900 SoC's UART.
+ Say 'Y' here if you wish to use the on-board serial port.
+ Otherwise, say 'N'.
+
+config SERIAL_OWL_CONSOLE
+ bool "Console on Actions Semi Owl serial port"
+ depends on SERIAL_OWL=y
+ select SERIAL_CORE_CONSOLE
+ select SERIAL_EARLYCON
+ default y
+ help
+ Say 'Y' here if you wish to use Actions Semiconductor S500/S900 UART
+ as the system console.
+
endmenu
config SERIAL_MCTRL_GPIO
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index 53c03e005132..842d185d697e 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
#
# Makefile for the kernel serial device drivers.
#
@@ -92,6 +93,7 @@ obj-$(CONFIG_SERIAL_STM32) += stm32-usart.o
obj-$(CONFIG_SERIAL_MVEBU_UART) += mvebu-uart.o
obj-$(CONFIG_SERIAL_PIC32) += pic32_uart.o
obj-$(CONFIG_SERIAL_MPS2_UART) += mps2-uart.o
+obj-$(CONFIG_SERIAL_OWL) += owl-uart.o
# GPIOLIB helpers for modem control lines
obj-$(CONFIG_SERIAL_MCTRL_GPIO) += serial_mctrl_gpio.o
diff --git a/drivers/tty/serial/amba-pl010.c b/drivers/tty/serial/amba-pl010.c
index f2f251075109..9ec4b8d2879f 100644
--- a/drivers/tty/serial/amba-pl010.c
+++ b/drivers/tty/serial/amba-pl010.c
@@ -697,6 +697,7 @@ static struct console amba_console = {
#define AMBA_CONSOLE NULL
#endif
+static DEFINE_MUTEX(amba_reg_lock);
static struct uart_driver amba_reg = {
.owner = THIS_MODULE,
.driver_name = "ttyAM",
@@ -749,6 +750,19 @@ static int pl010_probe(struct amba_device *dev, const struct amba_id *id)
amba_ports[i] = uap;
amba_set_drvdata(dev, uap);
+
+ mutex_lock(&amba_reg_lock);
+ if (!amba_reg.state) {
+ ret = uart_register_driver(&amba_reg);
+ if (ret < 0) {
+ mutex_unlock(&amba_reg_lock);
+ dev_err(uap->port.dev,
+ "Failed to register AMBA-PL010 driver\n");
+ return ret;
+ }
+ }
+ mutex_unlock(&amba_reg_lock);
+
ret = uart_add_one_port(&amba_reg, &uap->port);
if (ret)
amba_ports[i] = NULL;
@@ -760,12 +774,18 @@ static int pl010_remove(struct amba_device *dev)
{
struct uart_amba_port *uap = amba_get_drvdata(dev);
int i;
+ bool busy = false;
uart_remove_one_port(&amba_reg, &uap->port);
for (i = 0; i < ARRAY_SIZE(amba_ports); i++)
if (amba_ports[i] == uap)
amba_ports[i] = NULL;
+ else if (amba_ports[i])
+ busy = true;
+
+ if (!busy)
+ uart_unregister_driver(&amba_reg);
return 0;
}
@@ -794,7 +814,7 @@ static int pl010_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(pl010_dev_pm_ops, pl010_suspend, pl010_resume);
-static struct amba_id pl010_ids[] = {
+static const struct amba_id pl010_ids[] = {
{
.id = 0x00041010,
.mask = 0x000fffff,
@@ -816,23 +836,14 @@ static struct amba_driver pl010_driver = {
static int __init pl010_init(void)
{
- int ret;
-
printk(KERN_INFO "Serial: AMBA driver\n");
- ret = uart_register_driver(&amba_reg);
- if (ret == 0) {
- ret = amba_driver_register(&pl010_driver);
- if (ret)
- uart_unregister_driver(&amba_reg);
- }
- return ret;
+ return amba_driver_register(&pl010_driver);
}
static void __exit pl010_exit(void)
{
amba_driver_unregister(&pl010_driver);
- uart_unregister_driver(&amba_reg);
}
module_init(pl010_init);
diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c
index 8a857bb34fbb..111e6a950779 100644
--- a/drivers/tty/serial/amba-pl011.c
+++ b/drivers/tty/serial/amba-pl011.c
@@ -128,7 +128,7 @@ static struct vendor_data vendor_arm = {
.get_fifosize = get_fifosize_arm,
};
-static struct vendor_data vendor_sbsa = {
+static const struct vendor_data vendor_sbsa = {
.reg_offset = pl011_std_offsets,
.fr_busy = UART01x_FR_BUSY,
.fr_dsr = UART01x_FR_DSR,
@@ -142,16 +142,8 @@ static struct vendor_data vendor_sbsa = {
.fixed_options = true,
};
-/*
- * Erratum 44 for QDF2432v1 and QDF2400v1 SoCs describes the BUSY bit as
- * occasionally getting stuck as 1. To avoid the potential for a hang, check
- * TXFE == 0 instead of BUSY == 1. This may not be suitable for all UART
- * implementations, so only do so if an affected platform is detected in
- * parse_spcr().
- */
-static bool qdf2400_e44_present = false;
-
-static struct vendor_data vendor_qdt_qdf2400_e44 = {
+#ifdef CONFIG_ACPI_SPCR_TABLE
+static const struct vendor_data vendor_qdt_qdf2400_e44 = {
.reg_offset = pl011_std_offsets,
.fr_busy = UART011_FR_TXFE,
.fr_dsr = UART01x_FR_DSR,
@@ -165,6 +157,7 @@ static struct vendor_data vendor_qdt_qdf2400_e44 = {
.always_enabled = true,
.fixed_options = true,
};
+#endif
static u16 pl011_st_offsets[REG_ARRAY_SIZE] = {
[REG_DR] = UART01x_DR,
@@ -2375,12 +2368,14 @@ static int __init pl011_console_match(struct console *co, char *name, int idx,
resource_size_t addr;
int i;
- if (strcmp(name, "qdf2400_e44") == 0) {
- pr_info_once("UART: Working around QDF2400 SoC erratum 44");
- qdf2400_e44_present = true;
- } else if (strcmp(name, "pl011") != 0) {
+ /*
+ * Systems affected by the Qualcomm Technologies QDF2400 E44 erratum
+ * have a distinct console name, so make sure we check for that.
+ * The actual implementation of the erratum occurs in the probe
+ * function.
+ */
+ if ((strcmp(name, "qdf2400_e44") != 0) && (strcmp(name, "pl011") != 0))
return -ENODEV;
- }
if (uart_parse_earlycon(options, &iotype, &addr, &options))
return -ENODEV;
@@ -2734,11 +2729,17 @@ static int sbsa_uart_probe(struct platform_device *pdev)
}
uap->port.irq = ret;
- uap->reg_offset = vendor_sbsa.reg_offset;
- uap->vendor = qdf2400_e44_present ?
- &vendor_qdt_qdf2400_e44 : &vendor_sbsa;
+#ifdef CONFIG_ACPI_SPCR_TABLE
+ if (qdf2400_e44_present) {
+ dev_info(&pdev->dev, "working around QDF2400 SoC erratum 44\n");
+ uap->vendor = &vendor_qdt_qdf2400_e44;
+ } else
+#endif
+ uap->vendor = &vendor_sbsa;
+
+ uap->reg_offset = uap->vendor->reg_offset;
uap->fifosize = 32;
- uap->port.iotype = vendor_sbsa.access_32b ? UPIO_MEM32 : UPIO_MEM;
+ uap->port.iotype = uap->vendor->access_32b ? UPIO_MEM32 : UPIO_MEM;
uap->port.ops = &sbsa_uart_pops;
uap->fixed_baud = baudrate;
@@ -2786,7 +2787,7 @@ static struct platform_driver arm_sbsa_uart_platform_driver = {
},
};
-static struct amba_id pl011_ids[] = {
+static const struct amba_id pl011_ids[] = {
{
.id = 0x00041011,
.mask = 0x000fffff,
diff --git a/drivers/tty/serial/amba-pl011.h b/drivers/tty/serial/amba-pl011.h
index 411c60e1f9a4..077eb12a3472 100644
--- a/drivers/tty/serial/amba-pl011.h
+++ b/drivers/tty/serial/amba-pl011.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef AMBA_PL011_H
#define AMBA_PL011_H
diff --git a/drivers/tty/serial/apbuart.c b/drivers/tty/serial/apbuart.c
index 75eb083b3361..dd60ed96a0ad 100644
--- a/drivers/tty/serial/apbuart.c
+++ b/drivers/tty/serial/apbuart.c
@@ -325,7 +325,7 @@ static int apbuart_verify_port(struct uart_port *port,
return ret;
}
-static struct uart_ops grlib_apbuart_ops = {
+static const struct uart_ops grlib_apbuart_ops = {
.tx_empty = apbuart_tx_empty,
.set_mctrl = apbuart_set_mctrl,
.get_mctrl = apbuart_get_mctrl,
diff --git a/drivers/tty/serial/apbuart.h b/drivers/tty/serial/apbuart.h
index 5faf87c8d2bc..81baf007694f 100644
--- a/drivers/tty/serial/apbuart.h
+++ b/drivers/tty/serial/apbuart.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __GRLIB_APBUART_H__
#define __GRLIB_APBUART_H__
diff --git a/drivers/tty/serial/arc_uart.c b/drivers/tty/serial/arc_uart.c
index 5ac06fcaa9c6..77fe306690c4 100644
--- a/drivers/tty/serial/arc_uart.c
+++ b/drivers/tty/serial/arc_uart.c
@@ -549,8 +549,8 @@ static struct console arc_console = {
.data = &arc_uart_driver
};
-static __init void arc_early_serial_write(struct console *con, const char *s,
- unsigned int n)
+static void arc_early_serial_write(struct console *con, const char *s,
+ unsigned int n)
{
struct earlycon_device *dev = con->data;
diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
index c355ac9abafc..7551cab438ff 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -1,5 +1,5 @@
/*
- * Driver for Atmel AT91 / AT32 Serial ports
+ * Driver for Atmel AT91 Serial ports
* Copyright (C) 2003 Rick Bronson
*
* Based on drivers/char/serial_sa1100.c, by Deep Blue Solutions Ltd.
@@ -46,6 +46,7 @@
#include <linux/err.h>
#include <linux/irq.h>
#include <linux/suspend.h>
+#include <linux/mm.h>
#include <asm/io.h>
#include <asm/ioctls.h>
@@ -118,7 +119,6 @@ struct atmel_uart_char {
/*
* at91: 6 USARTs and one DBGU port (SAM9260)
- * avr32: 4
* samx7: 3 USARTs and 5 UARTs
*/
#define ATMEL_MAX_UART 8
@@ -228,21 +228,6 @@ static inline void atmel_uart_writel(struct uart_port *port, u32 reg, u32 value)
__raw_writel(value, port->membase + reg);
}
-#ifdef CONFIG_AVR32
-
-/* AVR32 cannot handle 8 or 16bit I/O accesses but only 32bit I/O accesses */
-static inline u8 atmel_uart_read_char(struct uart_port *port)
-{
- return __raw_readl(port->membase + ATMEL_US_RHR);
-}
-
-static inline void atmel_uart_write_char(struct uart_port *port, u8 value)
-{
- __raw_writel(value, port->membase + ATMEL_US_THR);
-}
-
-#else
-
static inline u8 atmel_uart_read_char(struct uart_port *port)
{
return __raw_readb(port->membase + ATMEL_US_RHR);
@@ -253,8 +238,6 @@ static inline void atmel_uart_write_char(struct uart_port *port, u8 value)
__raw_writeb(value, port->membase + ATMEL_US_THR);
}
-#endif
-
#ifdef CONFIG_SERIAL_ATMEL_PDC
static bool atmel_use_pdc_rx(struct uart_port *port)
{
@@ -959,7 +942,7 @@ static int atmel_prepare_tx_dma(struct uart_port *port)
sg_set_page(&atmel_port->sg_tx,
virt_to_page(port->state->xmit.buf),
UART_XMIT_SIZE,
- (unsigned long)port->state->xmit.buf & ~PAGE_MASK);
+ offset_in_page(port->state->xmit.buf));
nent = dma_map_sg(port->dev,
&atmel_port->sg_tx,
1,
@@ -1141,7 +1124,7 @@ static int atmel_prepare_rx_dma(struct uart_port *port)
sg_set_page(&atmel_port->sg_rx,
virt_to_page(ring->buf),
sizeof(struct atmel_uart_char) * ATMEL_SERIAL_RINGSIZE,
- (unsigned long)ring->buf & ~PAGE_MASK);
+ offset_in_page(ring->buf));
nent = dma_map_sg(port->dev,
&atmel_port->sg_rx,
1,
@@ -1655,72 +1638,56 @@ static void atmel_init_property(struct atmel_uart_port *atmel_port,
struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
- struct atmel_uart_data *pdata = dev_get_platdata(&pdev->dev);
-
- if (np) {
- /* DMA/PDC usage specification */
- if (of_property_read_bool(np, "atmel,use-dma-rx")) {
- if (of_property_read_bool(np, "dmas")) {
- atmel_port->use_dma_rx = true;
- atmel_port->use_pdc_rx = false;
- } else {
- atmel_port->use_dma_rx = false;
- atmel_port->use_pdc_rx = true;
- }
+
+ /* DMA/PDC usage specification */
+ if (of_property_read_bool(np, "atmel,use-dma-rx")) {
+ if (of_property_read_bool(np, "dmas")) {
+ atmel_port->use_dma_rx = true;
+ atmel_port->use_pdc_rx = false;
} else {
atmel_port->use_dma_rx = false;
- atmel_port->use_pdc_rx = false;
+ atmel_port->use_pdc_rx = true;
}
+ } else {
+ atmel_port->use_dma_rx = false;
+ atmel_port->use_pdc_rx = false;
+ }
- if (of_property_read_bool(np, "atmel,use-dma-tx")) {
- if (of_property_read_bool(np, "dmas")) {
- atmel_port->use_dma_tx = true;
- atmel_port->use_pdc_tx = false;
- } else {
- atmel_port->use_dma_tx = false;
- atmel_port->use_pdc_tx = true;
- }
+ if (of_property_read_bool(np, "atmel,use-dma-tx")) {
+ if (of_property_read_bool(np, "dmas")) {
+ atmel_port->use_dma_tx = true;
+ atmel_port->use_pdc_tx = false;
} else {
atmel_port->use_dma_tx = false;
- atmel_port->use_pdc_tx = false;
+ atmel_port->use_pdc_tx = true;
}
-
} else {
- atmel_port->use_pdc_rx = pdata->use_dma_rx;
- atmel_port->use_pdc_tx = pdata->use_dma_tx;
- atmel_port->use_dma_rx = false;
atmel_port->use_dma_tx = false;
+ atmel_port->use_pdc_tx = false;
}
-
}
static void atmel_init_rs485(struct uart_port *port,
struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
- struct atmel_uart_data *pdata = dev_get_platdata(&pdev->dev);
-
- if (np) {
- struct serial_rs485 *rs485conf = &port->rs485;
- u32 rs485_delay[2];
- /* rs485 properties */
- if (of_property_read_u32_array(np, "rs485-rts-delay",
- rs485_delay, 2) == 0) {
- rs485conf->delay_rts_before_send = rs485_delay[0];
- rs485conf->delay_rts_after_send = rs485_delay[1];
- rs485conf->flags = 0;
- }
- if (of_get_property(np, "rs485-rx-during-tx", NULL))
- rs485conf->flags |= SER_RS485_RX_DURING_TX;
+ struct serial_rs485 *rs485conf = &port->rs485;
+ u32 rs485_delay[2];
- if (of_get_property(np, "linux,rs485-enabled-at-boot-time",
- NULL))
- rs485conf->flags |= SER_RS485_ENABLED;
- } else {
- port->rs485 = pdata->rs485;
+ /* rs485 properties */
+ if (of_property_read_u32_array(np, "rs485-rts-delay",
+ rs485_delay, 2) == 0) {
+ rs485conf->delay_rts_before_send = rs485_delay[0];
+ rs485conf->delay_rts_after_send = rs485_delay[1];
+ rs485conf->flags = 0;
}
+ if (of_get_property(np, "rs485-rx-during-tx", NULL))
+ rs485conf->flags |= SER_RS485_RX_DURING_TX;
+
+ if (of_get_property(np, "linux,rs485-enabled-at-boot-time", NULL))
+ rs485conf->flags |= SER_RS485_ENABLED;
}
static void atmel_set_ops(struct uart_port *port)
@@ -2402,7 +2369,6 @@ static int atmel_init_port(struct atmel_uart_port *atmel_port,
{
int ret;
struct uart_port *port = &atmel_port->uart;
- struct atmel_uart_data *pdata = dev_get_platdata(&pdev->dev);
atmel_init_property(atmel_port, pdev);
atmel_set_ops(port);
@@ -2410,24 +2376,17 @@ static int atmel_init_port(struct atmel_uart_port *atmel_port,
atmel_init_rs485(port, pdev);
port->iotype = UPIO_MEM;
- port->flags = UPF_BOOT_AUTOCONF;
+ port->flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP;
port->ops = &atmel_pops;
port->fifosize = 1;
port->dev = &pdev->dev;
port->mapbase = pdev->resource[0].start;
port->irq = pdev->resource[1].start;
port->rs485_config = atmel_config_rs485;
+ port->membase = NULL;
memset(&atmel_port->rx_ring, 0, sizeof(atmel_port->rx_ring));
- if (pdata && pdata->regs) {
- /* Already mapped by setup code */
- port->membase = pdata->regs;
- } else {
- port->flags |= UPF_IOREMAP;
- port->membase = NULL;
- }
-
/* for console, the clock could already be configured */
if (!atmel_port->clk) {
atmel_port->clk = clk_get(&pdev->dev, "usart");
@@ -2460,8 +2419,6 @@ static int atmel_init_port(struct atmel_uart_port *atmel_port,
return 0;
}
-struct platform_device *atmel_default_console_device; /* the serial console device */
-
#ifdef CONFIG_SERIAL_ATMEL_CONSOLE
static void atmel_console_putchar(struct uart_port *port, int ch)
{
@@ -2594,47 +2551,6 @@ static struct console atmel_console = {
#define ATMEL_CONSOLE_DEVICE (&atmel_console)
-/*
- * Early console initialization (before VM subsystem initialized).
- */
-static int __init atmel_console_init(void)
-{
- int ret;
- if (atmel_default_console_device) {
- struct atmel_uart_data *pdata =
- dev_get_platdata(&atmel_default_console_device->dev);
- int id = pdata->num;
- struct atmel_uart_port *atmel_port = &atmel_ports[id];
-
- atmel_port->backup_imr = 0;
- atmel_port->uart.line = id;
-
- add_preferred_console(ATMEL_DEVICENAME, id, NULL);
- ret = atmel_init_port(atmel_port, atmel_default_console_device);
- if (ret)
- return ret;
- register_console(&atmel_console);
- }
-
- return 0;
-}
-
-console_initcall(atmel_console_init);
-
-/*
- * Late console initialization.
- */
-static int __init atmel_late_console_init(void)
-{
- if (atmel_default_console_device
- && !(atmel_console.flags & CON_ENABLED))
- register_console(&atmel_console);
-
- return 0;
-}
-
-core_initcall(atmel_late_console_init);
-
static inline bool atmel_is_console_port(struct uart_port *port)
{
return port->cons && port->cons->index == port->line;
@@ -2804,19 +2720,13 @@ static int atmel_serial_probe(struct platform_device *pdev)
{
struct atmel_uart_port *atmel_port;
struct device_node *np = pdev->dev.of_node;
- struct atmel_uart_data *pdata = dev_get_platdata(&pdev->dev);
void *data;
int ret = -ENODEV;
bool rs485_enabled;
BUILD_BUG_ON(ATMEL_SERIAL_RINGSIZE & (ATMEL_SERIAL_RINGSIZE - 1));
- if (np)
- ret = of_alias_get_id(np, "serial");
- else
- if (pdata)
- ret = pdata->num;
-
+ ret = of_alias_get_id(np, "serial");
if (ret < 0)
/* port id not found in platform data nor device-tree aliases:
* auto-enumerate it */
diff --git a/drivers/tty/serial/bcm63xx_uart.c b/drivers/tty/serial/bcm63xx_uart.c
index 583c9a0c7ecc..8c48c3784831 100644
--- a/drivers/tty/serial/bcm63xx_uart.c
+++ b/drivers/tty/serial/bcm63xx_uart.c
@@ -507,9 +507,14 @@ static void bcm_uart_set_termios(struct uart_port *port,
{
unsigned int ctl, baud, quot, ier;
unsigned long flags;
+ int tries;
spin_lock_irqsave(&port->lock, flags);
+ /* Drain the hot tub fully before we power it off for the winter. */
+ for (tries = 3; !bcm_uart_tx_empty(port) && tries; tries--)
+ mdelay(10);
+
/* disable uart while changing speed */
bcm_uart_disable(port);
bcm_uart_flush(port);
diff --git a/drivers/tty/serial/cpm_uart/cpm_uart_core.c b/drivers/tty/serial/cpm_uart/cpm_uart_core.c
index f6bcc19c99d5..9ac142cfc1f1 100644
--- a/drivers/tty/serial/cpm_uart/cpm_uart_core.c
+++ b/drivers/tty/serial/cpm_uart/cpm_uart_core.c
@@ -1123,7 +1123,7 @@ static void cpm_put_poll_char(struct uart_port *port,
}
#endif /* CONFIG_CONSOLE_POLL */
-static struct uart_ops cpm_uart_pops = {
+static const struct uart_ops cpm_uart_pops = {
.tx_empty = cpm_uart_tx_empty,
.set_mctrl = cpm_uart_set_mctrl,
.get_mctrl = cpm_uart_get_mctrl,
diff --git a/drivers/tty/serial/cpm_uart/cpm_uart_cpm1.h b/drivers/tty/serial/cpm_uart/cpm_uart_cpm1.h
index 60c7e94cde1e..18ec0849918a 100644
--- a/drivers/tty/serial/cpm_uart/cpm_uart_cpm1.h
+++ b/drivers/tty/serial/cpm_uart/cpm_uart_cpm1.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* Driver for CPM (SCC/SMC) serial ports
*
diff --git a/drivers/tty/serial/cpm_uart/cpm_uart_cpm2.h b/drivers/tty/serial/cpm_uart/cpm_uart_cpm2.h
index 51e651a69938..051a8509c3e5 100644
--- a/drivers/tty/serial/cpm_uart/cpm_uart_cpm2.h
+++ b/drivers/tty/serial/cpm_uart/cpm_uart_cpm2.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* Driver for CPM (SCC/SMC) serial ports
*
diff --git a/drivers/tty/serial/crisv10.c b/drivers/tty/serial/crisv10.c
index 59a2a7e18b5a..1421804975e0 100644
--- a/drivers/tty/serial/crisv10.c
+++ b/drivers/tty/serial/crisv10.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Serial port driver for the ETRAX 100LX chip
*
diff --git a/drivers/tty/serial/crisv10.h b/drivers/tty/serial/crisv10.h
index 15a52ee58251..79ba2bc95d3d 100644
--- a/drivers/tty/serial/crisv10.h
+++ b/drivers/tty/serial/crisv10.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* serial.h: Arch-dep definitions for the Etrax100 serial driver.
*
diff --git a/drivers/tty/serial/dz.h b/drivers/tty/serial/dz.h
index faf169ed27b3..3b3e31954f24 100644
--- a/drivers/tty/serial/dz.h
+++ b/drivers/tty/serial/dz.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* dz.h: Serial port driver for DECstations equipped
* with the DZ chipset.
diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c
index c3651540e1ba..98928f082d87 100644
--- a/drivers/tty/serial/earlycon.c
+++ b/drivers/tty/serial/earlycon.c
@@ -220,7 +220,7 @@ static int __init param_setup_earlycon(char *buf)
if (IS_ENABLED(CONFIG_ACPI_SPCR_TABLE)) {
earlycon_init_is_deferred = true;
return 0;
- } else {
+ } else if (!buf) {
return early_init_dt_scan_chosen_stdout();
}
}
@@ -282,7 +282,12 @@ int __init of_setup_earlycon(const struct earlycon_id *match,
}
}
+ val = of_get_flat_dt_prop(node, "current-speed", NULL);
+ if (val)
+ early_console_dev.baud = be32_to_cpu(*val);
+
if (options) {
+ early_console_dev.baud = simple_strtoul(options, NULL, 0);
strlcpy(early_console_dev.options, options,
sizeof(early_console_dev.options));
}
diff --git a/drivers/tty/serial/etraxfs-uart.c b/drivers/tty/serial/etraxfs-uart.c
index 2f80bc7e44fb..24bf6bfb29b4 100644
--- a/drivers/tty/serial/etraxfs-uart.c
+++ b/drivers/tty/serial/etraxfs-uart.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
#include <linux/module.h>
#include <linux/init.h>
#include <linux/console.h>
diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index 15df1ba78095..f0252184291e 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -117,7 +117,7 @@
#define UARTSFIFO_TXOF 0x02
#define UARTSFIFO_RXUF 0x01
-/* 32-bit register defination */
+/* 32-bit register definition */
#define UARTBAUD 0x00
#define UARTSTAT 0x04
#define UARTCTRL 0x08
@@ -140,6 +140,8 @@
#define UARTBAUD_SBNS 0x00002000
#define UARTBAUD_SBR 0x00000000
#define UARTBAUD_SBR_MASK 0x1fff
+#define UARTBAUD_OSR_MASK 0x1f
+#define UARTBAUD_OSR_SHIFT 24
#define UARTSTAT_LBKDIF 0x80000000
#define UARTSTAT_RXEDGIF 0x40000000
@@ -231,12 +233,14 @@
#define DEV_NAME "ttyLP"
#define UART_NR 6
+/* IMX lpuart has four extra unused regs located at the beginning */
+#define IMX_REG_OFF 0x10
+
struct lpuart_port {
struct uart_port port;
struct clk *clk;
unsigned int txfifo_size;
unsigned int rxfifo_size;
- bool lpuart32;
bool lpuart_dma_tx_use;
bool lpuart_dma_rx_use;
@@ -258,13 +262,28 @@ struct lpuart_port {
wait_queue_head_t dma_wait;
};
+struct lpuart_soc_data {
+ char iotype;
+ u8 reg_off;
+};
+
+static const struct lpuart_soc_data vf_data = {
+ .iotype = UPIO_MEM,
+};
+
+static const struct lpuart_soc_data ls_data = {
+ .iotype = UPIO_MEM32BE,
+};
+
+static struct lpuart_soc_data imx_data = {
+ .iotype = UPIO_MEM32,
+ .reg_off = IMX_REG_OFF,
+};
+
static const struct of_device_id lpuart_dt_ids[] = {
- {
- .compatible = "fsl,vf610-lpuart",
- },
- {
- .compatible = "fsl,ls1021a-lpuart",
- },
+ { .compatible = "fsl,vf610-lpuart", .data = &vf_data, },
+ { .compatible = "fsl,ls1021a-lpuart", .data = &ls_data, },
+ { .compatible = "fsl,imx7ulp-lpuart", .data = &imx_data, },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, lpuart_dt_ids);
@@ -272,14 +291,29 @@ MODULE_DEVICE_TABLE(of, lpuart_dt_ids);
/* Forward declare this for the dma callbacks*/
static void lpuart_dma_tx_complete(void *arg);
-static u32 lpuart32_read(void __iomem *addr)
+static inline u32 lpuart32_read(struct uart_port *port, u32 off)
{
- return ioread32be(addr);
+ switch (port->iotype) {
+ case UPIO_MEM32:
+ return readl(port->membase + off);
+ case UPIO_MEM32BE:
+ return ioread32be(port->membase + off);
+ default:
+ return 0;
+ }
}
-static void lpuart32_write(u32 val, void __iomem *addr)
+static inline void lpuart32_write(struct uart_port *port, u32 val,
+ u32 off)
{
- iowrite32be(val, addr);
+ switch (port->iotype) {
+ case UPIO_MEM32:
+ writel(val, port->membase + off);
+ break;
+ case UPIO_MEM32BE:
+ iowrite32be(val, port->membase + off);
+ break;
+ }
}
static void lpuart_stop_tx(struct uart_port *port)
@@ -295,9 +329,9 @@ static void lpuart32_stop_tx(struct uart_port *port)
{
unsigned long temp;
- temp = lpuart32_read(port->membase + UARTCTRL);
+ temp = lpuart32_read(port, UARTCTRL);
temp &= ~(UARTCTRL_TIE | UARTCTRL_TCIE);
- lpuart32_write(temp, port->membase + UARTCTRL);
+ lpuart32_write(port, temp, UARTCTRL);
}
static void lpuart_stop_rx(struct uart_port *port)
@@ -312,8 +346,8 @@ static void lpuart32_stop_rx(struct uart_port *port)
{
unsigned long temp;
- temp = lpuart32_read(port->membase + UARTCTRL);
- lpuart32_write(temp & ~UARTCTRL_RE, port->membase + UARTCTRL);
+ temp = lpuart32_read(port, UARTCTRL);
+ lpuart32_write(port, temp & ~UARTCTRL_RE, UARTCTRL);
}
static void lpuart_dma_tx(struct lpuart_port *sport)
@@ -487,6 +521,57 @@ static int lpuart_poll_get_char(struct uart_port *port)
return readb(port->membase + UARTDR);
}
+static int lpuart32_poll_init(struct uart_port *port)
+{
+ unsigned long flags;
+ struct lpuart_port *sport = container_of(port, struct lpuart_port, port);
+ u32 temp;
+
+ sport->port.fifosize = 0;
+
+ spin_lock_irqsave(&sport->port.lock, flags);
+
+ /* Disable Rx & Tx */
+ writel(0, sport->port.membase + UARTCTRL);
+
+ temp = readl(sport->port.membase + UARTFIFO);
+
+ /* Enable Rx and Tx FIFO */
+ writel(temp | UARTFIFO_RXFE | UARTFIFO_TXFE,
+ sport->port.membase + UARTFIFO);
+
+ /* flush Tx and Rx FIFO */
+ writel(UARTFIFO_TXFLUSH | UARTFIFO_RXFLUSH,
+ sport->port.membase + UARTFIFO);
+
+ /* explicitly clear RDRF */
+ if (readl(sport->port.membase + UARTSTAT) & UARTSTAT_RDRF) {
+ readl(sport->port.membase + UARTDATA);
+ writel(UARTFIFO_RXUF, sport->port.membase + UARTFIFO);
+ }
+
+ /* Enable Rx and Tx */
+ writel(UARTCTRL_RE | UARTCTRL_TE, sport->port.membase + UARTCTRL);
+ spin_unlock_irqrestore(&sport->port.lock, flags);
+
+ return 0;
+}
+
+static void lpuart32_poll_put_char(struct uart_port *port, unsigned char c)
+{
+ while (!(readl(port->membase + UARTSTAT) & UARTSTAT_TDRE))
+ barrier();
+
+ writel(c, port->membase + UARTDATA);
+}
+
+static int lpuart32_poll_get_char(struct uart_port *port)
+{
+ if (!(readl(port->membase + UARTSTAT) & UARTSTAT_RDRF))
+ return NO_POLL_CHAR;
+
+ return readl(port->membase + UARTDATA);
+}
#endif
static inline void lpuart_transmit_buffer(struct lpuart_port *sport)
@@ -512,14 +597,14 @@ static inline void lpuart32_transmit_buffer(struct lpuart_port *sport)
struct circ_buf *xmit = &sport->port.state->xmit;
unsigned long txcnt;
- txcnt = lpuart32_read(sport->port.membase + UARTWATER);
+ txcnt = lpuart32_read(&sport->port, UARTWATER);
txcnt = txcnt >> UARTWATER_TXCNT_OFF;
txcnt &= UARTWATER_COUNT_MASK;
while (!uart_circ_empty(xmit) && (txcnt < sport->txfifo_size)) {
- lpuart32_write(xmit->buf[xmit->tail], sport->port.membase + UARTDATA);
+ lpuart32_write(&sport->port, xmit->buf[xmit->tail], UARTDATA);
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
sport->port.icount.tx++;
- txcnt = lpuart32_read(sport->port.membase + UARTWATER);
+ txcnt = lpuart32_read(&sport->port, UARTWATER);
txcnt = txcnt >> UARTWATER_TXCNT_OFF;
txcnt &= UARTWATER_COUNT_MASK;
}
@@ -555,10 +640,10 @@ static void lpuart32_start_tx(struct uart_port *port)
struct lpuart_port *sport = container_of(port, struct lpuart_port, port);
unsigned long temp;
- temp = lpuart32_read(port->membase + UARTCTRL);
- lpuart32_write(temp | UARTCTRL_TIE, port->membase + UARTCTRL);
+ temp = lpuart32_read(port, UARTCTRL);
+ lpuart32_write(port, temp | UARTCTRL_TIE, UARTCTRL);
- if (lpuart32_read(port->membase + UARTSTAT) & UARTSTAT_TDRE)
+ if (lpuart32_read(port, UARTSTAT) & UARTSTAT_TDRE)
lpuart32_transmit_buffer(sport);
}
@@ -581,10 +666,16 @@ static unsigned int lpuart_tx_empty(struct uart_port *port)
static unsigned int lpuart32_tx_empty(struct uart_port *port)
{
- return (lpuart32_read(port->membase + UARTSTAT) & UARTSTAT_TC) ?
+ return (lpuart32_read(port, UARTSTAT) & UARTSTAT_TC) ?
TIOCSER_TEMT : 0;
}
+static bool lpuart_is_32(struct lpuart_port *sport)
+{
+ return sport->port.iotype == UPIO_MEM32 ||
+ sport->port.iotype == UPIO_MEM32BE;
+}
+
static irqreturn_t lpuart_txint(int irq, void *dev_id)
{
struct lpuart_port *sport = dev_id;
@@ -593,22 +684,22 @@ static irqreturn_t lpuart_txint(int irq, void *dev_id)
spin_lock_irqsave(&sport->port.lock, flags);
if (sport->port.x_char) {
- if (sport->lpuart32)
- lpuart32_write(sport->port.x_char, sport->port.membase + UARTDATA);
+ if (lpuart_is_32(sport))
+ lpuart32_write(&sport->port, sport->port.x_char, UARTDATA);
else
writeb(sport->port.x_char, sport->port.membase + UARTDR);
goto out;
}
if (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)) {
- if (sport->lpuart32)
+ if (lpuart_is_32(sport))
lpuart32_stop_tx(&sport->port);
else
lpuart_stop_tx(&sport->port);
goto out;
}
- if (sport->lpuart32)
+ if (lpuart_is_32(sport))
lpuart32_transmit_buffer(sport);
else
lpuart_transmit_buffer(sport);
@@ -694,15 +785,15 @@ static irqreturn_t lpuart32_rxint(int irq, void *dev_id)
spin_lock_irqsave(&sport->port.lock, flags);
- while (!(lpuart32_read(sport->port.membase + UARTFIFO) & UARTFIFO_RXEMPT)) {
+ while (!(lpuart32_read(&sport->port, UARTFIFO) & UARTFIFO_RXEMPT)) {
flg = TTY_NORMAL;
sport->port.icount.rx++;
/*
* to clear the FE, OR, NF, FE, PE flags,
* read STAT then read DATA reg
*/
- sr = lpuart32_read(sport->port.membase + UARTSTAT);
- rx = lpuart32_read(sport->port.membase + UARTDATA);
+ sr = lpuart32_read(&sport->port, UARTSTAT);
+ rx = lpuart32_read(&sport->port, UARTDATA);
rx &= 0x3ff;
if (uart_handle_sysrq_char(&sport->port, (unsigned char)rx))
@@ -769,18 +860,18 @@ static irqreturn_t lpuart32_int(int irq, void *dev_id)
struct lpuart_port *sport = dev_id;
unsigned long sts, rxcount;
- sts = lpuart32_read(sport->port.membase + UARTSTAT);
- rxcount = lpuart32_read(sport->port.membase + UARTWATER);
+ sts = lpuart32_read(&sport->port, UARTSTAT);
+ rxcount = lpuart32_read(&sport->port, UARTWATER);
rxcount = rxcount >> UARTWATER_RXCNT_OFF;
if (sts & UARTSTAT_RDRF || rxcount > 0)
lpuart32_rxint(irq, dev_id);
if ((sts & UARTSTAT_TDRE) &&
- !(lpuart32_read(sport->port.membase + UARTBAUD) & UARTBAUD_TDMAE))
+ !(lpuart32_read(&sport->port, UARTBAUD) & UARTBAUD_TDMAE))
lpuart_txint(irq, dev_id);
- lpuart32_write(sts, sport->port.membase + UARTSTAT);
+ lpuart32_write(&sport->port, sts, UARTSTAT);
return IRQ_HANDLED;
}
@@ -985,6 +1076,11 @@ static int lpuart_config_rs485(struct uart_port *port,
~(UARTMODEM_TXRTSPOL | UARTMODEM_TXRTSE);
writeb(modem, sport->port.membase + UARTMODEM);
+ /* clear unsupported configurations */
+ rs485->delay_rts_before_send = 0;
+ rs485->delay_rts_after_send = 0;
+ rs485->flags &= ~SER_RS485_RX_DURING_TX;
+
if (rs485->flags & SER_RS485_ENABLED) {
/* Enable auto RS-485 RTS mode */
modem |= UARTMODEM_TXRTSE;
@@ -1041,7 +1137,7 @@ static unsigned int lpuart32_get_mctrl(struct uart_port *port)
unsigned int temp = 0;
unsigned long reg;
- reg = lpuart32_read(port->membase + UARTMODIR);
+ reg = lpuart32_read(port, UARTMODIR);
if (reg & UARTMODIR_TXCTSE)
temp |= TIOCM_CTS;
@@ -1076,7 +1172,7 @@ static void lpuart32_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
unsigned long temp;
- temp = lpuart32_read(port->membase + UARTMODIR) &
+ temp = lpuart32_read(port, UARTMODIR) &
~(UARTMODIR_RXRTSE | UARTMODIR_TXCTSE);
if (mctrl & TIOCM_RTS)
@@ -1085,7 +1181,7 @@ static void lpuart32_set_mctrl(struct uart_port *port, unsigned int mctrl)
if (mctrl & TIOCM_CTS)
temp |= UARTMODIR_TXCTSE;
- lpuart32_write(temp, port->membase + UARTMODIR);
+ lpuart32_write(port, temp, UARTMODIR);
}
static void lpuart_break_ctl(struct uart_port *port, int break_state)
@@ -1104,12 +1200,12 @@ static void lpuart32_break_ctl(struct uart_port *port, int break_state)
{
unsigned long temp;
- temp = lpuart32_read(port->membase + UARTCTRL) & ~UARTCTRL_SBK;
+ temp = lpuart32_read(port, UARTCTRL) & ~UARTCTRL_SBK;
if (break_state != 0)
temp |= UARTCTRL_SBK;
- lpuart32_write(temp, port->membase + UARTCTRL);
+ lpuart32_write(port, temp, UARTCTRL);
}
static void lpuart_setup_watermark(struct lpuart_port *sport)
@@ -1149,24 +1245,24 @@ static void lpuart32_setup_watermark(struct lpuart_port *sport)
unsigned long val, ctrl;
unsigned long ctrl_saved;
- ctrl = lpuart32_read(sport->port.membase + UARTCTRL);
+ ctrl = lpuart32_read(&sport->port, UARTCTRL);
ctrl_saved = ctrl;
ctrl &= ~(UARTCTRL_TIE | UARTCTRL_TCIE | UARTCTRL_TE |
UARTCTRL_RIE | UARTCTRL_RE);
- lpuart32_write(ctrl, sport->port.membase + UARTCTRL);
+ lpuart32_write(&sport->port, ctrl, UARTCTRL);
/* enable FIFO mode */
- val = lpuart32_read(sport->port.membase + UARTFIFO);
+ val = lpuart32_read(&sport->port, UARTFIFO);
val |= UARTFIFO_TXFE | UARTFIFO_RXFE;
val |= UARTFIFO_TXFLUSH | UARTFIFO_RXFLUSH;
- lpuart32_write(val, sport->port.membase + UARTFIFO);
+ lpuart32_write(&sport->port, val, UARTFIFO);
/* set the watermark */
val = (0x1 << UARTWATER_RXWATER_OFF) | (0x0 << UARTWATER_TXWATER_OFF);
- lpuart32_write(val, sport->port.membase + UARTWATER);
+ lpuart32_write(&sport->port, val, UARTWATER);
/* Restore cr2 */
- lpuart32_write(ctrl_saved, sport->port.membase + UARTCTRL);
+ lpuart32_write(&sport->port, ctrl_saved, UARTCTRL);
}
static void rx_dma_timer_init(struct lpuart_port *sport)
@@ -1180,7 +1276,6 @@ static void rx_dma_timer_init(struct lpuart_port *sport)
static int lpuart_startup(struct uart_port *port)
{
struct lpuart_port *sport = container_of(port, struct lpuart_port, port);
- int ret;
unsigned long flags;
unsigned char temp;
@@ -1195,11 +1290,6 @@ static int lpuart_startup(struct uart_port *port)
sport->rxfifo_size = 0x1 << (((temp >> UARTPFIFO_RXSIZE_OFF) &
UARTPFIFO_FIFOSIZE_MASK) + 1);
- ret = devm_request_irq(port->dev, port->irq, lpuart_int, 0,
- DRIVER_NAME, sport);
- if (ret)
- return ret;
-
spin_lock_irqsave(&sport->port.lock, flags);
lpuart_setup_watermark(sport);
@@ -1237,12 +1327,11 @@ static int lpuart_startup(struct uart_port *port)
static int lpuart32_startup(struct uart_port *port)
{
struct lpuart_port *sport = container_of(port, struct lpuart_port, port);
- int ret;
unsigned long flags;
unsigned long temp;
/* determine FIFO size */
- temp = lpuart32_read(sport->port.membase + UARTFIFO);
+ temp = lpuart32_read(&sport->port, UARTFIFO);
sport->txfifo_size = 0x1 << (((temp >> UARTFIFO_TXSIZE_OFF) &
UARTFIFO_FIFOSIZE_MASK) - 1);
@@ -1250,19 +1339,14 @@ static int lpuart32_startup(struct uart_port *port)
sport->rxfifo_size = 0x1 << (((temp >> UARTFIFO_RXSIZE_OFF) &
UARTFIFO_FIFOSIZE_MASK) - 1);
- ret = devm_request_irq(port->dev, port->irq, lpuart32_int, 0,
- DRIVER_NAME, sport);
- if (ret)
- return ret;
-
spin_lock_irqsave(&sport->port.lock, flags);
lpuart32_setup_watermark(sport);
- temp = lpuart32_read(sport->port.membase + UARTCTRL);
+ temp = lpuart32_read(&sport->port, UARTCTRL);
temp |= (UARTCTRL_RIE | UARTCTRL_TIE | UARTCTRL_RE | UARTCTRL_TE);
temp |= UARTCTRL_ILIE;
- lpuart32_write(temp, sport->port.membase + UARTCTRL);
+ lpuart32_write(&sport->port, temp, UARTCTRL);
spin_unlock_irqrestore(&sport->port.lock, flags);
return 0;
@@ -1284,8 +1368,6 @@ static void lpuart_shutdown(struct uart_port *port)
spin_unlock_irqrestore(&port->lock, flags);
- devm_free_irq(port->dev, port->irq, sport);
-
if (sport->lpuart_dma_rx_use) {
del_timer_sync(&sport->lpuart_timer);
lpuart_dma_rx_free(&sport->port);
@@ -1304,21 +1386,18 @@ static void lpuart_shutdown(struct uart_port *port)
static void lpuart32_shutdown(struct uart_port *port)
{
- struct lpuart_port *sport = container_of(port, struct lpuart_port, port);
unsigned long temp;
unsigned long flags;
spin_lock_irqsave(&port->lock, flags);
/* disable Rx/Tx and interrupts */
- temp = lpuart32_read(port->membase + UARTCTRL);
+ temp = lpuart32_read(port, UARTCTRL);
temp &= ~(UARTCTRL_TE | UARTCTRL_RE |
UARTCTRL_TIE | UARTCTRL_TCIE | UARTCTRL_RIE);
- lpuart32_write(temp, port->membase + UARTCTRL);
+ lpuart32_write(port, temp, UARTCTRL);
spin_unlock_irqrestore(&port->lock, flags);
-
- devm_free_irq(port->dev, port->irq, sport);
}
static void
@@ -1479,6 +1558,75 @@ lpuart_set_termios(struct uart_port *port, struct ktermios *termios,
}
static void
+lpuart32_serial_setbrg(struct lpuart_port *sport, unsigned int baudrate)
+{
+ u32 sbr, osr, baud_diff, tmp_osr, tmp_sbr, tmp_diff, tmp;
+ u32 clk = sport->port.uartclk;
+
+ /*
+ * The idea is to use the best OSR (over-sampling rate) possible.
+ * Note, OSR is typically hard-set to 16 in other LPUART instantiations.
+ * Loop to find the best OSR value possible, one that generates minimum
+ * baud_diff iterate through the rest of the supported values of OSR.
+ *
+ * Calculation Formula:
+ * Baud Rate = baud clock / ((OSR+1) × SBR)
+ */
+ baud_diff = baudrate;
+ osr = 0;
+ sbr = 0;
+
+ for (tmp_osr = 4; tmp_osr <= 32; tmp_osr++) {
+ /* calculate the temporary sbr value */
+ tmp_sbr = (clk / (baudrate * tmp_osr));
+ if (tmp_sbr == 0)
+ tmp_sbr = 1;
+
+ /*
+ * calculate the baud rate difference based on the temporary
+ * osr and sbr values
+ */
+ tmp_diff = clk / (tmp_osr * tmp_sbr) - baudrate;
+
+ /* select best values between sbr and sbr+1 */
+ tmp = clk / (tmp_osr * (tmp_sbr + 1));
+ if (tmp_diff > (baudrate - tmp)) {
+ tmp_diff = baudrate - tmp;
+ tmp_sbr++;
+ }
+
+ if (tmp_diff <= baud_diff) {
+ baud_diff = tmp_diff;
+ osr = tmp_osr;
+ sbr = tmp_sbr;
+
+ if (!baud_diff)
+ break;
+ }
+ }
+
+ /* handle buadrate outside acceptable rate */
+ if (baud_diff > ((baudrate / 100) * 3))
+ dev_warn(sport->port.dev,
+ "unacceptable baud rate difference of more than 3%%\n");
+
+ tmp = lpuart32_read(&sport->port, UARTBAUD);
+
+ if ((osr > 3) && (osr < 8))
+ tmp |= UARTBAUD_BOTHEDGE;
+
+ tmp &= ~(UARTBAUD_OSR_MASK << UARTBAUD_OSR_SHIFT);
+ tmp |= (((osr-1) & UARTBAUD_OSR_MASK) << UARTBAUD_OSR_SHIFT);
+
+ tmp &= ~UARTBAUD_SBR_MASK;
+ tmp |= sbr & UARTBAUD_SBR_MASK;
+
+ tmp &= ~(UARTBAUD_TDMAE | UARTBAUD_RDMAE);
+
+ lpuart32_write(&sport->port, tmp, UARTBAUD);
+}
+
+static void
lpuart32_set_termios(struct uart_port *port, struct ktermios *termios,
struct ktermios *old)
{
@@ -1487,11 +1635,10 @@ lpuart32_set_termios(struct uart_port *port, struct ktermios *termios,
unsigned long ctrl, old_ctrl, bd, modem;
unsigned int baud;
unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8;
- unsigned int sbr;
- ctrl = old_ctrl = lpuart32_read(sport->port.membase + UARTCTRL);
- bd = lpuart32_read(sport->port.membase + UARTBAUD);
- modem = lpuart32_read(sport->port.membase + UARTMODIR);
+ ctrl = old_ctrl = lpuart32_read(&sport->port, UARTCTRL);
+ bd = lpuart32_read(&sport->port, UARTBAUD);
+ modem = lpuart32_read(&sport->port, UARTMODIR);
/*
* only support CS8 and CS7, and for CS7 must enable PE.
* supported mode:
@@ -1577,21 +1724,16 @@ lpuart32_set_termios(struct uart_port *port, struct ktermios *termios,
uart_update_timeout(port, termios->c_cflag, baud);
/* wait transmit engin complete */
- while (!(lpuart32_read(sport->port.membase + UARTSTAT) & UARTSTAT_TC))
+ while (!(lpuart32_read(&sport->port, UARTSTAT) & UARTSTAT_TC))
barrier();
/* disable transmit and receive */
- lpuart32_write(old_ctrl & ~(UARTCTRL_TE | UARTCTRL_RE),
- sport->port.membase + UARTCTRL);
+ lpuart32_write(&sport->port, old_ctrl & ~(UARTCTRL_TE | UARTCTRL_RE),
+ UARTCTRL);
- sbr = sport->port.uartclk / (16 * baud);
- bd &= ~UARTBAUD_SBR_MASK;
- bd |= sbr & UARTBAUD_SBR_MASK;
- bd |= UARTBAUD_BOTHEDGE;
- bd &= ~(UARTBAUD_TDMAE | UARTBAUD_RDMAE);
- lpuart32_write(bd, sport->port.membase + UARTBAUD);
- lpuart32_write(modem, sport->port.membase + UARTMODIR);
- lpuart32_write(ctrl, sport->port.membase + UARTCTRL);
+ lpuart32_serial_setbrg(sport, baud);
+ lpuart32_write(&sport->port, modem, UARTMODIR);
+ lpuart32_write(&sport->port, ctrl, UARTCTRL);
/* restore control register */
spin_unlock_irqrestore(&sport->port.lock, flags);
@@ -1679,6 +1821,11 @@ static const struct uart_ops lpuart32_pops = {
.config_port = lpuart_config_port,
.verify_port = lpuart_verify_port,
.flush_buffer = lpuart_flush_buffer,
+#if defined(CONFIG_CONSOLE_POLL)
+ .poll_init = lpuart32_poll_init,
+ .poll_get_char = lpuart32_poll_get_char,
+ .poll_put_char = lpuart32_poll_put_char,
+#endif
};
static struct lpuart_port *lpuart_ports[UART_NR];
@@ -1694,10 +1841,10 @@ static void lpuart_console_putchar(struct uart_port *port, int ch)
static void lpuart32_console_putchar(struct uart_port *port, int ch)
{
- while (!(lpuart32_read(port->membase + UARTSTAT) & UARTSTAT_TDRE))
+ while (!(lpuart32_read(port, UARTSTAT) & UARTSTAT_TDRE))
barrier();
- lpuart32_write(ch, port->membase + UARTDATA);
+ lpuart32_write(port, ch, UARTDATA);
}
static void
@@ -1745,18 +1892,18 @@ lpuart32_console_write(struct console *co, const char *s, unsigned int count)
spin_lock_irqsave(&sport->port.lock, flags);
/* first save CR2 and then disable interrupts */
- cr = old_cr = lpuart32_read(sport->port.membase + UARTCTRL);
+ cr = old_cr = lpuart32_read(&sport->port, UARTCTRL);
cr |= (UARTCTRL_TE | UARTCTRL_RE);
cr &= ~(UARTCTRL_TIE | UARTCTRL_TCIE | UARTCTRL_RIE);
- lpuart32_write(cr, sport->port.membase + UARTCTRL);
+ lpuart32_write(&sport->port, cr, UARTCTRL);
uart_console_write(&sport->port, s, count, lpuart32_console_putchar);
/* wait for transmitter finish complete and restore CR2 */
- while (!(lpuart32_read(sport->port.membase + UARTSTAT) & UARTSTAT_TC))
+ while (!(lpuart32_read(&sport->port, UARTSTAT) & UARTSTAT_TC))
barrier();
- lpuart32_write(old_cr, sport->port.membase + UARTCTRL);
+ lpuart32_write(&sport->port, old_cr, UARTCTRL);
if (locked)
spin_unlock_irqrestore(&sport->port.lock, flags);
@@ -1822,14 +1969,14 @@ lpuart32_console_get_options(struct lpuart_port *sport, int *baud,
unsigned long cr, bd;
unsigned int sbr, uartclk, baud_raw;
- cr = lpuart32_read(sport->port.membase + UARTCTRL);
+ cr = lpuart32_read(&sport->port, UARTCTRL);
cr &= UARTCTRL_TE | UARTCTRL_RE;
if (!cr)
return;
/* ok, the port was enabled */
- cr = lpuart32_read(sport->port.membase + UARTCTRL);
+ cr = lpuart32_read(&sport->port, UARTCTRL);
*parity = 'n';
if (cr & UARTCTRL_PE) {
@@ -1844,7 +1991,7 @@ lpuart32_console_get_options(struct lpuart_port *sport, int *baud,
else
*bits = 8;
- bd = lpuart32_read(sport->port.membase + UARTBAUD);
+ bd = lpuart32_read(&sport->port, UARTBAUD);
bd &= UARTBAUD_SBR_MASK;
sbr = bd;
uartclk = clk_get_rate(sport->clk);
@@ -1881,12 +2028,12 @@ static int __init lpuart_console_setup(struct console *co, char *options)
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
else
- if (sport->lpuart32)
+ if (lpuart_is_32(sport))
lpuart32_console_get_options(sport, &baud, &parity, &bits);
else
lpuart_console_get_options(sport, &baud, &parity, &bits);
- if (sport->lpuart32)
+ if (lpuart_is_32(sport))
lpuart32_setup_watermark(sport);
else
lpuart_setup_watermark(sport);
@@ -1945,12 +2092,26 @@ static int __init lpuart32_early_console_setup(struct earlycon_device *device,
if (!device->port.membase)
return -ENODEV;
+ device->port.iotype = UPIO_MEM32BE;
device->con->write = lpuart32_early_write;
return 0;
}
+static int __init lpuart32_imx_early_console_setup(struct earlycon_device *device,
+ const char *opt)
+{
+ if (!device->port.membase)
+ return -ENODEV;
+
+ device->port.iotype = UPIO_MEM32;
+ device->port.membase += IMX_REG_OFF;
+ device->con->write = lpuart32_early_write;
+
+ return 0;
+}
OF_EARLYCON_DECLARE(lpuart, "fsl,vf610-lpuart", lpuart_early_console_setup);
OF_EARLYCON_DECLARE(lpuart32, "fsl,ls1021a-lpuart", lpuart32_early_console_setup);
+OF_EARLYCON_DECLARE(lpuart32, "fsl,imx7ulp-lpuart", lpuart32_imx_early_console_setup);
EARLYCON_DECLARE(lpuart, lpuart_early_console_setup);
EARLYCON_DECLARE(lpuart32, lpuart32_early_console_setup);
@@ -1971,6 +2132,9 @@ static struct uart_driver lpuart_reg = {
static int lpuart_probe(struct platform_device *pdev)
{
+ const struct of_device_id *of_id = of_match_device(lpuart_dt_ids,
+ &pdev->dev);
+ const struct lpuart_soc_data *sdata = of_id->data;
struct device_node *np = pdev->dev.of_node;
struct lpuart_port *sport;
struct resource *res;
@@ -1988,25 +2152,23 @@ static int lpuart_probe(struct platform_device *pdev)
return ret;
}
sport->port.line = ret;
- sport->lpuart32 = of_device_is_compatible(np, "fsl,ls1021a-lpuart");
-
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
sport->port.membase = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(sport->port.membase))
return PTR_ERR(sport->port.membase);
+ sport->port.membase += sdata->reg_off;
sport->port.mapbase = res->start;
sport->port.dev = &pdev->dev;
sport->port.type = PORT_LPUART;
- sport->port.iotype = UPIO_MEM;
ret = platform_get_irq(pdev, 0);
if (ret < 0) {
dev_err(&pdev->dev, "cannot obtain irq\n");
return ret;
}
sport->port.irq = ret;
-
- if (sport->lpuart32)
+ sport->port.iotype = sdata->iotype;
+ if (lpuart_is_32(sport))
sport->port.ops = &lpuart32_pops;
else
sport->port.ops = &lpuart_pops;
@@ -2033,16 +2195,22 @@ static int lpuart_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, &sport->port);
- if (sport->lpuart32)
+ if (lpuart_is_32(sport)) {
lpuart_reg.cons = LPUART32_CONSOLE;
- else
+ ret = devm_request_irq(&pdev->dev, sport->port.irq, lpuart32_int, 0,
+ DRIVER_NAME, sport);
+ } else {
lpuart_reg.cons = LPUART_CONSOLE;
+ ret = devm_request_irq(&pdev->dev, sport->port.irq, lpuart_int, 0,
+ DRIVER_NAME, sport);
+ }
+
+ if (ret)
+ goto failed_irq_request;
ret = uart_add_one_port(&lpuart_reg, &sport->port);
- if (ret) {
- clk_disable_unprepare(sport->clk);
- return ret;
- }
+ if (ret)
+ goto failed_attach_port;
sport->dma_tx_chan = dma_request_slave_channel(sport->port.dev, "tx");
if (!sport->dma_tx_chan)
@@ -2061,6 +2229,11 @@ static int lpuart_probe(struct platform_device *pdev)
}
return 0;
+
+failed_attach_port:
+failed_irq_request:
+ clk_disable_unprepare(sport->clk);
+ return ret;
}
static int lpuart_remove(struct platform_device *pdev)
@@ -2085,12 +2258,13 @@ static int lpuart_suspend(struct device *dev)
{
struct lpuart_port *sport = dev_get_drvdata(dev);
unsigned long temp;
+ bool irq_wake;
- if (sport->lpuart32) {
+ if (lpuart_is_32(sport)) {
/* disable Rx/Tx and interrupts */
- temp = lpuart32_read(sport->port.membase + UARTCTRL);
+ temp = lpuart32_read(&sport->port, UARTCTRL);
temp &= ~(UARTCTRL_TE | UARTCTRL_TIE | UARTCTRL_TCIE);
- lpuart32_write(temp, sport->port.membase + UARTCTRL);
+ lpuart32_write(&sport->port, temp, UARTCTRL);
} else {
/* disable Rx/Tx and interrupts */
temp = readb(sport->port.membase + UARTCR2);
@@ -2100,6 +2274,9 @@ static int lpuart_suspend(struct device *dev)
uart_suspend_port(&lpuart_reg, &sport->port);
+ /* uart_suspend_port() might set wakeup flag */
+ irq_wake = irqd_is_wakeup_set(irq_get_irq_data(sport->port.irq));
+
if (sport->lpuart_dma_rx_use) {
/*
* EDMA driver during suspend will forcefully release any
@@ -2108,7 +2285,7 @@ static int lpuart_suspend(struct device *dev)
* cannot resume as as expected, hence gracefully release the
* Rx DMA path before suspend and start Rx DMA path on resume.
*/
- if (sport->port.irq_wake) {
+ if (irq_wake) {
del_timer_sync(&sport->lpuart_timer);
lpuart_dma_rx_free(&sport->port);
}
@@ -2123,7 +2300,7 @@ static int lpuart_suspend(struct device *dev)
dmaengine_terminate_all(sport->dma_tx_chan);
}
- if (sport->port.suspended && !sport->port.irq_wake)
+ if (sport->port.suspended && !irq_wake)
clk_disable_unprepare(sport->clk);
return 0;
@@ -2132,17 +2309,18 @@ static int lpuart_suspend(struct device *dev)
static int lpuart_resume(struct device *dev)
{
struct lpuart_port *sport = dev_get_drvdata(dev);
+ bool irq_wake = irqd_is_wakeup_set(irq_get_irq_data(sport->port.irq));
unsigned long temp;
- if (sport->port.suspended && !sport->port.irq_wake)
+ if (sport->port.suspended && !irq_wake)
clk_prepare_enable(sport->clk);
- if (sport->lpuart32) {
+ if (lpuart_is_32(sport)) {
lpuart32_setup_watermark(sport);
- temp = lpuart32_read(sport->port.membase + UARTCTRL);
+ temp = lpuart32_read(&sport->port, UARTCTRL);
temp |= (UARTCTRL_RIE | UARTCTRL_TIE | UARTCTRL_RE |
UARTCTRL_TE | UARTCTRL_ILIE);
- lpuart32_write(temp, sport->port.membase + UARTCTRL);
+ lpuart32_write(&sport->port, temp, UARTCTRL);
} else {
lpuart_setup_watermark(sport);
temp = readb(sport->port.membase + UARTCR2);
@@ -2151,7 +2329,7 @@ static int lpuart_resume(struct device *dev)
}
if (sport->lpuart_dma_rx_use) {
- if (sport->port.irq_wake) {
+ if (irq_wake) {
if (!lpuart_start_rx_dma(sport))
rx_dma_timer_init(sport);
else
diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
index bbefddd92bfe..dfeff3951f93 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -207,9 +207,6 @@ struct imx_port {
unsigned int have_rtscts:1;
unsigned int have_rtsgpio:1;
unsigned int dte_mode:1;
- unsigned int irda_inv_rx:1;
- unsigned int irda_inv_tx:1;
- unsigned short trcv_delay; /* transceiver delay */
struct clk *clk_ipg;
struct clk *clk_per;
const struct imx_uart_data *devdata;
@@ -229,7 +226,6 @@ struct imx_port {
dma_cookie_t rx_cookie;
unsigned int tx_bytes;
unsigned int dma_tx_nents;
- wait_queue_head_t dma_wait;
unsigned int saved_reg[10];
bool context_saved;
};
@@ -461,6 +457,9 @@ static inline void imx_transmit_buffer(struct imx_port *sport)
}
}
+ if (sport->dma_is_txing)
+ return;
+
while (!uart_circ_empty(xmit) &&
!(readl(sport->port.membase + uts_reg(sport)) & UTS_TXFULL)) {
/* send xmit->buf[xmit->tail]
@@ -501,20 +500,12 @@ static void dma_tx_callback(void *data)
sport->dma_is_txing = 0;
- spin_unlock_irqrestore(&sport->port.lock, flags);
-
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(&sport->port);
- if (waitqueue_active(&sport->dma_wait)) {
- wake_up(&sport->dma_wait);
- dev_dbg(sport->port.dev, "exit in %s.\n", __func__);
- return;
- }
-
- spin_lock_irqsave(&sport->port.lock, flags);
if (!uart_circ_empty(xmit) && !uart_tx_stopped(&sport->port))
imx_dma_tx(sport);
+
spin_unlock_irqrestore(&sport->port.lock, flags);
}
@@ -1211,8 +1202,6 @@ static void imx_enable_dma(struct imx_port *sport)
{
unsigned long temp;
- init_waitqueue_head(&sport->dma_wait);
-
/* set UCR1 */
temp = readl(sport->port.membase + UCR1);
temp |= UCR1_RDMAEN | UCR1_TDMAEN | UCR1_ATDMAEN;
@@ -1302,7 +1291,9 @@ static int imx_startup(struct uart_port *port)
imx_enable_dma(sport);
temp = readl(sport->port.membase + UCR1);
- temp |= UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN;
+ temp |= UCR1_RRDYEN | UCR1_UARTEN;
+ if (sport->have_rtscts)
+ temp |= UCR1_RTSDEN;
writel(temp, sport->port.membase + UCR1);
@@ -1340,29 +1331,13 @@ static int imx_startup(struct uart_port *port)
imx_enable_ms(&sport->port);
/*
- * If the serial port is opened for reading start RX DMA immediately
- * instead of waiting for RX FIFO interrupts. In our iMX53 the average
- * delay for the first reception dropped from approximately 35000
- * microseconds to 1000 microseconds.
+ * Start RX DMA immediately instead of waiting for RX FIFO interrupts.
+ * In our iMX53 the average delay for the first reception dropped from
+ * approximately 35000 microseconds to 1000 microseconds.
*/
if (sport->dma_is_enabled) {
- struct tty_struct *tty = sport->port.state->port.tty;
- struct tty_file_private *file_priv;
- int readcnt = 0;
-
- spin_lock(&tty->files_lock);
-
- if (!list_empty(&tty->tty_files))
- list_for_each_entry(file_priv, &tty->tty_files, list)
- if (!(file_priv->file->f_flags & O_WRONLY))
- readcnt++;
-
- spin_unlock(&tty->files_lock);
-
- if (readcnt > 0) {
- imx_disable_rx_int(sport);
- start_rx_dma(sport);
- }
+ imx_disable_rx_int(sport);
+ start_rx_dma(sport);
}
spin_unlock_irqrestore(&sport->port.lock, flags);
@@ -2349,6 +2324,7 @@ static int imx_serial_port_suspend(struct device *dev)
serial_imx_enable_wakeup(sport, true);
uart_suspend_port(&imx_reg, &sport->port);
+ disable_irq(sport->port.irq);
/* Needed to enable clock in suspend_noirq */
return clk_prepare(sport->clk_ipg);
@@ -2363,6 +2339,7 @@ static int imx_serial_port_resume(struct device *dev)
serial_imx_enable_wakeup(sport, false);
uart_resume_port(&imx_reg, &sport->port);
+ enable_irq(sport->port.irq);
clk_unprepare(sport->clk_ipg);
diff --git a/drivers/tty/serial/ioc3_serial.c b/drivers/tty/serial/ioc3_serial.c
index 2a61dd6b4009..906ee770ff4a 100644
--- a/drivers/tty/serial/ioc3_serial.c
+++ b/drivers/tty/serial/ioc3_serial.c
@@ -377,7 +377,7 @@ static struct ioc3_port *get_ioc3_port(struct uart_port *the_port)
* called per port from attach...
* @port: port to initialize
*/
-static int inline port_init(struct ioc3_port *port)
+static inline int port_init(struct ioc3_port *port)
{
uint32_t sio_cr;
struct port_hooks *hooks = port->ip_hooks;
@@ -1430,7 +1430,7 @@ static int receive_chars(struct uart_port *the_port)
* @pending: interrupts to handle
*/
-static int inline
+static inline int
ioc3uart_intr_one(struct ioc3_submodule *is,
struct ioc3_driver_data *idd,
unsigned int pending)
diff --git a/drivers/tty/serial/ioc4_serial.c b/drivers/tty/serial/ioc4_serial.c
index f96bcf9bee25..43d7d32eb150 100644
--- a/drivers/tty/serial/ioc4_serial.c
+++ b/drivers/tty/serial/ioc4_serial.c
@@ -824,7 +824,7 @@ pending_intrs(struct ioc4_soft *soft, int type)
* called per port from attach...
* @port: port to initialize
*/
-static int inline port_init(struct ioc4_port *port)
+static inline int port_init(struct ioc4_port *port)
{
uint32_t sio_cr;
struct hooks *hooks = port->ip_hooks;
@@ -1048,7 +1048,7 @@ static irqreturn_t ioc4_intr(int irq, void *arg)
* IOC4 with serial ports in the system.
* @idd: Master module data for this IOC4
*/
-static int inline ioc4_attach_local(struct ioc4_driver_data *idd)
+static inline int ioc4_attach_local(struct ioc4_driver_data *idd)
{
struct ioc4_port *port;
struct ioc4_port *ports[IOC4_NUM_SERIAL_PORTS];
diff --git a/drivers/tty/serial/ip22zilog.h b/drivers/tty/serial/ip22zilog.h
index a59a9a8341d2..b52801fe2d0d 100644
--- a/drivers/tty/serial/ip22zilog.h
+++ b/drivers/tty/serial/ip22zilog.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _IP22_ZILOG_H
#define _IP22_ZILOG_H
diff --git a/drivers/tty/serial/jsm/jsm_driver.c b/drivers/tty/serial/jsm/jsm_driver.c
index a119f11bf2f4..102d499814ac 100644
--- a/drivers/tty/serial/jsm/jsm_driver.c
+++ b/drivers/tty/serial/jsm/jsm_driver.c
@@ -304,7 +304,7 @@ static void jsm_remove_one(struct pci_dev *pdev)
kfree(brd);
}
-static struct pci_device_id jsm_pci_tbl[] = {
+static const struct pci_device_id jsm_pci_tbl[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_2DB9), 0, 0, 0 },
{ PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_2DB9PRI), 0, 0, 1 },
{ PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_2RJ45), 0, 0, 2 },
diff --git a/drivers/tty/serial/m32r_sio.c b/drivers/tty/serial/m32r_sio.c
index 218b7118e85d..5b3bd9511993 100644
--- a/drivers/tty/serial/m32r_sio.c
+++ b/drivers/tty/serial/m32r_sio.c
@@ -854,7 +854,7 @@ m32r_sio_verify_port(struct uart_port *port, struct serial_struct *ser)
return 0;
}
-static struct uart_ops m32r_sio_pops = {
+static const struct uart_ops m32r_sio_pops = {
.tx_empty = m32r_sio_tx_empty,
.set_mctrl = m32r_sio_set_mctrl,
.get_mctrl = m32r_sio_get_mctrl,
diff --git a/drivers/tty/serial/meson_uart.c b/drivers/tty/serial/meson_uart.c
index 60f16795d16b..07c0f98be3ac 100644
--- a/drivers/tty/serial/meson_uart.c
+++ b/drivers/tty/serial/meson_uart.c
@@ -286,7 +286,7 @@ static int meson_uart_startup(struct uart_port *port)
writel(val, port->membase + AML_UART_MISC);
ret = request_irq(port->irq, meson_uart_interrupt, 0,
- meson_uart_type(port), port);
+ port->name, port);
return ret;
}
@@ -298,8 +298,6 @@ static void meson_uart_change_speed(struct uart_port *port, unsigned long baud)
while (!meson_uart_tx_empty(port))
cpu_relax();
- val = readl(port->membase + AML_UART_REG5);
- val &= ~AML_UART_BAUD_MASK;
if (port->uartclk == 24000000) {
val = ((port->uartclk / 3) / baud) - 1;
val |= AML_UART_BAUD_XTAL;
@@ -355,7 +353,7 @@ static void meson_uart_set_termios(struct uart_port *port,
if (cflags & CSTOPB)
val |= AML_UART_STOP_BIN_2SB;
else
- val &= ~AML_UART_STOP_BIN_1SB;
+ val |= AML_UART_STOP_BIN_1SB;
if (cflags & CRTSCTS)
val &= ~AML_UART_TWO_WIRE_EN;
@@ -395,51 +393,25 @@ static int meson_uart_verify_port(struct uart_port *port,
return ret;
}
-static int meson_uart_res_size(struct uart_port *port)
-{
- struct platform_device *pdev = to_platform_device(port->dev);
- struct resource *res;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(port->dev, "cannot obtain I/O memory region");
- return -ENODEV;
- }
-
- return resource_size(res);
-}
-
static void meson_uart_release_port(struct uart_port *port)
{
- int size = meson_uart_res_size(port);
-
- if (port->flags & UPF_IOREMAP) {
- devm_release_mem_region(port->dev, port->mapbase, size);
- devm_iounmap(port->dev, port->membase);
- port->membase = NULL;
- }
+ devm_iounmap(port->dev, port->membase);
+ port->membase = NULL;
+ devm_release_mem_region(port->dev, port->mapbase, port->mapsize);
}
static int meson_uart_request_port(struct uart_port *port)
{
- int size = meson_uart_res_size(port);
-
- if (size < 0)
- return size;
-
- if (!devm_request_mem_region(port->dev, port->mapbase, size,
+ if (!devm_request_mem_region(port->dev, port->mapbase, port->mapsize,
dev_name(port->dev))) {
dev_err(port->dev, "Memory region busy\n");
return -EBUSY;
}
- if (port->flags & UPF_IOREMAP) {
- port->membase = devm_ioremap_nocache(port->dev,
- port->mapbase,
- size);
- if (port->membase == NULL)
- return -ENOMEM;
- }
+ port->membase = devm_ioremap_nocache(port->dev, port->mapbase,
+ port->mapsize);
+ if (!port->membase)
+ return -ENOMEM;
return 0;
}
@@ -452,7 +424,7 @@ static void meson_uart_config_port(struct uart_port *port, int flags)
}
}
-static struct uart_ops meson_uart_ops = {
+static const struct uart_ops meson_uart_ops = {
.set_mctrl = meson_uart_set_mctrl,
.get_mctrl = meson_uart_get_mctrl,
.tx_empty = meson_uart_tx_empty,
@@ -470,6 +442,14 @@ static struct uart_ops meson_uart_ops = {
};
#ifdef CONFIG_SERIAL_MESON_CONSOLE
+static void meson_uart_enable_tx_engine(struct uart_port *port)
+{
+ u32 val;
+
+ val = readl(port->membase + AML_UART_CONTROL);
+ val |= AML_UART_TX_EN;
+ writel(val, port->membase + AML_UART_CONTROL);
+}
static void meson_console_putchar(struct uart_port *port, int ch)
{
@@ -499,7 +479,6 @@ static void meson_serial_port_write(struct uart_port *port, const char *s,
}
val = readl(port->membase + AML_UART_CONTROL);
- val |= AML_UART_TX_EN;
tmp = val & ~(AML_UART_TX_INT_EN | AML_UART_RX_INT_EN);
writel(tmp, port->membase + AML_UART_CONTROL);
@@ -538,6 +517,8 @@ static int meson_serial_console_setup(struct console *co, char *options)
if (!port || !port->membase)
return -ENODEV;
+ meson_uart_enable_tx_engine(port);
+
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
@@ -576,11 +557,16 @@ meson_serial_early_console_setup(struct earlycon_device *device, const char *opt
if (!device->port.membase)
return -ENODEV;
+ meson_uart_enable_tx_engine(&device->port);
device->con->write = meson_serial_early_console_write;
return 0;
}
+/* Legacy bindings, should be removed when no more used */
OF_EARLYCON_DECLARE(meson, "amlogic,meson-uart",
meson_serial_early_console_setup);
+/* Stable bindings */
+OF_EARLYCON_DECLARE(meson, "amlogic,meson-ao-uart",
+ meson_serial_early_console_setup);
#define MESON_SERIAL_CONSOLE (&meson_serial_console)
#else
@@ -595,11 +581,76 @@ static struct uart_driver meson_uart_driver = {
.cons = MESON_SERIAL_CONSOLE,
};
+static inline struct clk *meson_uart_probe_clock(struct device *dev,
+ const char *id)
+{
+ struct clk *clk = NULL;
+ int ret;
+
+ clk = devm_clk_get(dev, id);
+ if (IS_ERR(clk))
+ return clk;
+
+ ret = clk_prepare_enable(clk);
+ if (ret) {
+ dev_err(dev, "couldn't enable clk\n");
+ return ERR_PTR(ret);
+ }
+
+ devm_add_action_or_reset(dev,
+ (void(*)(void *))clk_disable_unprepare,
+ clk);
+
+ return clk;
+}
+
+/*
+ * This function gets clocks in the legacy non-stable DT bindings.
+ * This code will be remove once all the platforms switch to the
+ * new DT bindings.
+ */
+static int meson_uart_probe_clocks_legacy(struct platform_device *pdev,
+ struct uart_port *port)
+{
+ struct clk *clk = NULL;
+
+ clk = meson_uart_probe_clock(&pdev->dev, NULL);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ port->uartclk = clk_get_rate(clk);
+
+ return 0;
+}
+
+static int meson_uart_probe_clocks(struct platform_device *pdev,
+ struct uart_port *port)
+{
+ struct clk *clk_xtal = NULL;
+ struct clk *clk_pclk = NULL;
+ struct clk *clk_baud = NULL;
+
+ clk_pclk = meson_uart_probe_clock(&pdev->dev, "pclk");
+ if (IS_ERR(clk_pclk))
+ return PTR_ERR(clk_pclk);
+
+ clk_xtal = meson_uart_probe_clock(&pdev->dev, "xtal");
+ if (IS_ERR(clk_xtal))
+ return PTR_ERR(clk_xtal);
+
+ clk_baud = meson_uart_probe_clock(&pdev->dev, "baud");
+ if (IS_ERR(clk_baud))
+ return PTR_ERR(clk_baud);
+
+ port->uartclk = clk_get_rate(clk_baud);
+
+ return 0;
+}
+
static int meson_uart_probe(struct platform_device *pdev)
{
struct resource *res_mem, *res_irq;
struct uart_port *port;
- struct clk *clk;
int ret = 0;
if (pdev->dev.of_node)
@@ -625,15 +676,20 @@ static int meson_uart_probe(struct platform_device *pdev)
if (!port)
return -ENOMEM;
- clk = clk_get(&pdev->dev, NULL);
- if (IS_ERR(clk))
- return PTR_ERR(clk);
+ /* Use legacy way until all platforms switch to new bindings */
+ if (of_device_is_compatible(pdev->dev.of_node, "amlogic,meson-uart"))
+ ret = meson_uart_probe_clocks_legacy(pdev, port);
+ else
+ ret = meson_uart_probe_clocks(pdev, port);
+
+ if (ret)
+ return ret;
- port->uartclk = clk_get_rate(clk);
port->iotype = UPIO_MEM;
port->mapbase = res_mem->start;
+ port->mapsize = resource_size(res_mem);
port->irq = res_irq->start;
- port->flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP | UPF_LOW_LATENCY;
+ port->flags = UPF_BOOT_AUTOCONF | UPF_LOW_LATENCY;
port->dev = &pdev->dev;
port->line = pdev->id;
port->type = PORT_MESON;
@@ -668,9 +724,14 @@ static int meson_uart_remove(struct platform_device *pdev)
return 0;
}
-
static const struct of_device_id meson_uart_dt_match[] = {
+ /* Legacy bindings, should be removed when no more used */
{ .compatible = "amlogic,meson-uart" },
+ /* Stable bindings */
+ { .compatible = "amlogic,meson6-uart" },
+ { .compatible = "amlogic,meson8-uart" },
+ { .compatible = "amlogic,meson8b-uart" },
+ { .compatible = "amlogic,meson-gx-uart" },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, meson_uart_dt_match);
diff --git a/drivers/tty/serial/mpc52xx_uart.c b/drivers/tty/serial/mpc52xx_uart.c
index 3970d6a9aaca..791c4c74f6d6 100644
--- a/drivers/tty/serial/mpc52xx_uart.c
+++ b/drivers/tty/serial/mpc52xx_uart.c
@@ -1347,7 +1347,7 @@ mpc52xx_uart_verify_port(struct uart_port *port, struct serial_struct *ser)
}
-static struct uart_ops mpc52xx_uart_ops = {
+static const struct uart_ops mpc52xx_uart_ops = {
.tx_empty = mpc52xx_uart_tx_empty,
.set_mctrl = mpc52xx_uart_set_mctrl,
.get_mctrl = mpc52xx_uart_get_mctrl,
@@ -1634,8 +1634,8 @@ mpc52xx_console_setup(struct console *co, char *options)
return -EINVAL;
}
- pr_debug("Console on ttyPSC%x is %s\n",
- co->index, mpc52xx_uart_nodes[co->index]->full_name);
+ pr_debug("Console on ttyPSC%x is %pOF\n",
+ co->index, mpc52xx_uart_nodes[co->index]);
/* Fetch register locations */
ret = of_address_to_resource(np, 0, &res);
@@ -1755,8 +1755,8 @@ static int mpc52xx_uart_of_probe(struct platform_device *op)
break;
if (idx >= MPC52xx_PSC_MAXNUM)
return -EINVAL;
- pr_debug("Found %s assigned to ttyPSC%x\n",
- mpc52xx_uart_nodes[idx]->full_name, idx);
+ pr_debug("Found %pOF assigned to ttyPSC%x\n",
+ mpc52xx_uart_nodes[idx], idx);
/* set the uart clock to the input clock of the psc, the different
* prescalers are taken into account in the set_baudrate() methods
@@ -1881,8 +1881,8 @@ mpc52xx_uart_of_enumerate(void)
for (i = 0; i < MPC52xx_PSC_MAXNUM; i++) {
if (mpc52xx_uart_nodes[i])
- pr_debug("%s assigned to ttyPSC%x\n",
- mpc52xx_uart_nodes[i]->full_name, i);
+ pr_debug("%pOF assigned to ttyPSC%x\n",
+ mpc52xx_uart_nodes[i], i);
}
}
diff --git a/drivers/tty/serial/mpsc.c b/drivers/tty/serial/mpsc.c
index 1a60a2063e75..67ffecc50e42 100644
--- a/drivers/tty/serial/mpsc.c
+++ b/drivers/tty/serial/mpsc.c
@@ -754,9 +754,10 @@ static int mpsc_alloc_ring_mem(struct mpsc_port_info *pi)
if (!dma_set_mask(pi->port.dev, 0xffffffff)) {
printk(KERN_ERR "MPSC: Inadequate DMA support\n");
rc = -ENXIO;
- } else if ((pi->dma_region = dma_alloc_noncoherent(pi->port.dev,
+ } else if ((pi->dma_region = dma_alloc_attrs(pi->port.dev,
MPSC_DMA_ALLOC_SIZE,
- &pi->dma_region_p, GFP_KERNEL))
+ &pi->dma_region_p, GFP_KERNEL,
+ DMA_ATTR_NON_CONSISTENT))
== NULL) {
printk(KERN_ERR "MPSC: Can't alloc Desc region\n");
rc = -ENOMEM;
@@ -771,8 +772,9 @@ static void mpsc_free_ring_mem(struct mpsc_port_info *pi)
pr_debug("mpsc_free_ring_mem[%d]: Freeing ring mem\n", pi->port.line);
if (pi->dma_region) {
- dma_free_noncoherent(pi->port.dev, MPSC_DMA_ALLOC_SIZE,
- pi->dma_region, pi->dma_region_p);
+ dma_free_attrs(pi->port.dev, MPSC_DMA_ALLOC_SIZE,
+ pi->dma_region, pi->dma_region_p,
+ DMA_ATTR_NON_CONSISTENT);
pi->dma_region = NULL;
pi->dma_region_p = (dma_addr_t)NULL;
}
diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c
index 6788e7532dff..1db79ee8a886 100644
--- a/drivers/tty/serial/msm_serial.c
+++ b/drivers/tty/serial/msm_serial.c
@@ -1175,11 +1175,6 @@ static int msm_startup(struct uart_port *port)
snprintf(msm_port->name, sizeof(msm_port->name),
"msm_serial%d", port->line);
- ret = request_irq(port->irq, msm_uart_irq, IRQF_TRIGGER_HIGH,
- msm_port->name, port);
- if (unlikely(ret))
- return ret;
-
msm_init_clock(port);
if (likely(port->fifosize > 12))
@@ -1206,7 +1201,21 @@ static int msm_startup(struct uart_port *port)
msm_request_rx_dma(msm_port, msm_port->uart.mapbase);
}
+ ret = request_irq(port->irq, msm_uart_irq, IRQF_TRIGGER_HIGH,
+ msm_port->name, port);
+ if (unlikely(ret))
+ goto err_irq;
+
return 0;
+
+err_irq:
+ if (msm_port->is_uartdm)
+ msm_release_dma(msm_port);
+
+ clk_disable_unprepare(msm_port->pclk);
+ clk_disable_unprepare(msm_port->clk);
+
+ return ret;
}
static void msm_shutdown(struct uart_port *port)
diff --git a/drivers/tty/serial/mux.c b/drivers/tty/serial/mux.c
index 8a4be4b73723..2bff69e70e4b 100644
--- a/drivers/tty/serial/mux.c
+++ b/drivers/tty/serial/mux.c
@@ -427,7 +427,7 @@ static struct console mux_console = {
#define MUX_CONSOLE NULL
#endif
-static struct uart_ops mux_pops = {
+static const struct uart_ops mux_pops = {
.tx_empty = mux_tx_empty,
.set_mctrl = mux_set_mctrl,
.get_mctrl = mux_get_mctrl,
@@ -503,7 +503,7 @@ static int __init mux_probe(struct parisc_device *dev)
return 0;
}
-static int mux_remove(struct parisc_device *dev)
+static int __exit mux_remove(struct parisc_device *dev)
{
int i, j;
int port_count = (long)dev_get_drvdata(&dev->dev);
@@ -536,13 +536,13 @@ static int mux_remove(struct parisc_device *dev)
* This table only contains the parisc_device_id of known builtin mux
* devices. All other mux cards will be detected by the generic mux_tbl.
*/
-static struct parisc_device_id builtin_mux_tbl[] = {
+static const struct parisc_device_id builtin_mux_tbl[] __initconst = {
{ HPHW_A_DIRECT, HVERSION_REV_ANY_ID, 0x15, 0x0000D }, /* All K-class */
{ HPHW_A_DIRECT, HVERSION_REV_ANY_ID, 0x44, 0x0000D }, /* E35, E45, and E55 */
{ 0, }
};
-static struct parisc_device_id mux_tbl[] = {
+static const struct parisc_device_id mux_tbl[] __initconst = {
{ HPHW_A_DIRECT, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0000D },
{ 0, }
};
@@ -550,18 +550,18 @@ static struct parisc_device_id mux_tbl[] = {
MODULE_DEVICE_TABLE(parisc, builtin_mux_tbl);
MODULE_DEVICE_TABLE(parisc, mux_tbl);
-static struct parisc_driver builtin_serial_mux_driver = {
+static struct parisc_driver builtin_serial_mux_driver __refdata = {
.name = "builtin_serial_mux",
.id_table = builtin_mux_tbl,
.probe = mux_probe,
- .remove = mux_remove,
+ .remove = __exit_p(mux_remove),
};
-static struct parisc_driver serial_mux_driver = {
+static struct parisc_driver serial_mux_driver __refdata = {
.name = "serial_mux",
.id_table = mux_tbl,
.probe = mux_probe,
- .remove = mux_remove,
+ .remove = __exit_p(mux_remove),
};
/**
diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c
index 1ea05ac57aa7..7754053deeda 100644
--- a/drivers/tty/serial/omap-serial.c
+++ b/drivers/tty/serial/omap-serial.c
@@ -1235,21 +1235,20 @@ out:
#ifdef CONFIG_SERIAL_OMAP_CONSOLE
#ifdef CONFIG_SERIAL_EARLYCON
-static unsigned int __init omap_serial_early_in(struct uart_port *port,
- int offset)
+static unsigned int omap_serial_early_in(struct uart_port *port, int offset)
{
offset <<= port->regshift;
return readw(port->membase + offset);
}
-static void __init omap_serial_early_out(struct uart_port *port, int offset,
- int value)
+static void omap_serial_early_out(struct uart_port *port, int offset,
+ int value)
{
offset <<= port->regshift;
writew(value, port->membase + offset);
}
-static void __init omap_serial_early_putc(struct uart_port *port, int c)
+static void omap_serial_early_putc(struct uart_port *port, int c)
{
unsigned int status;
@@ -1262,8 +1261,8 @@ static void __init omap_serial_early_putc(struct uart_port *port, int c)
omap_serial_early_out(port, UART_TX, c);
}
-static void __init early_omap_serial_write(struct console *console,
- const char *s, unsigned int count)
+static void early_omap_serial_write(struct console *console, const char *s,
+ unsigned int count)
{
struct earlycon_device *device = console->data;
struct uart_port *port = &device->port;
diff --git a/drivers/tty/serial/owl-uart.c b/drivers/tty/serial/owl-uart.c
new file mode 100644
index 000000000000..b9c859365334
--- /dev/null
+++ b/drivers/tty/serial/owl-uart.c
@@ -0,0 +1,766 @@
+/*
+ * Actions Semi Owl family serial console
+ *
+ * Copyright 2013 Actions Semi Inc.
+ * Author: Actions Semi, Inc.
+ *
+ * Copyright (c) 2016-2017 Andreas Färber
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+
+#define OWL_UART_PORT_NUM 7
+#define OWL_UART_DEV_NAME "ttyOWL"
+
+#define OWL_UART_CTL 0x000
+#define OWL_UART_RXDAT 0x004
+#define OWL_UART_TXDAT 0x008
+#define OWL_UART_STAT 0x00c
+
+#define OWL_UART_CTL_DWLS_MASK GENMASK(1, 0)
+#define OWL_UART_CTL_DWLS_5BITS (0x0 << 0)
+#define OWL_UART_CTL_DWLS_6BITS (0x1 << 0)
+#define OWL_UART_CTL_DWLS_7BITS (0x2 << 0)
+#define OWL_UART_CTL_DWLS_8BITS (0x3 << 0)
+#define OWL_UART_CTL_STPS_2BITS BIT(2)
+#define OWL_UART_CTL_PRS_MASK GENMASK(6, 4)
+#define OWL_UART_CTL_PRS_NONE (0x0 << 4)
+#define OWL_UART_CTL_PRS_ODD (0x4 << 4)
+#define OWL_UART_CTL_PRS_MARK (0x5 << 4)
+#define OWL_UART_CTL_PRS_EVEN (0x6 << 4)
+#define OWL_UART_CTL_PRS_SPACE (0x7 << 4)
+#define OWL_UART_CTL_AFE BIT(12)
+#define OWL_UART_CTL_TRFS_TX BIT(14)
+#define OWL_UART_CTL_EN BIT(15)
+#define OWL_UART_CTL_RXDE BIT(16)
+#define OWL_UART_CTL_TXDE BIT(17)
+#define OWL_UART_CTL_RXIE BIT(18)
+#define OWL_UART_CTL_TXIE BIT(19)
+#define OWL_UART_CTL_LBEN BIT(20)
+
+#define OWL_UART_STAT_RIP BIT(0)
+#define OWL_UART_STAT_TIP BIT(1)
+#define OWL_UART_STAT_RXER BIT(2)
+#define OWL_UART_STAT_TFER BIT(3)
+#define OWL_UART_STAT_RXST BIT(4)
+#define OWL_UART_STAT_RFEM BIT(5)
+#define OWL_UART_STAT_TFFU BIT(6)
+#define OWL_UART_STAT_CTSS BIT(7)
+#define OWL_UART_STAT_RTSS BIT(8)
+#define OWL_UART_STAT_TFES BIT(10)
+#define OWL_UART_STAT_TRFL_MASK GENMASK(16, 11)
+#define OWL_UART_STAT_UTBB BIT(17)
+
+static struct uart_driver owl_uart_driver;
+
+struct owl_uart_info {
+ unsigned int tx_fifosize;
+};
+
+struct owl_uart_port {
+ struct uart_port port;
+ struct clk *clk;
+};
+
+#define to_owl_uart_port(prt) container_of(prt, struct owl_uart_port, prt)
+
+static struct owl_uart_port *owl_uart_ports[OWL_UART_PORT_NUM];
+
+static inline void owl_uart_write(struct uart_port *port, u32 val, unsigned int off)
+{
+ writel(val, port->membase + off);
+}
+
+static inline u32 owl_uart_read(struct uart_port *port, unsigned int off)
+{
+ return readl(port->membase + off);
+}
+
+static void owl_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+ u32 ctl;
+
+ ctl = owl_uart_read(port, OWL_UART_CTL);
+
+ if (mctrl & TIOCM_LOOP)
+ ctl |= OWL_UART_CTL_LBEN;
+ else
+ ctl &= ~OWL_UART_CTL_LBEN;
+
+ owl_uart_write(port, ctl, OWL_UART_CTL);
+}
+
+static unsigned int owl_uart_get_mctrl(struct uart_port *port)
+{
+ unsigned int mctrl = TIOCM_CAR | TIOCM_DSR;
+ u32 stat, ctl;
+
+ ctl = owl_uart_read(port, OWL_UART_CTL);
+ stat = owl_uart_read(port, OWL_UART_STAT);
+ if (stat & OWL_UART_STAT_RTSS)
+ mctrl |= TIOCM_RTS;
+ if ((stat & OWL_UART_STAT_CTSS) || !(ctl & OWL_UART_CTL_AFE))
+ mctrl |= TIOCM_CTS;
+ return mctrl;
+}
+
+static unsigned int owl_uart_tx_empty(struct uart_port *port)
+{
+ unsigned long flags;
+ u32 val;
+ unsigned int ret;
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ val = owl_uart_read(port, OWL_UART_STAT);
+ ret = (val & OWL_UART_STAT_TFES) ? TIOCSER_TEMT : 0;
+
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ return ret;
+}
+
+static void owl_uart_stop_rx(struct uart_port *port)
+{
+ u32 val;
+
+ val = owl_uart_read(port, OWL_UART_CTL);
+ val &= ~(OWL_UART_CTL_RXIE | OWL_UART_CTL_RXDE);
+ owl_uart_write(port, val, OWL_UART_CTL);
+
+ val = owl_uart_read(port, OWL_UART_STAT);
+ val |= OWL_UART_STAT_RIP;
+ owl_uart_write(port, val, OWL_UART_STAT);
+}
+
+static void owl_uart_stop_tx(struct uart_port *port)
+{
+ u32 val;
+
+ val = owl_uart_read(port, OWL_UART_CTL);
+ val &= ~(OWL_UART_CTL_TXIE | OWL_UART_CTL_TXDE);
+ owl_uart_write(port, val, OWL_UART_CTL);
+
+ val = owl_uart_read(port, OWL_UART_STAT);
+ val |= OWL_UART_STAT_TIP;
+ owl_uart_write(port, val, OWL_UART_STAT);
+}
+
+static void owl_uart_start_tx(struct uart_port *port)
+{
+ u32 val;
+
+ if (uart_tx_stopped(port)) {
+ owl_uart_stop_tx(port);
+ return;
+ }
+
+ val = owl_uart_read(port, OWL_UART_STAT);
+ val |= OWL_UART_STAT_TIP;
+ owl_uart_write(port, val, OWL_UART_STAT);
+
+ val = owl_uart_read(port, OWL_UART_CTL);
+ val |= OWL_UART_CTL_TXIE;
+ owl_uart_write(port, val, OWL_UART_CTL);
+}
+
+static void owl_uart_send_chars(struct uart_port *port)
+{
+ struct circ_buf *xmit = &port->state->xmit;
+ unsigned int ch;
+
+ if (uart_tx_stopped(port))
+ return;
+
+ if (port->x_char) {
+ while (!(owl_uart_read(port, OWL_UART_STAT) & OWL_UART_STAT_TFFU))
+ cpu_relax();
+ owl_uart_write(port, port->x_char, OWL_UART_TXDAT);
+ port->icount.tx++;
+ port->x_char = 0;
+ }
+
+ while (!(owl_uart_read(port, OWL_UART_STAT) & OWL_UART_STAT_TFFU)) {
+ if (uart_circ_empty(xmit))
+ break;
+
+ ch = xmit->buf[xmit->tail];
+ owl_uart_write(port, ch, OWL_UART_TXDAT);
+ xmit->tail = (xmit->tail + 1) & (SERIAL_XMIT_SIZE - 1);
+ port->icount.tx++;
+ }
+
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(port);
+
+ if (uart_circ_empty(xmit))
+ owl_uart_stop_tx(port);
+}
+
+static void owl_uart_receive_chars(struct uart_port *port)
+{
+ u32 stat, val;
+
+ val = owl_uart_read(port, OWL_UART_CTL);
+ val &= ~OWL_UART_CTL_TRFS_TX;
+ owl_uart_write(port, val, OWL_UART_CTL);
+
+ stat = owl_uart_read(port, OWL_UART_STAT);
+ while (!(stat & OWL_UART_STAT_RFEM)) {
+ char flag = TTY_NORMAL;
+
+ if (stat & OWL_UART_STAT_RXER)
+ port->icount.overrun++;
+
+ if (stat & OWL_UART_STAT_RXST) {
+ /* We are not able to distinguish the error type. */
+ port->icount.brk++;
+ port->icount.frame++;
+
+ stat &= port->read_status_mask;
+ if (stat & OWL_UART_STAT_RXST)
+ flag = TTY_PARITY;
+ } else
+ port->icount.rx++;
+
+ val = owl_uart_read(port, OWL_UART_RXDAT);
+ val &= 0xff;
+
+ if ((stat & port->ignore_status_mask) == 0)
+ tty_insert_flip_char(&port->state->port, val, flag);
+
+ stat = owl_uart_read(port, OWL_UART_STAT);
+ }
+
+ spin_unlock(&port->lock);
+ tty_flip_buffer_push(&port->state->port);
+ spin_lock(&port->lock);
+}
+
+static irqreturn_t owl_uart_irq(int irq, void *dev_id)
+{
+ struct uart_port *port = dev_id;
+ unsigned long flags;
+ u32 stat;
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ stat = owl_uart_read(port, OWL_UART_STAT);
+
+ if (stat & OWL_UART_STAT_RIP)
+ owl_uart_receive_chars(port);
+
+ if (stat & OWL_UART_STAT_TIP)
+ owl_uart_send_chars(port);
+
+ stat = owl_uart_read(port, OWL_UART_STAT);
+ stat |= OWL_UART_STAT_RIP | OWL_UART_STAT_TIP;
+ owl_uart_write(port, stat, OWL_UART_STAT);
+
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+static void owl_uart_shutdown(struct uart_port *port)
+{
+ u32 val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ val = owl_uart_read(port, OWL_UART_CTL);
+ val &= ~(OWL_UART_CTL_TXIE | OWL_UART_CTL_RXIE
+ | OWL_UART_CTL_TXDE | OWL_UART_CTL_RXDE | OWL_UART_CTL_EN);
+ owl_uart_write(port, val, OWL_UART_CTL);
+
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ free_irq(port->irq, port);
+}
+
+static int owl_uart_startup(struct uart_port *port)
+{
+ u32 val;
+ unsigned long flags;
+ int ret;
+
+ ret = request_irq(port->irq, owl_uart_irq, IRQF_TRIGGER_HIGH,
+ "owl-uart", port);
+ if (ret)
+ return ret;
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ val = owl_uart_read(port, OWL_UART_STAT);
+ val |= OWL_UART_STAT_RIP | OWL_UART_STAT_TIP
+ | OWL_UART_STAT_RXER | OWL_UART_STAT_TFER | OWL_UART_STAT_RXST;
+ owl_uart_write(port, val, OWL_UART_STAT);
+
+ val = owl_uart_read(port, OWL_UART_CTL);
+ val |= OWL_UART_CTL_RXIE | OWL_UART_CTL_TXIE;
+ val |= OWL_UART_CTL_EN;
+ owl_uart_write(port, val, OWL_UART_CTL);
+
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ return 0;
+}
+
+static void owl_uart_change_baudrate(struct owl_uart_port *owl_port,
+ unsigned long baud)
+{
+ clk_set_rate(owl_port->clk, baud * 8);
+}
+
+static void owl_uart_set_termios(struct uart_port *port,
+ struct ktermios *termios,
+ struct ktermios *old)
+{
+ struct owl_uart_port *owl_port = to_owl_uart_port(port);
+ unsigned int baud;
+ u32 ctl;
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ ctl = owl_uart_read(port, OWL_UART_CTL);
+
+ ctl &= ~OWL_UART_CTL_DWLS_MASK;
+ switch (termios->c_cflag & CSIZE) {
+ case CS5:
+ ctl |= OWL_UART_CTL_DWLS_5BITS;
+ break;
+ case CS6:
+ ctl |= OWL_UART_CTL_DWLS_6BITS;
+ break;
+ case CS7:
+ ctl |= OWL_UART_CTL_DWLS_7BITS;
+ break;
+ case CS8:
+ default:
+ ctl |= OWL_UART_CTL_DWLS_8BITS;
+ break;
+ }
+
+ if (termios->c_cflag & CSTOPB)
+ ctl |= OWL_UART_CTL_STPS_2BITS;
+ else
+ ctl &= ~OWL_UART_CTL_STPS_2BITS;
+
+ ctl &= ~OWL_UART_CTL_PRS_MASK;
+ if (termios->c_cflag & PARENB) {
+ if (termios->c_cflag & CMSPAR) {
+ if (termios->c_cflag & PARODD)
+ ctl |= OWL_UART_CTL_PRS_MARK;
+ else
+ ctl |= OWL_UART_CTL_PRS_SPACE;
+ } else if (termios->c_cflag & PARODD)
+ ctl |= OWL_UART_CTL_PRS_ODD;
+ else
+ ctl |= OWL_UART_CTL_PRS_EVEN;
+ } else
+ ctl |= OWL_UART_CTL_PRS_NONE;
+
+ if (termios->c_cflag & CRTSCTS)
+ ctl |= OWL_UART_CTL_AFE;
+ else
+ ctl &= ~OWL_UART_CTL_AFE;
+
+ owl_uart_write(port, ctl, OWL_UART_CTL);
+
+ baud = uart_get_baud_rate(port, termios, old, 9600, 3200000);
+ owl_uart_change_baudrate(owl_port, baud);
+
+ /* Don't rewrite B0 */
+ if (tty_termios_baud_rate(termios))
+ tty_termios_encode_baud_rate(termios, baud, baud);
+
+ port->read_status_mask |= OWL_UART_STAT_RXER;
+ if (termios->c_iflag & INPCK)
+ port->read_status_mask |= OWL_UART_STAT_RXST;
+
+ uart_update_timeout(port, termios->c_cflag, baud);
+
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void owl_uart_release_port(struct uart_port *port)
+{
+ struct platform_device *pdev = to_platform_device(port->dev);
+ struct resource *res;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return;
+
+ if (port->flags & UPF_IOREMAP) {
+ devm_release_mem_region(port->dev, port->mapbase,
+ resource_size(res));
+ devm_iounmap(port->dev, port->membase);
+ port->membase = NULL;
+ }
+}
+
+static int owl_uart_request_port(struct uart_port *port)
+{
+ struct platform_device *pdev = to_platform_device(port->dev);
+ struct resource *res;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENXIO;
+
+ if (!devm_request_mem_region(port->dev, port->mapbase,
+ resource_size(res), dev_name(port->dev)))
+ return -EBUSY;
+
+ if (port->flags & UPF_IOREMAP) {
+ port->membase = devm_ioremap_nocache(port->dev, port->mapbase,
+ resource_size(res));
+ if (!port->membase)
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+static const char *owl_uart_type(struct uart_port *port)
+{
+ return (port->type == PORT_OWL) ? "owl-uart" : NULL;
+}
+
+static int owl_uart_verify_port(struct uart_port *port,
+ struct serial_struct *ser)
+{
+ if (port->type != PORT_OWL)
+ return -EINVAL;
+
+ if (port->irq != ser->irq)
+ return -EINVAL;
+
+ return 0;
+}
+
+static void owl_uart_config_port(struct uart_port *port, int flags)
+{
+ if (flags & UART_CONFIG_TYPE) {
+ port->type = PORT_OWL;
+ owl_uart_request_port(port);
+ }
+}
+
+static const struct uart_ops owl_uart_ops = {
+ .set_mctrl = owl_uart_set_mctrl,
+ .get_mctrl = owl_uart_get_mctrl,
+ .tx_empty = owl_uart_tx_empty,
+ .start_tx = owl_uart_start_tx,
+ .stop_rx = owl_uart_stop_rx,
+ .stop_tx = owl_uart_stop_tx,
+ .startup = owl_uart_startup,
+ .shutdown = owl_uart_shutdown,
+ .set_termios = owl_uart_set_termios,
+ .type = owl_uart_type,
+ .config_port = owl_uart_config_port,
+ .request_port = owl_uart_request_port,
+ .release_port = owl_uart_release_port,
+ .verify_port = owl_uart_verify_port,
+};
+
+#ifdef CONFIG_SERIAL_OWL_CONSOLE
+
+static void owl_console_putchar(struct uart_port *port, int ch)
+{
+ if (!port->membase)
+ return;
+
+ while (owl_uart_read(port, OWL_UART_STAT) & OWL_UART_STAT_TFFU)
+ cpu_relax();
+
+ owl_uart_write(port, ch, OWL_UART_TXDAT);
+}
+
+static void owl_uart_port_write(struct uart_port *port, const char *s,
+ u_int count)
+{
+ u32 old_ctl, val;
+ unsigned long flags;
+ int locked;
+
+ local_irq_save(flags);
+
+ if (port->sysrq)
+ locked = 0;
+ else if (oops_in_progress)
+ locked = spin_trylock(&port->lock);
+ else {
+ spin_lock(&port->lock);
+ locked = 1;
+ }
+
+ old_ctl = owl_uart_read(port, OWL_UART_CTL);
+ val = old_ctl | OWL_UART_CTL_TRFS_TX;
+ /* disable IRQ */
+ val &= ~(OWL_UART_CTL_RXIE | OWL_UART_CTL_TXIE);
+ owl_uart_write(port, val, OWL_UART_CTL);
+
+ uart_console_write(port, s, count, owl_console_putchar);
+
+ /* wait until all contents have been sent out */
+ while (owl_uart_read(port, OWL_UART_STAT) & OWL_UART_STAT_TRFL_MASK)
+ cpu_relax();
+
+ /* clear IRQ pending */
+ val = owl_uart_read(port, OWL_UART_STAT);
+ val |= OWL_UART_STAT_TIP | OWL_UART_STAT_RIP;
+ owl_uart_write(port, val, OWL_UART_STAT);
+
+ owl_uart_write(port, old_ctl, OWL_UART_CTL);
+
+ if (locked)
+ spin_unlock(&port->lock);
+
+ local_irq_restore(flags);
+}
+
+static void owl_uart_console_write(struct console *co, const char *s,
+ u_int count)
+{
+ struct owl_uart_port *owl_port;
+
+ owl_port = owl_uart_ports[co->index];
+ if (!owl_port)
+ return;
+
+ owl_uart_port_write(&owl_port->port, s, count);
+}
+
+static int owl_uart_console_setup(struct console *co, char *options)
+{
+ struct owl_uart_port *owl_port;
+ int baud = 115200;
+ int bits = 8;
+ int parity = 'n';
+ int flow = 'n';
+
+ if (co->index < 0 || co->index >= OWL_UART_PORT_NUM)
+ return -EINVAL;
+
+ owl_port = owl_uart_ports[co->index];
+ if (!owl_port || !owl_port->port.membase)
+ return -ENODEV;
+
+ if (options)
+ uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+ return uart_set_options(&owl_port->port, co, baud, parity, bits, flow);
+}
+
+static struct console owl_uart_console = {
+ .name = OWL_UART_DEV_NAME,
+ .write = owl_uart_console_write,
+ .device = uart_console_device,
+ .setup = owl_uart_console_setup,
+ .flags = CON_PRINTBUFFER,
+ .index = -1,
+ .data = &owl_uart_driver,
+};
+
+static int __init owl_uart_console_init(void)
+{
+ register_console(&owl_uart_console);
+
+ return 0;
+}
+console_initcall(owl_uart_console_init);
+
+static void owl_uart_early_console_write(struct console *co,
+ const char *s,
+ u_int count)
+{
+ struct earlycon_device *dev = co->data;
+
+ owl_uart_port_write(&dev->port, s, count);
+}
+
+static int __init
+owl_uart_early_console_setup(struct earlycon_device *device, const char *opt)
+{
+ if (!device->port.membase)
+ return -ENODEV;
+
+ device->con->write = owl_uart_early_console_write;
+
+ return 0;
+}
+OF_EARLYCON_DECLARE(owl, "actions,owl-uart",
+ owl_uart_early_console_setup);
+
+#define OWL_UART_CONSOLE (&owl_uart_console)
+#else
+#define OWL_UART_CONSOLE NULL
+#endif
+
+static struct uart_driver owl_uart_driver = {
+ .owner = THIS_MODULE,
+ .driver_name = "owl-uart",
+ .dev_name = OWL_UART_DEV_NAME,
+ .nr = OWL_UART_PORT_NUM,
+ .cons = OWL_UART_CONSOLE,
+};
+
+static const struct owl_uart_info owl_s500_info = {
+ .tx_fifosize = 16,
+};
+
+static const struct owl_uart_info owl_s900_info = {
+ .tx_fifosize = 32,
+};
+
+static const struct of_device_id owl_uart_dt_matches[] = {
+ { .compatible = "actions,s500-uart", .data = &owl_s500_info },
+ { .compatible = "actions,s900-uart", .data = &owl_s900_info },
+ { }
+};
+MODULE_DEVICE_TABLE(of, owl_uart_dt_matches);
+
+static int owl_uart_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *match;
+ const struct owl_uart_info *info = NULL;
+ struct resource *res_mem;
+ struct owl_uart_port *owl_port;
+ int ret, irq;
+
+ if (pdev->dev.of_node) {
+ pdev->id = of_alias_get_id(pdev->dev.of_node, "serial");
+ match = of_match_node(owl_uart_dt_matches, pdev->dev.of_node);
+ if (match)
+ info = match->data;
+ }
+
+ if (pdev->id < 0 || pdev->id >= OWL_UART_PORT_NUM) {
+ dev_err(&pdev->dev, "id %d out of range\n", pdev->id);
+ return -EINVAL;
+ }
+
+ res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res_mem) {
+ dev_err(&pdev->dev, "could not get mem\n");
+ return -ENODEV;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "could not get irq\n");
+ return irq;
+ }
+
+ if (owl_uart_ports[pdev->id]) {
+ dev_err(&pdev->dev, "port %d already allocated\n", pdev->id);
+ return -EBUSY;
+ }
+
+ owl_port = devm_kzalloc(&pdev->dev, sizeof(*owl_port), GFP_KERNEL);
+ if (!owl_port)
+ return -ENOMEM;
+
+ owl_port->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(owl_port->clk)) {
+ dev_err(&pdev->dev, "could not get clk\n");
+ return PTR_ERR(owl_port->clk);
+ }
+
+ owl_port->port.dev = &pdev->dev;
+ owl_port->port.line = pdev->id;
+ owl_port->port.type = PORT_OWL;
+ owl_port->port.iotype = UPIO_MEM;
+ owl_port->port.mapbase = res_mem->start;
+ owl_port->port.irq = irq;
+ owl_port->port.uartclk = clk_get_rate(owl_port->clk);
+ if (owl_port->port.uartclk == 0) {
+ dev_err(&pdev->dev, "clock rate is zero\n");
+ return -EINVAL;
+ }
+ owl_port->port.flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP | UPF_LOW_LATENCY;
+ owl_port->port.x_char = 0;
+ owl_port->port.fifosize = (info) ? info->tx_fifosize : 16;
+ owl_port->port.ops = &owl_uart_ops;
+
+ owl_uart_ports[pdev->id] = owl_port;
+ platform_set_drvdata(pdev, owl_port);
+
+ ret = uart_add_one_port(&owl_uart_driver, &owl_port->port);
+ if (ret)
+ owl_uart_ports[pdev->id] = NULL;
+
+ return ret;
+}
+
+static int owl_uart_remove(struct platform_device *pdev)
+{
+ struct owl_uart_port *owl_port = platform_get_drvdata(pdev);
+
+ uart_remove_one_port(&owl_uart_driver, &owl_port->port);
+ owl_uart_ports[pdev->id] = NULL;
+
+ return 0;
+}
+
+static struct platform_driver owl_uart_platform_driver = {
+ .probe = owl_uart_probe,
+ .remove = owl_uart_remove,
+ .driver = {
+ .name = "owl-uart",
+ .of_match_table = owl_uart_dt_matches,
+ },
+};
+
+static int __init owl_uart_init(void)
+{
+ int ret;
+
+ ret = uart_register_driver(&owl_uart_driver);
+ if (ret)
+ return ret;
+
+ ret = platform_driver_register(&owl_uart_platform_driver);
+ if (ret)
+ uart_unregister_driver(&owl_uart_driver);
+
+ return ret;
+}
+
+static void __init owl_uart_exit(void)
+{
+ platform_driver_unregister(&owl_uart_platform_driver);
+ uart_unregister_driver(&owl_uart_driver);
+}
+
+module_init(owl_uart_init);
+module_exit(owl_uart_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c
index 42caccb5e87e..d9123f995705 100644
--- a/drivers/tty/serial/pch_uart.c
+++ b/drivers/tty/serial/pch_uart.c
@@ -46,11 +46,6 @@ enum {
PCH_UART_HANDLED_LS_INT_SHIFT,
};
-enum {
- PCH_UART_8LINE,
- PCH_UART_2LINE,
-};
-
#define PCH_UART_DRIVER_DEVICE "ttyPCH"
/* Set the max number of UART port
@@ -267,7 +262,7 @@ struct eg20t_port {
/**
* struct pch_uart_driver_data - private data structure for UART-DMA
- * @port_type: The number of DMA channel
+ * @port_type: The type of UART port
* @line_no: UART port line number (0, 1, 2...)
*/
struct pch_uart_driver_data {
@@ -290,17 +285,17 @@ enum pch_uart_num_t {
};
static struct pch_uart_driver_data drv_dat[] = {
- [pch_et20t_uart0] = {PCH_UART_8LINE, 0},
- [pch_et20t_uart1] = {PCH_UART_2LINE, 1},
- [pch_et20t_uart2] = {PCH_UART_2LINE, 2},
- [pch_et20t_uart3] = {PCH_UART_2LINE, 3},
- [pch_ml7213_uart0] = {PCH_UART_8LINE, 0},
- [pch_ml7213_uart1] = {PCH_UART_2LINE, 1},
- [pch_ml7213_uart2] = {PCH_UART_2LINE, 2},
- [pch_ml7223_uart0] = {PCH_UART_8LINE, 0},
- [pch_ml7223_uart1] = {PCH_UART_2LINE, 1},
- [pch_ml7831_uart0] = {PCH_UART_8LINE, 0},
- [pch_ml7831_uart1] = {PCH_UART_2LINE, 1},
+ [pch_et20t_uart0] = {PORT_PCH_8LINE, 0},
+ [pch_et20t_uart1] = {PORT_PCH_2LINE, 1},
+ [pch_et20t_uart2] = {PORT_PCH_2LINE, 2},
+ [pch_et20t_uart3] = {PORT_PCH_2LINE, 3},
+ [pch_ml7213_uart0] = {PORT_PCH_8LINE, 0},
+ [pch_ml7213_uart1] = {PORT_PCH_2LINE, 1},
+ [pch_ml7213_uart2] = {PORT_PCH_2LINE, 2},
+ [pch_ml7223_uart0] = {PORT_PCH_8LINE, 0},
+ [pch_ml7223_uart1] = {PORT_PCH_2LINE, 1},
+ [pch_ml7831_uart0] = {PORT_PCH_8LINE, 0},
+ [pch_ml7831_uart1] = {PORT_PCH_2LINE, 1},
};
#ifdef CONFIG_SERIAL_PCH_UART_CONSOLE
@@ -376,7 +371,7 @@ static const struct file_operations port_regs_ops = {
};
#endif /* CONFIG_DEBUG_FS */
-static struct dmi_system_id pch_uart_dmi_table[] = {
+static const struct dmi_system_id pch_uart_dmi_table[] = {
{
.ident = "CM-iTC",
{
@@ -878,8 +873,7 @@ static int dma_handle_rx(struct eg20t_port *priv)
sg_dma_len(sg) = priv->trigger_level;
sg_set_page(&priv->sg_rx, virt_to_page(priv->rx_buf_virt),
- sg_dma_len(sg), (unsigned long)priv->rx_buf_virt &
- ~PAGE_MASK);
+ sg_dma_len(sg), offset_in_page(priv->rx_buf_virt));
sg_dma_address(sg) = priv->rx_buf_dma;
@@ -1778,10 +1772,10 @@ static struct eg20t_port *pch_uart_init_port(struct pci_dev *pdev,
goto init_port_free_txbuf;
switch (port_type) {
- case PORT_UNKNOWN:
+ case PORT_PCH_8LINE:
fifosize = 256; /* EG20T/ML7213: UART0 */
break;
- case PORT_8250:
+ case PORT_PCH_2LINE:
fifosize = 64; /* EG20T:UART1~3 ML7213: UART1~2*/
break;
default:
@@ -1805,7 +1799,7 @@ static struct eg20t_port *pch_uart_init_port(struct pci_dev *pdev,
priv->fifo_size = fifosize;
priv->uartclk = pch_uart_get_uartclk();
- priv->port_type = PORT_MAX_8250 + port_type + 1;
+ priv->port_type = port_type;
priv->port.dev = &pdev->dev;
priv->port.iobase = iobase;
priv->port.membase = NULL;
@@ -1863,8 +1857,7 @@ static void pch_uart_exit_port(struct eg20t_port *priv)
{
#ifdef CONFIG_DEBUG_FS
- if (priv->debugfs)
- debugfs_remove(priv->debugfs);
+ debugfs_remove(priv->debugfs);
#endif
uart_remove_one_port(&pch_uart_driver, &priv->port);
free_page((unsigned long)priv->rxbuf.buf);
diff --git a/drivers/tty/serial/pmac_zilog.c b/drivers/tty/serial/pmac_zilog.c
index 0da52947e59e..6ccdd018fb45 100644
--- a/drivers/tty/serial/pmac_zilog.c
+++ b/drivers/tty/serial/pmac_zilog.c
@@ -1671,8 +1671,8 @@ static int __init pmz_probe(void)
if (!node_a && !node_b) {
of_node_put(node_a);
of_node_put(node_b);
- printk(KERN_ERR "pmac_zilog: missing node %c for escc %s\n",
- (!node_a) ? 'a' : 'b', node_p->full_name);
+ printk(KERN_ERR "pmac_zilog: missing node %c for escc %pOF\n",
+ (!node_a) ? 'a' : 'b', node_p);
continue;
}
diff --git a/drivers/tty/serial/pmac_zilog.h b/drivers/tty/serial/pmac_zilog.h
index 3483242ee3e0..bb874e76810e 100644
--- a/drivers/tty/serial/pmac_zilog.h
+++ b/drivers/tty/serial/pmac_zilog.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __PMAC_ZILOG_H__
#define __PMAC_ZILOG_H__
diff --git a/drivers/tty/serial/sccnxp.c b/drivers/tty/serial/sccnxp.c
index fcf803ffad19..b9c7a904c1ea 100644
--- a/drivers/tty/serial/sccnxp.c
+++ b/drivers/tty/serial/sccnxp.c
@@ -884,14 +884,28 @@ static int sccnxp_probe(struct platform_device *pdev)
clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(clk)) {
- if (PTR_ERR(clk) == -EPROBE_DEFER) {
- ret = -EPROBE_DEFER;
+ ret = PTR_ERR(clk);
+ if (ret == -EPROBE_DEFER)
goto err_out;
- }
+ uartclk = 0;
+ } else {
+ ret = clk_prepare_enable(clk);
+ if (ret)
+ goto err_out;
+
+ ret = devm_add_action_or_reset(&pdev->dev,
+ (void(*)(void *))clk_disable_unprepare,
+ clk);
+ if (ret)
+ goto err_out;
+
+ uartclk = clk_get_rate(clk);
+ }
+
+ if (!uartclk) {
dev_notice(&pdev->dev, "Using default clock frequency\n");
uartclk = s->chip->freq_std;
- } else
- uartclk = clk_get_rate(clk);
+ }
/* Check input frequency */
if ((uartclk < s->chip->freq_min) || (uartclk > s->chip->freq_max)) {
@@ -983,7 +997,7 @@ static int sccnxp_probe(struct platform_device *pdev)
uart_unregister_driver(&s->uart);
err_out:
if (!IS_ERR(s->regulator))
- return regulator_disable(s->regulator);
+ regulator_disable(s->regulator);
return ret;
}
diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c
index d92a150c8733..cf9b736f26f8 100644
--- a/drivers/tty/serial/serial-tegra.c
+++ b/drivers/tty/serial/serial-tegra.c
@@ -1310,7 +1310,7 @@ static int tegra_uart_probe(struct platform_device *pdev)
return PTR_ERR(tup->uart_clk);
}
- tup->rst = devm_reset_control_get(&pdev->dev, "serial");
+ tup->rst = devm_reset_control_get_exclusive(&pdev->dev, "serial");
if (IS_ERR(tup->rst)) {
dev_err(&pdev->dev, "Couldn't get the reset\n");
return PTR_ERR(tup->rst);
diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index 13bfd5dcffce..3a14cccbd7ff 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -36,7 +36,7 @@
#include <linux/delay.h>
#include <linux/mutex.h>
-#include <asm/irq.h>
+#include <linux/irq.h>
#include <linux/uaccess.h>
/*
@@ -165,6 +165,27 @@ uart_update_mctrl(struct uart_port *port, unsigned int set, unsigned int clear)
#define uart_set_mctrl(port, set) uart_update_mctrl(port, set, 0)
#define uart_clear_mctrl(port, clear) uart_update_mctrl(port, 0, clear)
+static void uart_port_dtr_rts(struct uart_port *uport, int raise)
+{
+ int rs485_on = uport->rs485_config &&
+ (uport->rs485.flags & SER_RS485_ENABLED);
+ int RTS_after_send = !!(uport->rs485.flags & SER_RS485_RTS_AFTER_SEND);
+
+ if (raise) {
+ if (rs485_on && !RTS_after_send) {
+ uart_set_mctrl(uport, TIOCM_DTR);
+ uart_clear_mctrl(uport, TIOCM_RTS);
+ } else {
+ uart_set_mctrl(uport, TIOCM_DTR | TIOCM_RTS);
+ }
+ } else {
+ unsigned int clear = TIOCM_DTR;
+
+ clear |= (!rs485_on || !RTS_after_send) ? TIOCM_RTS : 0;
+ uart_clear_mctrl(uport, clear);
+ }
+}
+
/*
* Startup the port. This will be called once per open. All calls
* will be serialised by the per-port mutex.
@@ -214,7 +235,7 @@ static int uart_port_startup(struct tty_struct *tty, struct uart_state *state,
* port is open and ready to respond.
*/
if (init_hw && C_BAUD(tty))
- uart_set_mctrl(uport, TIOCM_RTS | TIOCM_DTR);
+ uart_port_dtr_rts(uport, 1);
}
/*
@@ -272,7 +293,7 @@ static void uart_shutdown(struct tty_struct *tty, struct uart_state *state)
uport->cons->cflag = tty->termios.c_cflag;
if (!tty || C_HUPCL(tty))
- uart_clear_mctrl(uport, TIOCM_DTR | TIOCM_RTS);
+ uart_port_dtr_rts(uport, 0);
uart_port_shutdown(port);
}
@@ -744,7 +765,7 @@ static int uart_get_info(struct tty_port *port, struct serial_struct *retinfo)
if (HIGH_BITS_OFFSET)
retinfo->port_high = (long) uport->iobase >> HIGH_BITS_OFFSET;
retinfo->irq = uport->irq;
- retinfo->flags = uport->flags;
+ retinfo->flags = (__force int)uport->flags;
retinfo->xmit_fifo_size = uport->fifosize;
retinfo->baud_base = uport->uartclk / 16;
retinfo->close_delay = jiffies_to_msecs(port->close_delay) / 10;
@@ -818,7 +839,7 @@ static int uart_set_info(struct tty_struct *tty, struct tty_port *port,
new_info->type != uport->type);
old_flags = uport->flags;
- new_flags = new_info->flags;
+ new_flags = (__force upf_t)new_info->flags;
old_custom_divisor = uport->custom_divisor;
if (!capable(CAP_SYS_ADMIN)) {
@@ -954,11 +975,10 @@ static int uart_set_info(struct tty_struct *tty, struct tty_port *port,
old_custom_divisor != uport->custom_divisor) {
/*
* If they're setting up a custom divisor or speed,
- * instead of clearing it, then bitch about it. No
- * need to rate-limit; it's CAP_SYS_ADMIN only.
+ * instead of clearing it, then bitch about it.
*/
if (uport->flags & UPF_SPD_MASK) {
- dev_notice(uport->dev,
+ dev_notice_ratelimited(uport->dev,
"%s sets custom speed on %s. This is deprecated.\n",
current->comm,
tty_name(port->tty));
@@ -1659,7 +1679,7 @@ static int uart_carrier_raised(struct tty_port *port)
return 0;
}
-static void uart_dtr_rts(struct tty_port *port, int onoff)
+static void uart_dtr_rts(struct tty_port *port, int raise)
{
struct uart_state *state = container_of(port, struct uart_state, port);
struct uart_port *uport;
@@ -1667,12 +1687,7 @@ static void uart_dtr_rts(struct tty_port *port, int onoff)
uport = uart_port_ref(state);
if (!uport)
return;
-
- if (onoff)
- uart_set_mctrl(uport, TIOCM_DTR | TIOCM_RTS);
- else
- uart_clear_mctrl(uport, TIOCM_DTR | TIOCM_RTS);
-
+ uart_port_dtr_rts(uport, raise);
uart_port_deref(uport);
}
@@ -2084,8 +2099,7 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *uport)
tty_dev = device_find_child(uport->dev, &match, serial_match_port);
if (tty_dev && device_may_wakeup(tty_dev)) {
- if (!enable_irq_wake(uport->irq))
- uport->irq_wake = 1;
+ enable_irq_wake(uport->irq);
put_device(tty_dev);
mutex_unlock(&port->mutex);
return 0;
@@ -2148,10 +2162,8 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport)
tty_dev = device_find_child(uport->dev, &match, serial_match_port);
if (!uport->suspended && device_may_wakeup(tty_dev)) {
- if (uport->irq_wake) {
+ if (irqd_is_wakeup_set(irq_get_irq_data((uport->irq))))
disable_irq_wake(uport->irq);
- uport->irq_wake = 0;
- }
put_device(tty_dev);
mutex_unlock(&port->mutex);
return 0;
diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c
index 71707e8e6e3f..784dd42002ea 100644
--- a/drivers/tty/serial/sh-sci.c
+++ b/drivers/tty/serial/sh-sci.c
@@ -1085,10 +1085,12 @@ static ssize_t rx_trigger_store(struct device *dev,
{
struct uart_port *port = dev_get_drvdata(dev);
struct sci_port *sci = to_sci_port(port);
+ int ret;
long r;
- if (kstrtol(buf, 0, &r) == -EINVAL)
- return -EINVAL;
+ ret = kstrtol(buf, 0, &r);
+ if (ret)
+ return ret;
sci->rx_trigger = scif_set_rtrg(port, r);
if (port->type == PORT_SCIFA || port->type == PORT_SCIFB)
@@ -1116,10 +1118,12 @@ static ssize_t rx_fifo_timeout_store(struct device *dev,
{
struct uart_port *port = dev_get_drvdata(dev);
struct sci_port *sci = to_sci_port(port);
+ int ret;
long r;
- if (kstrtol(buf, 0, &r) == -EINVAL)
- return -EINVAL;
+ ret = kstrtol(buf, 0, &r);
+ if (ret)
+ return ret;
sci->rx_fifo_timeout = r;
scif_set_rtrg(port, 1);
if (r > 0)
@@ -1450,8 +1454,7 @@ static struct dma_chan *sci_request_dma_chan(struct uart_port *port,
chan = dma_request_slave_channel(port->dev,
dir == DMA_MEM_TO_DEV ? "tx" : "rx");
if (!chan) {
- dev_warn(port->dev,
- "dma_request_slave_channel_compat failed\n");
+ dev_warn(port->dev, "dma_request_slave_channel failed\n");
return NULL;
}
@@ -1558,7 +1561,16 @@ static void sci_free_dma(struct uart_port *port)
if (s->chan_rx)
sci_rx_dma_release(s, false);
}
-#else
+
+static void sci_flush_buffer(struct uart_port *port)
+{
+ /*
+ * In uart_flush_buffer(), the xmit circular buffer has just been
+ * cleared, so we have to reset tx_dma_len accordingly.
+ */
+ to_sci_port(port)->tx_dma_len = 0;
+}
+#else /* !CONFIG_SERIAL_SH_SCI_DMA */
static inline void sci_request_dma(struct uart_port *port)
{
}
@@ -1566,7 +1578,9 @@ static inline void sci_request_dma(struct uart_port *port)
static inline void sci_free_dma(struct uart_port *port)
{
}
-#endif
+
+#define sci_flush_buffer NULL
+#endif /* !CONFIG_SERIAL_SH_SCI_DMA */
static irqreturn_t sci_rx_interrupt(int irq, void *ptr)
{
@@ -2581,6 +2595,7 @@ static const struct uart_ops sci_uart_ops = {
.break_ctl = sci_break_ctl,
.startup = sci_startup,
.shutdown = sci_shutdown,
+ .flush_buffer = sci_flush_buffer,
.set_termios = sci_set_termios,
.pm = sci_pm,
.type = sci_type,
@@ -2950,6 +2965,7 @@ static inline int sci_probe_earlyprintk(struct platform_device *pdev)
static const char banner[] __initconst = "SuperH (H)SCI(F) driver initialized";
+static DEFINE_MUTEX(sci_uart_registration_lock);
static struct uart_driver sci_uart_driver = {
.owner = THIS_MODULE,
.driver_name = "sci",
@@ -3057,8 +3073,7 @@ static struct plat_sci_port *sci_parse_dt(struct platform_device *pdev,
p->type = SCI_OF_TYPE(match->data);
p->regtype = SCI_OF_REGTYPE(match->data);
- if (of_find_property(np, "uart-has-rtscts", NULL))
- sp->has_rtscts = true;
+ sp->has_rtscts = of_property_read_bool(np, "uart-has-rtscts");
return p;
}
@@ -3078,6 +3093,16 @@ static int sci_probe_single(struct platform_device *dev,
return -EINVAL;
}
+ mutex_lock(&sci_uart_registration_lock);
+ if (!sci_uart_driver.state) {
+ ret = uart_register_driver(&sci_uart_driver);
+ if (ret) {
+ mutex_unlock(&sci_uart_registration_lock);
+ return ret;
+ }
+ }
+ mutex_unlock(&sci_uart_registration_lock);
+
ret = sci_init_single(dev, sciport, index, p, false);
if (ret)
return ret;
@@ -3201,24 +3226,17 @@ static struct platform_driver sci_driver = {
static int __init sci_init(void)
{
- int ret;
-
pr_info("%s\n", banner);
- ret = uart_register_driver(&sci_uart_driver);
- if (likely(ret == 0)) {
- ret = platform_driver_register(&sci_driver);
- if (unlikely(ret))
- uart_unregister_driver(&sci_uart_driver);
- }
-
- return ret;
+ return platform_driver_register(&sci_driver);
}
static void __exit sci_exit(void)
{
platform_driver_unregister(&sci_driver);
- uart_unregister_driver(&sci_uart_driver);
+
+ if (sci_uart_driver.state)
+ uart_unregister_driver(&sci_uart_driver);
}
#ifdef CONFIG_SERIAL_SH_SCI_CONSOLE
diff --git a/drivers/tty/serial/sh-sci.h b/drivers/tty/serial/sh-sci.h
index 971b2ab088d8..938e23a2d166 100644
--- a/drivers/tty/serial/sh-sci.h
+++ b/drivers/tty/serial/sh-sci.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#include <linux/bitops.h>
#include <linux/serial_core.h>
#include <linux/io.h>
diff --git a/drivers/tty/serial/sirfsoc_uart.c b/drivers/tty/serial/sirfsoc_uart.c
index e03282d92b59..684cb8dd8050 100644
--- a/drivers/tty/serial/sirfsoc_uart.c
+++ b/drivers/tty/serial/sirfsoc_uart.c
@@ -1253,7 +1253,7 @@ next_hrt:
return HRTIMER_RESTART;
}
-static struct of_device_id sirfsoc_uart_ids[] = {
+static const struct of_device_id sirfsoc_uart_ids[] = {
{ .compatible = "sirf,prima2-uart", .data = &sirfsoc_uart,},
{ .compatible = "sirf,atlas7-uart", .data = &sirfsoc_uart},
{ .compatible = "sirf,prima2-usp-uart", .data = &sirfsoc_usp},
diff --git a/drivers/tty/serial/sprd_serial.c b/drivers/tty/serial/sprd_serial.c
index 90996ad97b37..e902494ebbd5 100644
--- a/drivers/tty/serial/sprd_serial.c
+++ b/drivers/tty/serial/sprd_serial.c
@@ -63,6 +63,7 @@
/* interrupt clear register */
#define SPRD_ICLR 0x0014
+#define SPRD_ICLR_TIMEOUT BIT(13)
/* line control register */
#define SPRD_LCR 0x0018
@@ -298,7 +299,8 @@ static irqreturn_t sprd_handle_irq(int irq, void *dev_id)
return IRQ_NONE;
}
- serial_out(port, SPRD_ICLR, ~0);
+ if (ims & SPRD_IMSR_TIMEOUT)
+ serial_out(port, SPRD_ICLR, SPRD_ICLR_TIMEOUT);
if (ims & (SPRD_IMSR_RX_FIFO_FULL |
SPRD_IMSR_BREAK_DETECT | SPRD_IMSR_TIMEOUT))
@@ -729,8 +731,8 @@ static int sprd_probe(struct platform_device *pdev)
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
- dev_err(&pdev->dev, "not provide irq resource\n");
- return -ENODEV;
+ dev_err(&pdev->dev, "not provide irq resource: %d\n", irq);
+ return irq;
}
up->irq = irq;
diff --git a/drivers/tty/serial/st-asc.c b/drivers/tty/serial/st-asc.c
index f5335be344f6..b313a792b149 100644
--- a/drivers/tty/serial/st-asc.c
+++ b/drivers/tty/serial/st-asc.c
@@ -310,7 +310,7 @@ static void asc_receive_chars(struct uart_port *port)
if (mode == ASC_CTL_MODE_8BIT || mode == ASC_CTL_MODE_8BIT_PAR)
ignore_pe = true;
- if (port->irq_wake)
+ if (irqd_is_wakeup_set(irq_get_irq_data(port->irq)))
pm_wakeup_event(tport->tty->dev, 0);
while ((status = asc_in(port, ASC_STA)) & ASC_STA_RBF) {
@@ -758,6 +758,7 @@ static int asc_init_port(struct asc_port *ascport,
if (IS_ERR(ascport->pinctrl)) {
ret = PTR_ERR(ascport->pinctrl);
dev_err(&pdev->dev, "Failed to get Pinctrl: %d\n", ret);
+ return ret;
}
ascport->states[DEFAULT] =
diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c
index 033856287ca2..03a583264d9e 100644
--- a/drivers/tty/serial/stm32-usart.c
+++ b/drivers/tty/serial/stm32-usart.c
@@ -1,5 +1,6 @@
/*
* Copyright (C) Maxime Coquelin 2015
+ * Copyright (C) STMicroelectronics SA 2017
* Authors: Maxime Coquelin <mcoquelin.stm32@gmail.com>
* Gerald Baeza <gerald.baeza@st.com>
* License terms: GNU General Public License (GPL), version 2
@@ -25,6 +26,7 @@
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
+#include <linux/pm_wakeirq.h>
#include <linux/serial_core.h>
#include <linux/serial.h>
#include <linux/spinlock.h>
@@ -110,14 +112,13 @@ static void stm32_receive_chars(struct uart_port *port, bool threaded)
unsigned long c;
u32 sr;
char flag;
- static int last_res = RX_BUF_L;
- if (port->irq_wake)
+ if (irqd_is_wakeup_set(irq_get_irq_data(port->irq)))
pm_wakeup_event(tport->tty->dev, 0);
- while (stm32_pending_rx(port, &sr, &last_res, threaded)) {
+ while (stm32_pending_rx(port, &sr, &stm32_port->last_res, threaded)) {
sr |= USART_SR_DUMMY_RX;
- c = stm32_get_char(port, &sr, &last_res);
+ c = stm32_get_char(port, &sr, &stm32_port->last_res);
flag = TTY_NORMAL;
port->icount.rx++;
@@ -202,7 +203,7 @@ static void stm32_transmit_chars_pio(struct uart_port *port)
ret = readl_relaxed_poll_timeout_atomic(port->membase + ofs->isr,
isr,
(isr & USART_SR_TXE),
- 10, 100);
+ 10, 100000);
if (ret)
dev_err(port->dev, "tx empty not set\n");
@@ -326,6 +327,10 @@ static irqreturn_t stm32_interrupt(int irq, void *ptr)
sr = readl_relaxed(port->membase + ofs->isr);
+ if ((sr & USART_SR_WUF) && (ofs->icr != UNDEF_REG))
+ writel_relaxed(USART_ICR_WUCF,
+ port->membase + ofs->icr);
+
if ((sr & USART_SR_RXNE) && !(stm32_port->rx_ch))
stm32_receive_chars(port, false);
@@ -442,6 +447,7 @@ static int stm32_startup(struct uart_port *port)
{
struct stm32_port *stm32_port = to_stm32_port(port);
struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
+ struct stm32_usart_config *cfg = &stm32_port->info->cfg;
const char *name = to_platform_device(port->dev)->name;
u32 val;
int ret;
@@ -452,7 +458,18 @@ static int stm32_startup(struct uart_port *port)
if (ret)
return ret;
+ if (cfg->has_wakeup && stm32_port->wakeirq >= 0) {
+ ret = dev_pm_set_dedicated_wake_irq(port->dev,
+ stm32_port->wakeirq);
+ if (ret) {
+ free_irq(port->irq, port);
+ return ret;
+ }
+ }
+
val = USART_CR1_RXNEIE | USART_CR1_TE | USART_CR1_RE;
+ if (stm32_port->fifoen)
+ val |= USART_CR1_FIFOEN;
stm32_set_bits(port, ofs->cr1, val);
return 0;
@@ -467,8 +484,11 @@ static void stm32_shutdown(struct uart_port *port)
val = USART_CR1_TXEIE | USART_CR1_RXNEIE | USART_CR1_TE | USART_CR1_RE;
val |= BIT(cfg->uart_enable_bit);
+ if (stm32_port->fifoen)
+ val |= USART_CR1_FIFOEN;
stm32_clr_bits(port, ofs->cr1, val);
+ dev_pm_clear_wake_irq(port->dev);
free_irq(port->irq, port);
}
@@ -496,6 +516,8 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios,
cr1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_RXNEIE;
cr1 |= BIT(cfg->uart_enable_bit);
+ if (stm32_port->fifoen)
+ cr1 |= USART_CR1_FIFOEN;
cr2 = 0;
cr3 = 0;
@@ -518,7 +540,7 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios,
port->status &= ~(UPSTAT_AUTOCTS | UPSTAT_AUTORTS);
if (cflag & CRTSCTS) {
port->status |= UPSTAT_AUTOCTS | UPSTAT_AUTORTS;
- cr3 |= USART_CR3_CTSE;
+ cr3 |= USART_CR3_CTSE | USART_CR3_RTSE;
}
usartdiv = DIV_ROUND_CLOSEST(port->uartclk, baud);
@@ -659,6 +681,8 @@ static int stm32_init_port(struct stm32_port *stm32port,
port->ops = &stm32_uart_ops;
port->dev = &pdev->dev;
port->irq = platform_get_irq(pdev, 0);
+ stm32port->wakeirq = platform_get_irq(pdev, 1);
+ stm32port->fifoen = stm32port->info->cfg.has_fifo;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
port->membase = devm_ioremap_resource(&pdev->dev, res);
@@ -678,8 +702,10 @@ static int stm32_init_port(struct stm32_port *stm32port,
return ret;
stm32port->port.uartclk = clk_get_rate(stm32port->clk);
- if (!stm32port->port.uartclk)
+ if (!stm32port->port.uartclk) {
+ clk_disable_unprepare(stm32port->clk);
ret = -EINVAL;
+ }
return ret;
}
@@ -693,8 +719,10 @@ static struct stm32_port *stm32_of_get_stm32_port(struct platform_device *pdev)
return NULL;
id = of_alias_get_id(np, "serial");
- if (id < 0)
- id = 0;
+ if (id < 0) {
+ dev_err(&pdev->dev, "failed to get alias id, errno %d\n", id);
+ return NULL;
+ }
if (WARN_ON(id >= STM32_MAX_PORTS))
return NULL;
@@ -702,6 +730,7 @@ static struct stm32_port *stm32_of_get_stm32_port(struct platform_device *pdev)
stm32_ports[id].hw_flow_control = of_property_read_bool(np,
"st,hw-flow-ctrl");
stm32_ports[id].port.line = id;
+ stm32_ports[id].last_res = RX_BUF_L;
return &stm32_ports[id];
}
@@ -711,6 +740,8 @@ static const struct of_device_id stm32_match[] = {
{ .compatible = "st,stm32-uart", .data = &stm32f4_info},
{ .compatible = "st,stm32f7-usart", .data = &stm32f7_info},
{ .compatible = "st,stm32f7-uart", .data = &stm32f7_info},
+ { .compatible = "st,stm32h7-usart", .data = &stm32h7_info},
+ { .compatible = "st,stm32h7-uart", .data = &stm32h7_info},
{},
};
@@ -860,9 +891,15 @@ static int stm32_serial_probe(struct platform_device *pdev)
if (ret)
return ret;
+ if (stm32port->info->cfg.has_wakeup && stm32port->wakeirq >= 0) {
+ ret = device_init_wakeup(&pdev->dev, true);
+ if (ret)
+ goto err_uninit;
+ }
+
ret = uart_add_one_port(&stm32_usart_driver, &stm32port->port);
if (ret)
- return ret;
+ goto err_nowup;
ret = stm32_of_dma_rx_probe(stm32port, pdev);
if (ret)
@@ -875,6 +912,15 @@ static int stm32_serial_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, &stm32port->port);
return 0;
+
+err_nowup:
+ if (stm32port->info->cfg.has_wakeup && stm32port->wakeirq >= 0)
+ device_init_wakeup(&pdev->dev, false);
+
+err_uninit:
+ clk_disable_unprepare(stm32port->clk);
+
+ return ret;
}
static int stm32_serial_remove(struct platform_device *pdev)
@@ -882,6 +928,7 @@ static int stm32_serial_remove(struct platform_device *pdev)
struct uart_port *port = platform_get_drvdata(pdev);
struct stm32_port *stm32_port = to_stm32_port(port);
struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
+ struct stm32_usart_config *cfg = &stm32_port->info->cfg;
stm32_clr_bits(port, ofs->cr3, USART_CR3_DMAR);
@@ -903,6 +950,9 @@ static int stm32_serial_remove(struct platform_device *pdev)
TX_BUF_L, stm32_port->tx_buf,
stm32_port->tx_dma_buf);
+ if (cfg->has_wakeup && stm32_port->wakeirq >= 0)
+ device_init_wakeup(&pdev->dev, false);
+
clk_disable_unprepare(stm32_port->clk);
return uart_remove_one_port(&stm32_usart_driver, port);
@@ -1008,11 +1058,66 @@ static struct uart_driver stm32_usart_driver = {
.cons = STM32_SERIAL_CONSOLE,
};
+#ifdef CONFIG_PM_SLEEP
+static void stm32_serial_enable_wakeup(struct uart_port *port, bool enable)
+{
+ struct stm32_port *stm32_port = to_stm32_port(port);
+ struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
+ struct stm32_usart_config *cfg = &stm32_port->info->cfg;
+ u32 val;
+
+ if (!cfg->has_wakeup || stm32_port->wakeirq < 0)
+ return;
+
+ if (enable) {
+ stm32_clr_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit));
+ stm32_set_bits(port, ofs->cr1, USART_CR1_UESM);
+ val = readl_relaxed(port->membase + ofs->cr3);
+ val &= ~USART_CR3_WUS_MASK;
+ /* Enable Wake up interrupt from low power on start bit */
+ val |= USART_CR3_WUS_START_BIT | USART_CR3_WUFIE;
+ writel_relaxed(val, port->membase + ofs->cr3);
+ stm32_set_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit));
+ } else {
+ stm32_clr_bits(port, ofs->cr1, USART_CR1_UESM);
+ }
+}
+
+static int stm32_serial_suspend(struct device *dev)
+{
+ struct uart_port *port = dev_get_drvdata(dev);
+
+ uart_suspend_port(&stm32_usart_driver, port);
+
+ if (device_may_wakeup(dev))
+ stm32_serial_enable_wakeup(port, true);
+ else
+ stm32_serial_enable_wakeup(port, false);
+
+ return 0;
+}
+
+static int stm32_serial_resume(struct device *dev)
+{
+ struct uart_port *port = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev))
+ stm32_serial_enable_wakeup(port, false);
+
+ return uart_resume_port(&stm32_usart_driver, port);
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct dev_pm_ops stm32_serial_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(stm32_serial_suspend, stm32_serial_resume)
+};
+
static struct platform_driver stm32_serial_driver = {
.probe = stm32_serial_probe,
.remove = stm32_serial_remove,
.driver = {
.name = DRIVER_NAME,
+ .pm = &stm32_serial_pm_ops,
.of_match_table = of_match_ptr(stm32_match),
},
};
diff --git a/drivers/tty/serial/stm32-usart.h b/drivers/tty/serial/stm32-usart.h
index cd97ceb76e4f..ffc0c5285e51 100644
--- a/drivers/tty/serial/stm32-usart.h
+++ b/drivers/tty/serial/stm32-usart.h
@@ -1,5 +1,6 @@
/*
* Copyright (C) Maxime Coquelin 2015
+ * Copyright (C) STMicroelectronics SA 2017
* Authors: Maxime Coquelin <mcoquelin.stm32@gmail.com>
* Gerald Baeza <gerald_baeza@yahoo.fr>
* License terms: GNU General Public License (GPL), version 2
@@ -24,6 +25,8 @@ struct stm32_usart_offsets {
struct stm32_usart_config {
u8 uart_enable_bit; /* USART_CR1_UE */
bool has_7bits_data;
+ bool has_wakeup;
+ bool has_fifo;
};
struct stm32_usart_info {
@@ -74,6 +77,28 @@ struct stm32_usart_info stm32f7_info = {
}
};
+struct stm32_usart_info stm32h7_info = {
+ .ofs = {
+ .cr1 = 0x00,
+ .cr2 = 0x04,
+ .cr3 = 0x08,
+ .brr = 0x0c,
+ .gtpr = 0x10,
+ .rtor = 0x14,
+ .rqr = 0x18,
+ .isr = 0x1c,
+ .icr = 0x20,
+ .rdr = 0x24,
+ .tdr = 0x28,
+ },
+ .cfg = {
+ .uart_enable_bit = 0,
+ .has_7bits_data = true,
+ .has_wakeup = true,
+ .has_fifo = true,
+ }
+};
+
/* USART_SR (F4) / USART_ISR (F7) */
#define USART_SR_PE BIT(0)
#define USART_SR_FE BIT(1)
@@ -93,6 +118,7 @@ struct stm32_usart_info stm32f7_info = {
#define USART_SR_BUSY BIT(16) /* F7 */
#define USART_SR_CMF BIT(17) /* F7 */
#define USART_SR_SBKF BIT(18) /* F7 */
+#define USART_SR_WUF BIT(20) /* H7 */
#define USART_SR_TEACK BIT(21) /* F7 */
#define USART_SR_ERR_MASK (USART_SR_LBD | USART_SR_ORE | \
USART_SR_FE | USART_SR_PE)
@@ -113,6 +139,7 @@ struct stm32_usart_info stm32f7_info = {
/* USART_CR1 */
#define USART_CR1_SBK BIT(0)
#define USART_CR1_RWU BIT(1) /* F4 */
+#define USART_CR1_UESM BIT(1) /* H7 */
#define USART_CR1_RE BIT(2)
#define USART_CR1_TE BIT(3)
#define USART_CR1_IDLEIE BIT(4)
@@ -134,6 +161,7 @@ struct stm32_usart_info stm32f7_info = {
#define USART_CR1_EOBIE BIT(27) /* F7 */
#define USART_CR1_M1 BIT(28) /* F7 */
#define USART_CR1_IE_MASK (GENMASK(8, 4) | BIT(14) | BIT(26) | BIT(27))
+#define USART_CR1_FIFOEN BIT(29) /* H7 */
/* USART_CR2 */
#define USART_CR2_ADD_MASK GENMASK(3, 0) /* F4 */
@@ -175,6 +203,9 @@ struct stm32_usart_info stm32f7_info = {
#define USART_CR3_DEM BIT(14) /* F7 */
#define USART_CR3_DEP BIT(15) /* F7 */
#define USART_CR3_SCARCNT_MASK GENMASK(19, 17) /* F7 */
+#define USART_CR3_WUS_MASK GENMASK(21, 20) /* H7 */
+#define USART_CR3_WUS_START_BIT BIT(21) /* H7 */
+#define USART_CR3_WUFIE BIT(22) /* H7 */
/* USART_GTPR */
#define USART_GTPR_PSC_MASK GENMASK(7, 0)
@@ -203,9 +234,10 @@ struct stm32_usart_info stm32f7_info = {
#define USART_ICR_RTOCF BIT(11) /* F7 */
#define USART_ICR_EOBCF BIT(12) /* F7 */
#define USART_ICR_CMCF BIT(17) /* F7 */
+#define USART_ICR_WUCF BIT(20) /* H7 */
#define STM32_SERIAL_NAME "ttyS"
-#define STM32_MAX_PORTS 6
+#define STM32_MAX_PORTS 8
#define RX_BUF_L 200 /* dma rx buffer length */
#define RX_BUF_P RX_BUF_L /* dma rx buffer period */
@@ -221,8 +253,11 @@ struct stm32_port {
struct dma_chan *tx_ch; /* dma tx channel */
dma_addr_t tx_dma_buf; /* dma tx buffer bus address */
unsigned char *tx_buf; /* dma tx buffer cpu address */
+ int last_res;
bool tx_dma_busy; /* dma tx busy */
bool hw_flow_control;
+ bool fifoen;
+ int wakeirq;
};
static struct stm32_port stm32_ports[STM32_MAX_PORTS];
diff --git a/drivers/tty/serial/sunsab.c b/drivers/tty/serial/sunsab.c
index b5e3195b3697..653a076d89d3 100644
--- a/drivers/tty/serial/sunsab.c
+++ b/drivers/tty/serial/sunsab.c
@@ -819,7 +819,7 @@ static int sunsab_verify_port(struct uart_port *port, struct serial_struct *ser)
return -EINVAL;
}
-static struct uart_ops sunsab_pops = {
+static const struct uart_ops sunsab_pops = {
.tx_empty = sunsab_tx_empty,
.set_mctrl = sunsab_set_mctrl,
.get_mctrl = sunsab_get_mctrl,
diff --git a/drivers/tty/serial/sunsab.h b/drivers/tty/serial/sunsab.h
index b78e1f7b8050..1644031aacda 100644
--- a/drivers/tty/serial/sunsab.h
+++ b/drivers/tty/serial/sunsab.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/* sunsab.h: Register Definitions for the Siemens SAB82532 DUSCC
*
* Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
diff --git a/drivers/tty/serial/sunsu.c b/drivers/tty/serial/sunsu.c
index 72df2e1b88af..95d34d7565c9 100644
--- a/drivers/tty/serial/sunsu.c
+++ b/drivers/tty/serial/sunsu.c
@@ -958,7 +958,7 @@ sunsu_type(struct uart_port *port)
return uart_config[type].name;
}
-static struct uart_ops sunsu_pops = {
+static const struct uart_ops sunsu_pops = {
.tx_empty = sunsu_tx_empty,
.set_mctrl = sunsu_set_mctrl,
.get_mctrl = sunsu_get_mctrl,
@@ -1212,8 +1212,8 @@ static int sunsu_kbd_ms_init(struct uart_sunsu_port *up)
if (up->port.type == PORT_UNKNOWN)
return -ENODEV;
- printk("%s: %s port at %llx, irq %u\n",
- up->port.dev->of_node->full_name,
+ printk("%pOF: %s port at %llx, irq %u\n",
+ up->port.dev->of_node,
(up->su_type == SU_PORT_KBD) ? "Keyboard" : "Mouse",
(unsigned long long) up->port.mapbase,
up->port.irq);
diff --git a/drivers/tty/serial/sunzilog.h b/drivers/tty/serial/sunzilog.h
index 5dec7b47cc38..6d6764f0ac98 100644
--- a/drivers/tty/serial/sunzilog.h
+++ b/drivers/tty/serial/sunzilog.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _SUNZILOG_H
#define _SUNZILOG_H
diff --git a/drivers/tty/serial/ucc_uart.c b/drivers/tty/serial/ucc_uart.c
index 481eb2989a1e..55b702775786 100644
--- a/drivers/tty/serial/ucc_uart.c
+++ b/drivers/tty/serial/ucc_uart.c
@@ -1085,7 +1085,7 @@ static int qe_uart_verify_port(struct uart_port *port,
*
* Details on these functions can be found in Documentation/serial/driver
*/
-static struct uart_ops qe_uart_pops = {
+static const struct uart_ops qe_uart_pops = {
.tx_empty = qe_uart_tx_empty,
.set_mctrl = qe_uart_set_mctrl,
.get_mctrl = qe_uart_get_mctrl,
diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c
index c0539950f8d7..31a630ae0870 100644
--- a/drivers/tty/serial/xilinx_uartps.c
+++ b/drivers/tty/serial/xilinx_uartps.c
@@ -186,6 +186,7 @@ MODULE_PARM_DESC(rx_timeout, "Rx timeout, 1-255");
* @pclk: APB clock
* @baud: Current baud rate
* @clk_rate_change_nb: Notifier block for clock changes
+ * @quirks: Flags for RXBS support.
*/
struct cdns_uart {
struct uart_port *port;
@@ -1162,7 +1163,7 @@ static void cdns_uart_console_putchar(struct uart_port *port, int ch)
writel(ch, port->membase + CDNS_UART_FIFO);
}
-static void __init cdns_early_write(struct console *con, const char *s,
+static void cdns_early_write(struct console *con, const char *s,
unsigned n)
{
struct earlycon_device *dev = con->data;
@@ -1587,20 +1588,21 @@ static int cdns_uart_probe(struct platform_device *pdev)
if (rc) {
dev_err(&pdev->dev,
"uart_add_one_port() failed; err=%i\n", rc);
- goto err_out_notif_unreg;
+ goto err_out_pm_disable;
}
return 0;
+err_out_pm_disable:
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_set_suspended(&pdev->dev);
+ pm_runtime_dont_use_autosuspend(&pdev->dev);
err_out_notif_unreg:
#ifdef CONFIG_COMMON_CLK
clk_notifier_unregister(cdns_uart_data->uartclk,
&cdns_uart_data->clk_rate_change_nb);
#endif
err_out_clk_disable:
- pm_runtime_disable(&pdev->dev);
- pm_runtime_set_suspended(&pdev->dev);
- pm_runtime_dont_use_autosuspend(&pdev->dev);
clk_disable_unprepare(cdns_uart_data->uartclk);
err_out_clk_dis_pclk:
clk_disable_unprepare(cdns_uart_data->pclk);
diff --git a/drivers/tty/serial/zs.h b/drivers/tty/serial/zs.h
index aa921b57d827..26ef8eafa1c1 100644
--- a/drivers/tty/serial/zs.h
+++ b/drivers/tty/serial/zs.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* zs.h: Definitions for the DECstation Z85C30 serial driver.
*
diff --git a/drivers/tty/synclink.c b/drivers/tty/synclink.c
index a2c308f7d637..3be981101297 100644
--- a/drivers/tty/synclink.c
+++ b/drivers/tty/synclink.c
@@ -884,7 +884,7 @@ static int synclink_init_one (struct pci_dev *dev,
const struct pci_device_id *ent);
static void synclink_remove_one (struct pci_dev *dev);
-static struct pci_device_id synclink_pci_tbl[] = {
+static const struct pci_device_id synclink_pci_tbl[] = {
{ PCI_VENDOR_ID_MICROGATE, PCI_DEVICE_ID_MICROGATE_USC, PCI_ANY_ID, PCI_ANY_ID, },
{ PCI_VENDOR_ID_MICROGATE, 0x0210, PCI_ANY_ID, PCI_ANY_ID, },
{ 0, }, /* terminate list */
@@ -7960,7 +7960,7 @@ static void hdlcdev_rx(struct mgsl_struct *info, char *buf, int size)
return;
}
- memcpy(skb_put(skb, size), buf, size);
+ skb_put_data(skb, buf, size);
skb->protocol = hdlc_type_trans(skb, dev);
diff --git a/drivers/tty/synclink_gt.c b/drivers/tty/synclink_gt.c
index 31885f20fc15..636b8ae29b46 100644
--- a/drivers/tty/synclink_gt.c
+++ b/drivers/tty/synclink_gt.c
@@ -95,7 +95,7 @@ MODULE_LICENSE("GPL");
#define MGSL_MAGIC 0x5401
#define MAX_DEVICES 32
-static struct pci_device_id pci_table[] = {
+static const struct pci_device_id pci_table[] = {
{PCI_VENDOR_ID_MICROGATE, SYNCLINK_GT_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,},
{PCI_VENDOR_ID_MICROGATE, SYNCLINK_GT2_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,},
{PCI_VENDOR_ID_MICROGATE, SYNCLINK_GT4_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,},
@@ -184,7 +184,7 @@ static void hdlcdev_exit(struct slgt_info *info);
struct cond_wait {
struct cond_wait *next;
wait_queue_head_t q;
- wait_queue_t wait;
+ wait_queue_entry_t wait;
unsigned int data;
};
static void init_cond_wait(struct cond_wait *w, unsigned int data);
@@ -1755,7 +1755,7 @@ static void hdlcdev_rx(struct slgt_info *info, char *buf, int size)
return;
}
- memcpy(skb_put(skb, size), buf, size);
+ skb_put_data(skb, buf, size);
skb->protocol = hdlc_type_trans(skb, dev);
diff --git a/drivers/tty/synclinkmp.c b/drivers/tty/synclinkmp.c
index 51e8846cd68f..4fed9e7b281f 100644
--- a/drivers/tty/synclinkmp.c
+++ b/drivers/tty/synclinkmp.c
@@ -479,7 +479,7 @@ static char *driver_version = "$Revision: 4.38 $";
static int synclinkmp_init_one(struct pci_dev *dev,const struct pci_device_id *ent);
static void synclinkmp_remove_one(struct pci_dev *dev);
-static struct pci_device_id synclinkmp_pci_tbl[] = {
+static const struct pci_device_id synclinkmp_pci_tbl[] = {
{ PCI_VENDOR_ID_MICROGATE, PCI_DEVICE_ID_MICROGATE_SCA, PCI_ANY_ID, PCI_ANY_ID, },
{ 0, }, /* terminate list */
};
@@ -1874,7 +1874,7 @@ static void hdlcdev_rx(SLMP_INFO *info, char *buf, int size)
return;
}
- memcpy(skb_put(skb, size), buf, size);
+ skb_put_data(skb, buf, size);
skb->protocol = hdlc_type_trans(skb, dev);
diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c
index 3ffc1ce29023..d008f5a75197 100644
--- a/drivers/tty/sysrq.c
+++ b/drivers/tty/sysrq.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Linux Magic System Request Key Hacks
*
diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c
index 4e7a4e9dcf4d..f8eba1c5412f 100644
--- a/drivers/tty/tty_buffer.c
+++ b/drivers/tty/tty_buffer.c
@@ -362,6 +362,32 @@ int tty_insert_flip_string_flags(struct tty_port *port,
EXPORT_SYMBOL(tty_insert_flip_string_flags);
/**
+ * __tty_insert_flip_char - Add one character to the tty buffer
+ * @port: tty port
+ * @ch: character
+ * @flag: flag byte
+ *
+ * Queue a single byte to the tty buffering, with an optional flag.
+ * This is the slow path of tty_insert_flip_char.
+ */
+int __tty_insert_flip_char(struct tty_port *port, unsigned char ch, char flag)
+{
+ struct tty_buffer *tb;
+ int flags = (flag == TTY_NORMAL) ? TTYB_NORMAL : 0;
+
+ if (!__tty_buffer_request_room(port, 1, flags))
+ return 0;
+
+ tb = port->buf.tail;
+ if (~tb->flags & TTYB_NORMAL)
+ *flag_buf_ptr(tb, tb->used) = flag;
+ *char_buf_ptr(tb, tb->used++) = ch;
+
+ return 1;
+}
+EXPORT_SYMBOL(__tty_insert_flip_char);
+
+/**
* tty_schedule_flip - push characters to ldisc
* @port: tty port to push from
*
diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c
index 0c150b5a9dd6..94cccb6efa32 100644
--- a/drivers/tty/tty_io.c
+++ b/drivers/tty/tty_io.c
@@ -280,7 +280,7 @@ static int check_tty_count(struct tty_struct *tty, const char *routine)
{
#ifdef CHECK_TTY_COUNT
struct list_head *p;
- int count = 0;
+ int count = 0, kopen_count = 0;
spin_lock(&tty->files_lock);
list_for_each(p, &tty->tty_files) {
@@ -291,10 +291,12 @@ static int check_tty_count(struct tty_struct *tty, const char *routine)
tty->driver->subtype == PTY_TYPE_SLAVE &&
tty->link && tty->link->count)
count++;
- if (tty->count != count) {
- tty_warn(tty, "%s: tty->count(%d) != #fd's(%d)\n",
- routine, tty->count, count);
- return count;
+ if (tty_port_kopened(tty->port))
+ kopen_count++;
+ if (tty->count != (count + kopen_count)) {
+ tty_warn(tty, "%s: tty->count(%d) != (#fd's(%d) + #kopen's(%d))\n",
+ routine, tty->count, count, kopen_count);
+ return (count + kopen_count);
}
#endif
return 0;
@@ -325,6 +327,56 @@ static struct tty_driver *get_tty_driver(dev_t device, int *index)
return NULL;
}
+/**
+ * tty_dev_name_to_number - return dev_t for device name
+ * @name: user space name of device under /dev
+ * @number: pointer to dev_t that this function will populate
+ *
+ * This function converts device names like ttyS0 or ttyUSB1 into dev_t
+ * like (4, 64) or (188, 1). If no corresponding driver is registered then
+ * the function returns -ENODEV.
+ *
+ * Locking: this acquires tty_mutex to protect the tty_drivers list from
+ * being modified while we are traversing it, and makes sure to
+ * release it before exiting.
+ */
+int tty_dev_name_to_number(const char *name, dev_t *number)
+{
+ struct tty_driver *p;
+ int ret;
+ int index, prefix_length = 0;
+ const char *str;
+
+ for (str = name; *str && !isdigit(*str); str++)
+ ;
+
+ if (!*str)
+ return -EINVAL;
+
+ ret = kstrtoint(str, 10, &index);
+ if (ret)
+ return ret;
+
+ prefix_length = str - name;
+ mutex_lock(&tty_mutex);
+
+ list_for_each_entry(p, &tty_drivers, tty_drivers)
+ if (prefix_length == strlen(p->name) && strncmp(name,
+ p->name, prefix_length) == 0) {
+ if (index < p->num) {
+ *number = MKDEV(p->major, p->minor_start + index);
+ goto out;
+ }
+ }
+
+ /* if here then driver wasn't found */
+ ret = -ENODEV;
+out:
+ mutex_unlock(&tty_mutex);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(tty_dev_name_to_number);
+
#ifdef CONFIG_CONSOLE_POLL
/**
@@ -412,6 +464,14 @@ static int hung_up_tty_fasync(int fd, struct file *file, int on)
return -ENOTTY;
}
+static void tty_show_fdinfo(struct seq_file *m, struct file *file)
+{
+ struct tty_struct *tty = file_tty(file);
+
+ if (tty && tty->ops && tty->ops->show_fdinfo)
+ tty->ops->show_fdinfo(tty, m);
+}
+
static const struct file_operations tty_fops = {
.llseek = no_llseek,
.read = tty_read,
@@ -422,6 +482,7 @@ static const struct file_operations tty_fops = {
.open = tty_open,
.release = tty_release,
.fasync = tty_fasync,
+ .show_fdinfo = tty_show_fdinfo,
};
static const struct file_operations console_fops = {
@@ -1083,7 +1144,10 @@ static struct tty_struct *tty_driver_lookup_tty(struct tty_driver *driver,
struct tty_struct *tty;
if (driver->ops->lookup)
- tty = driver->ops->lookup(driver, file, idx);
+ if (!file)
+ tty = ERR_PTR(-EIO);
+ else
+ tty = driver->ops->lookup(driver, file, idx);
else
tty = driver->ttys[idx];
@@ -1460,6 +1524,38 @@ static int tty_release_checks(struct tty_struct *tty, int idx)
}
/**
+ * tty_kclose - closes tty opened by tty_kopen
+ * @tty: tty device
+ *
+ * Performs the final steps to release and free a tty device. It is the
+ * same as tty_release_struct except that it also resets TTY_PORT_KOPENED
+ * flag on tty->port.
+ */
+void tty_kclose(struct tty_struct *tty)
+{
+ /*
+ * Ask the line discipline code to release its structures
+ */
+ tty_ldisc_release(tty);
+
+ /* Wait for pending work before tty destruction commmences */
+ tty_flush_works(tty);
+
+ tty_debug_hangup(tty, "freeing structure\n");
+ /*
+ * The release_tty function takes care of the details of clearing
+ * the slots and preserving the termios structure. The tty_unlock_pair
+ * should be safe as we keep a kref while the tty is locked (so the
+ * unlock never unlocks a freed tty).
+ */
+ mutex_lock(&tty_mutex);
+ tty_port_set_kopened(tty->port, 0);
+ release_tty(tty, tty->index);
+ mutex_unlock(&tty_mutex);
+}
+EXPORT_SYMBOL_GPL(tty_kclose);
+
+/**
* tty_release_struct - release a tty struct
* @tty: tty device
* @idx: index of the tty
@@ -1715,7 +1811,7 @@ static struct tty_driver *tty_lookup_driver(dev_t device, struct file *filp,
struct tty_driver *console_driver = console_device(index);
if (console_driver) {
driver = tty_driver_kref_get(console_driver);
- if (driver) {
+ if (driver && filp) {
/* Don't let /dev/console block */
filp->f_flags |= O_NONBLOCK;
break;
@@ -1733,6 +1829,56 @@ static struct tty_driver *tty_lookup_driver(dev_t device, struct file *filp,
}
/**
+ * tty_kopen - open a tty device for kernel
+ * @device: dev_t of device to open
+ *
+ * Opens tty exclusively for kernel. Performs the driver lookup,
+ * makes sure it's not already opened and performs the first-time
+ * tty initialization.
+ *
+ * Returns the locked initialized &tty_struct
+ *
+ * Claims the global tty_mutex to serialize:
+ * - concurrent first-time tty initialization
+ * - concurrent tty driver removal w/ lookup
+ * - concurrent tty removal from driver table
+ */
+struct tty_struct *tty_kopen(dev_t device)
+{
+ struct tty_struct *tty;
+ struct tty_driver *driver = NULL;
+ int index = -1;
+
+ mutex_lock(&tty_mutex);
+ driver = tty_lookup_driver(device, NULL, &index);
+ if (IS_ERR(driver)) {
+ mutex_unlock(&tty_mutex);
+ return ERR_CAST(driver);
+ }
+
+ /* check whether we're reopening an existing tty */
+ tty = tty_driver_lookup_tty(driver, NULL, index);
+ if (IS_ERR(tty))
+ goto out;
+
+ if (tty) {
+ /* drop kref from tty_driver_lookup_tty() */
+ tty_kref_put(tty);
+ tty = ERR_PTR(-EBUSY);
+ } else { /* tty_init_dev returns tty with the tty_lock held */
+ tty = tty_init_dev(driver, index);
+ if (IS_ERR(tty))
+ goto out;
+ tty_port_set_kopened(tty->port, 1);
+ }
+out:
+ mutex_unlock(&tty_mutex);
+ tty_driver_kref_put(driver);
+ return tty;
+}
+EXPORT_SYMBOL_GPL(tty_kopen);
+
+/**
* tty_open_by_driver - open a tty device
* @device: dev_t of device to open
* @inode: inode of device file
@@ -1771,6 +1917,12 @@ static struct tty_struct *tty_open_by_driver(dev_t device, struct inode *inode,
}
if (tty) {
+ if (tty_port_kopened(tty->port)) {
+ tty_kref_put(tty);
+ mutex_unlock(&tty_mutex);
+ tty = ERR_PTR(-EBUSY);
+ goto out;
+ }
mutex_unlock(&tty_mutex);
retval = tty_lock_interruptible(tty);
tty_kref_put(tty); /* drop kref from tty_driver_lookup_tty() */
@@ -2464,6 +2616,9 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
case TIOCSSERIAL:
tty_warn_deprecated_flags(p);
break;
+ case TIOCGPTPEER:
+ /* Special because the struct file is needed */
+ return ptm_open_peer(file, tty, (int)arg);
default:
retval = tty_jobctrl_ioctl(tty, real_tty, file, cmd, arg);
if (retval != -ENOIOCTLCMD)
diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c
index e4603b09863a..84a8ac2a779f 100644
--- a/drivers/tty/tty_ldisc.c
+++ b/drivers/tty/tty_ldisc.c
@@ -492,6 +492,29 @@ static void tty_ldisc_close(struct tty_struct *tty, struct tty_ldisc *ld)
}
/**
+ * tty_ldisc_failto - helper for ldisc failback
+ * @tty: tty to open the ldisc on
+ * @ld: ldisc we are trying to fail back to
+ *
+ * Helper to try and recover a tty when switching back to the old
+ * ldisc fails and we need something attached.
+ */
+
+static int tty_ldisc_failto(struct tty_struct *tty, int ld)
+{
+ struct tty_ldisc *disc = tty_ldisc_get(tty, ld);
+ int r;
+
+ if (IS_ERR(disc))
+ return PTR_ERR(disc);
+ tty->ldisc = disc;
+ tty_set_termios_ldisc(tty, ld);
+ if ((r = tty_ldisc_open(tty, disc)) < 0)
+ tty_ldisc_put(disc);
+ return r;
+}
+
+/**
* tty_ldisc_restore - helper for tty ldisc change
* @tty: tty to recover
* @old: previous ldisc
@@ -502,9 +525,6 @@ static void tty_ldisc_close(struct tty_struct *tty, struct tty_ldisc *ld)
static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old)
{
- struct tty_ldisc *new_ldisc;
- int r;
-
/* There is an outstanding reference here so this is safe */
old = tty_ldisc_get(tty, old->ops->num);
WARN_ON(IS_ERR(old));
@@ -512,17 +532,13 @@ static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old)
tty_set_termios_ldisc(tty, old->ops->num);
if (tty_ldisc_open(tty, old) < 0) {
tty_ldisc_put(old);
- /* This driver is always present */
- new_ldisc = tty_ldisc_get(tty, N_TTY);
- if (IS_ERR(new_ldisc))
- panic("n_tty: get");
- tty->ldisc = new_ldisc;
- tty_set_termios_ldisc(tty, N_TTY);
- r = tty_ldisc_open(tty, new_ldisc);
- if (r < 0)
- panic("Couldn't open N_TTY ldisc for "
- "%s --- error %d.",
- tty_name(tty), r);
+ /* The traditional behaviour is to fall back to N_TTY, we
+ want to avoid falling back to N_NULL unless we have no
+ choice to avoid the risk of breaking anything */
+ if (tty_ldisc_failto(tty, N_TTY) < 0 &&
+ tty_ldisc_failto(tty, N_NULL) < 0)
+ panic("Couldn't open N_NULL ldisc for %s.",
+ tty_name(tty));
}
}
@@ -605,6 +621,7 @@ err:
tty_unlock(tty);
return retval;
}
+EXPORT_SYMBOL_GPL(tty_set_ldisc);
/**
* tty_ldisc_kill - teardown ldisc
@@ -677,10 +694,8 @@ int tty_ldisc_reinit(struct tty_struct *tty, int disc)
tty_set_termios_ldisc(tty, disc);
retval = tty_ldisc_open(tty, tty->ldisc);
if (retval) {
- if (!WARN_ON(disc == N_TTY)) {
- tty_ldisc_put(tty->ldisc);
- tty->ldisc = NULL;
- }
+ tty_ldisc_put(tty->ldisc);
+ tty->ldisc = NULL;
}
return retval;
}
@@ -735,8 +750,9 @@ void tty_ldisc_hangup(struct tty_struct *tty, bool reinit)
if (tty->ldisc) {
if (reinit) {
- if (tty_ldisc_reinit(tty, tty->termios.c_line) < 0)
- tty_ldisc_reinit(tty, N_TTY);
+ if (tty_ldisc_reinit(tty, tty->termios.c_line) < 0 &&
+ tty_ldisc_reinit(tty, N_TTY) < 0)
+ WARN_ON(tty_ldisc_reinit(tty, N_NULL) < 0);
} else
tty_ldisc_kill(tty);
}
@@ -797,6 +813,7 @@ void tty_ldisc_release(struct tty_struct *tty)
tty_ldisc_debug(tty, "released\n");
}
+EXPORT_SYMBOL_GPL(tty_ldisc_release);
/**
* tty_ldisc_init - ldisc setup for new tty
diff --git a/drivers/tty/tty_mutex.c b/drivers/tty/tty_mutex.c
index d8bae67a6174..2640635ee177 100644
--- a/drivers/tty/tty_mutex.c
+++ b/drivers/tty/tty_mutex.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
#include <linux/tty.h>
#include <linux/module.h>
#include <linux/kallsyms.h>
diff --git a/drivers/tty/vcc.c b/drivers/tty/vcc.c
new file mode 100644
index 000000000000..ef01d24858cd
--- /dev/null
+++ b/drivers/tty/vcc.c
@@ -0,0 +1,1155 @@
+/* vcc.c: sun4v virtual channel concentrator
+ *
+ * Copyright (C) 2017 Oracle. All rights reserved.
+ */
+
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <asm/vio.h>
+#include <asm/ldc.h>
+
+#define DRV_MODULE_NAME "vcc"
+#define DRV_MODULE_VERSION "1.1"
+#define DRV_MODULE_RELDATE "July 1, 2017"
+
+static char version[] =
+ DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")";
+
+MODULE_DESCRIPTION("Sun LDOM virtual console concentrator driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_MODULE_VERSION);
+
+struct vcc_port {
+ struct vio_driver_state vio;
+
+ spinlock_t lock;
+ char *domain;
+ struct tty_struct *tty; /* only populated while dev is open */
+ unsigned long index; /* index into the vcc_table */
+
+ u64 refcnt;
+ bool excl_locked;
+
+ bool removed;
+
+ /* This buffer is required to support the tty write_room interface
+ * and guarantee that any characters that the driver accepts will
+ * be eventually sent, either immediately or later.
+ */
+ int chars_in_buffer;
+ struct vio_vcc buffer;
+
+ struct timer_list rx_timer;
+ struct timer_list tx_timer;
+};
+
+/* Microseconds that thread will delay waiting for a vcc port ref */
+#define VCC_REF_DELAY 100
+
+#define VCC_MAX_PORTS 1024
+#define VCC_MINOR_START 0 /* must be zero */
+#define VCC_BUFF_LEN VIO_VCC_MTU_SIZE
+
+#define VCC_CTL_BREAK -1
+#define VCC_CTL_HUP -2
+
+static const char vcc_driver_name[] = "vcc";
+static const char vcc_device_node[] = "vcc";
+static struct tty_driver *vcc_tty_driver;
+
+static struct vcc_port *vcc_table[VCC_MAX_PORTS];
+static DEFINE_SPINLOCK(vcc_table_lock);
+
+int vcc_dbg;
+int vcc_dbg_ldc;
+int vcc_dbg_vio;
+
+module_param(vcc_dbg, uint, 0664);
+module_param(vcc_dbg_ldc, uint, 0664);
+module_param(vcc_dbg_vio, uint, 0664);
+
+#define VCC_DBG_DRV 0x1
+#define VCC_DBG_LDC 0x2
+#define VCC_DBG_PKT 0x4
+
+#define vccdbg(f, a...) \
+ do { \
+ if (vcc_dbg & VCC_DBG_DRV) \
+ pr_info(f, ## a); \
+ } while (0) \
+
+#define vccdbgl(l) \
+ do { \
+ if (vcc_dbg & VCC_DBG_LDC) \
+ ldc_print(l); \
+ } while (0) \
+
+#define vccdbgp(pkt) \
+ do { \
+ if (vcc_dbg & VCC_DBG_PKT) { \
+ int i; \
+ for (i = 0; i < pkt.tag.stype; i++) \
+ pr_info("[%c]", pkt.data[i]); \
+ } \
+ } while (0) \
+
+/* Note: Be careful when adding flags to this line discipline. Don't
+ * add anything that will cause echoing or we'll go into recursive
+ * loop echoing chars back and forth with the console drivers.
+ */
+static const struct ktermios vcc_tty_termios = {
+ .c_iflag = IGNBRK | IGNPAR,
+ .c_oflag = OPOST,
+ .c_cflag = B38400 | CS8 | CREAD | HUPCL,
+ .c_cc = INIT_C_CC,
+ .c_ispeed = 38400,
+ .c_ospeed = 38400
+};
+
+/**
+ * vcc_table_add() - Add VCC port to the VCC table
+ * @port: pointer to the VCC port
+ *
+ * Return: index of the port in the VCC table on success,
+ * -1 on failure
+ */
+static int vcc_table_add(struct vcc_port *port)
+{
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(&vcc_table_lock, flags);
+ for (i = VCC_MINOR_START; i < VCC_MAX_PORTS; i++) {
+ if (!vcc_table[i]) {
+ vcc_table[i] = port;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&vcc_table_lock, flags);
+
+ if (i < VCC_MAX_PORTS)
+ return i;
+ else
+ return -1;
+}
+
+/**
+ * vcc_table_remove() - Removes a VCC port from the VCC table
+ * @index: Index into the VCC table
+ */
+static void vcc_table_remove(unsigned long index)
+{
+ unsigned long flags;
+
+ if (WARN_ON(index >= VCC_MAX_PORTS))
+ return;
+
+ spin_lock_irqsave(&vcc_table_lock, flags);
+ vcc_table[index] = NULL;
+ spin_unlock_irqrestore(&vcc_table_lock, flags);
+}
+
+/**
+ * vcc_get() - Gets a reference to VCC port
+ * @index: Index into the VCC table
+ * @excl: Indicates if an exclusive access is requested
+ *
+ * Return: reference to the VCC port, if found
+ * NULL, if port not found
+ */
+static struct vcc_port *vcc_get(unsigned long index, bool excl)
+{
+ struct vcc_port *port;
+ unsigned long flags;
+
+try_again:
+ spin_lock_irqsave(&vcc_table_lock, flags);
+
+ port = vcc_table[index];
+ if (!port) {
+ spin_unlock_irqrestore(&vcc_table_lock, flags);
+ return NULL;
+ }
+
+ if (!excl) {
+ if (port->excl_locked) {
+ spin_unlock_irqrestore(&vcc_table_lock, flags);
+ udelay(VCC_REF_DELAY);
+ goto try_again;
+ }
+ port->refcnt++;
+ spin_unlock_irqrestore(&vcc_table_lock, flags);
+ return port;
+ }
+
+ if (port->refcnt) {
+ spin_unlock_irqrestore(&vcc_table_lock, flags);
+ /* Threads wanting exclusive access will wait half the time,
+ * probably giving them higher priority in the case of
+ * multiple waiters.
+ */
+ udelay(VCC_REF_DELAY/2);
+ goto try_again;
+ }
+
+ port->refcnt++;
+ port->excl_locked = true;
+ spin_unlock_irqrestore(&vcc_table_lock, flags);
+
+ return port;
+}
+
+/**
+ * vcc_put() - Returns a reference to VCC port
+ * @port: pointer to VCC port
+ * @excl: Indicates if the returned reference is an exclusive reference
+ *
+ * Note: It's the caller's responsibility to ensure the correct value
+ * for the excl flag
+ */
+static void vcc_put(struct vcc_port *port, bool excl)
+{
+ unsigned long flags;
+
+ if (!port)
+ return;
+
+ spin_lock_irqsave(&vcc_table_lock, flags);
+
+ /* check if caller attempted to put with the wrong flags */
+ if (WARN_ON((excl && !port->excl_locked) ||
+ (!excl && port->excl_locked)))
+ goto done;
+
+ port->refcnt--;
+
+ if (excl)
+ port->excl_locked = false;
+
+done:
+ spin_unlock_irqrestore(&vcc_table_lock, flags);
+}
+
+/**
+ * vcc_get_ne() - Get a non-exclusive reference to VCC port
+ * @index: Index into the VCC table
+ *
+ * Gets a non-exclusive reference to VCC port, if it's not removed
+ *
+ * Return: pointer to the VCC port, if found
+ * NULL, if port not found
+ */
+static struct vcc_port *vcc_get_ne(unsigned long index)
+{
+ struct vcc_port *port;
+
+ port = vcc_get(index, false);
+
+ if (port && port->removed) {
+ vcc_put(port, false);
+ return NULL;
+ }
+
+ return port;
+}
+
+static void vcc_kick_rx(struct vcc_port *port)
+{
+ struct vio_driver_state *vio = &port->vio;
+
+ assert_spin_locked(&port->lock);
+
+ if (!timer_pending(&port->rx_timer) && !port->removed) {
+ disable_irq_nosync(vio->vdev->rx_irq);
+ port->rx_timer.expires = (jiffies + 1);
+ add_timer(&port->rx_timer);
+ }
+}
+
+static void vcc_kick_tx(struct vcc_port *port)
+{
+ assert_spin_locked(&port->lock);
+
+ if (!timer_pending(&port->tx_timer) && !port->removed) {
+ port->tx_timer.expires = (jiffies + 1);
+ add_timer(&port->tx_timer);
+ }
+}
+
+static int vcc_rx_check(struct tty_struct *tty, int size)
+{
+ if (WARN_ON(!tty || !tty->port))
+ return 1;
+
+ /* tty_buffer_request_room won't sleep because it uses
+ * GFP_ATOMIC flag to allocate buffer
+ */
+ if (test_bit(TTY_THROTTLED, &tty->flags) ||
+ (tty_buffer_request_room(tty->port, VCC_BUFF_LEN) < VCC_BUFF_LEN))
+ return 0;
+
+ return 1;
+}
+
+static int vcc_rx(struct tty_struct *tty, char *buf, int size)
+{
+ int len = 0;
+
+ if (WARN_ON(!tty || !tty->port))
+ return len;
+
+ len = tty_insert_flip_string(tty->port, buf, size);
+ if (len)
+ tty_flip_buffer_push(tty->port);
+
+ return len;
+}
+
+static int vcc_ldc_read(struct vcc_port *port)
+{
+ struct vio_driver_state *vio = &port->vio;
+ struct tty_struct *tty;
+ struct vio_vcc pkt;
+ int rv = 0;
+
+ tty = port->tty;
+ if (!tty) {
+ rv = ldc_rx_reset(vio->lp);
+ vccdbg("VCC: reset rx q: rv=%d\n", rv);
+ goto done;
+ }
+
+ /* Read as long as LDC has incoming data. */
+ while (1) {
+ if (!vcc_rx_check(tty, VIO_VCC_MTU_SIZE)) {
+ vcc_kick_rx(port);
+ break;
+ }
+
+ vccdbgl(vio->lp);
+
+ rv = ldc_read(vio->lp, &pkt, sizeof(pkt));
+ if (rv <= 0)
+ break;
+
+ vccdbg("VCC: ldc_read()=%d\n", rv);
+ vccdbg("TAG [%02x:%02x:%04x:%08x]\n",
+ pkt.tag.type, pkt.tag.stype,
+ pkt.tag.stype_env, pkt.tag.sid);
+
+ if (pkt.tag.type == VIO_TYPE_DATA) {
+ vccdbgp(pkt);
+ /* vcc_rx_check ensures memory availability */
+ vcc_rx(tty, pkt.data, pkt.tag.stype);
+ } else {
+ pr_err("VCC: unknown msg [%02x:%02x:%04x:%08x]\n",
+ pkt.tag.type, pkt.tag.stype,
+ pkt.tag.stype_env, pkt.tag.sid);
+ rv = -ECONNRESET;
+ break;
+ }
+
+ WARN_ON(rv != LDC_PACKET_SIZE);
+ }
+
+done:
+ return rv;
+}
+
+static void vcc_rx_timer(unsigned long index)
+{
+ struct vio_driver_state *vio;
+ struct vcc_port *port;
+ unsigned long flags;
+ int rv;
+
+ port = vcc_get_ne(index);
+ if (!port)
+ return;
+
+ spin_lock_irqsave(&port->lock, flags);
+ port->rx_timer.expires = 0;
+
+ vio = &port->vio;
+
+ enable_irq(vio->vdev->rx_irq);
+
+ if (!port->tty || port->removed)
+ goto done;
+
+ rv = vcc_ldc_read(port);
+ if (rv == -ECONNRESET)
+ vio_conn_reset(vio);
+
+done:
+ spin_unlock_irqrestore(&port->lock, flags);
+ vcc_put(port, false);
+}
+
+static void vcc_tx_timer(unsigned long index)
+{
+ struct vcc_port *port;
+ struct vio_vcc *pkt;
+ unsigned long flags;
+ int tosend = 0;
+ int rv;
+
+ port = vcc_get_ne(index);
+ if (!port)
+ return;
+
+ spin_lock_irqsave(&port->lock, flags);
+ port->tx_timer.expires = 0;
+
+ if (!port->tty || port->removed)
+ goto done;
+
+ tosend = min(VCC_BUFF_LEN, port->chars_in_buffer);
+ if (!tosend)
+ goto done;
+
+ pkt = &port->buffer;
+ pkt->tag.type = VIO_TYPE_DATA;
+ pkt->tag.stype = tosend;
+ vccdbgl(port->vio.lp);
+
+ rv = ldc_write(port->vio.lp, pkt, (VIO_TAG_SIZE + tosend));
+ WARN_ON(!rv);
+
+ if (rv < 0) {
+ vccdbg("VCC: ldc_write()=%d\n", rv);
+ vcc_kick_tx(port);
+ } else {
+ struct tty_struct *tty = port->tty;
+
+ port->chars_in_buffer = 0;
+ if (tty)
+ tty_wakeup(tty);
+ }
+
+done:
+ spin_unlock_irqrestore(&port->lock, flags);
+ vcc_put(port, false);
+}
+
+/**
+ * vcc_event() - LDC event processing engine
+ * @arg: VCC private data
+ * @event: LDC event
+ *
+ * Handles LDC events for VCC
+ */
+static void vcc_event(void *arg, int event)
+{
+ struct vio_driver_state *vio;
+ struct vcc_port *port;
+ unsigned long flags;
+ int rv;
+
+ port = arg;
+ vio = &port->vio;
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ switch (event) {
+ case LDC_EVENT_RESET:
+ case LDC_EVENT_UP:
+ vio_link_state_change(vio, event);
+ break;
+
+ case LDC_EVENT_DATA_READY:
+ rv = vcc_ldc_read(port);
+ if (rv == -ECONNRESET)
+ vio_conn_reset(vio);
+ break;
+
+ default:
+ pr_err("VCC: unexpected LDC event(%d)\n", event);
+ }
+
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static struct ldc_channel_config vcc_ldc_cfg = {
+ .event = vcc_event,
+ .mtu = VIO_VCC_MTU_SIZE,
+ .mode = LDC_MODE_RAW,
+ .debug = 0,
+};
+
+/* Ordered from largest major to lowest */
+static struct vio_version vcc_versions[] = {
+ { .major = 1, .minor = 0 },
+};
+
+static struct tty_port_operations vcc_port_ops = { 0 };
+
+static ssize_t vcc_sysfs_domain_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct vcc_port *port;
+ int rv;
+
+ port = dev_get_drvdata(dev);
+ if (!port)
+ return -ENODEV;
+
+ rv = scnprintf(buf, PAGE_SIZE, "%s\n", port->domain);
+
+ return rv;
+}
+
+static int vcc_send_ctl(struct vcc_port *port, int ctl)
+{
+ struct vio_vcc pkt;
+ int rv;
+
+ pkt.tag.type = VIO_TYPE_CTRL;
+ pkt.tag.sid = ctl;
+ pkt.tag.stype = 0;
+
+ rv = ldc_write(port->vio.lp, &pkt, sizeof(pkt.tag));
+ WARN_ON(!rv);
+ vccdbg("VCC: ldc_write(%ld)=%d\n", sizeof(pkt.tag), rv);
+
+ return rv;
+}
+
+static ssize_t vcc_sysfs_break_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct vcc_port *port;
+ unsigned long flags;
+ int rv = count;
+ int brk;
+
+ port = dev_get_drvdata(dev);
+ if (!port)
+ return -ENODEV;
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ if (sscanf(buf, "%ud", &brk) != 1 || brk != 1)
+ rv = -EINVAL;
+ else if (vcc_send_ctl(port, VCC_CTL_BREAK) < 0)
+ vcc_kick_tx(port);
+
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ return rv;
+}
+
+static DEVICE_ATTR(domain, 0400, vcc_sysfs_domain_show, NULL);
+static DEVICE_ATTR(break, 0200, NULL, vcc_sysfs_break_store);
+
+static struct attribute *vcc_sysfs_entries[] = {
+ &dev_attr_domain.attr,
+ &dev_attr_break.attr,
+ NULL
+};
+
+static struct attribute_group vcc_attribute_group = {
+ .name = NULL,
+ .attrs = vcc_sysfs_entries,
+};
+
+/**
+ * vcc_probe() - Initialize VCC port
+ * @vdev: Pointer to VIO device of the new VCC port
+ * @id: VIO device ID
+ *
+ * Initializes a VCC port to receive serial console data from
+ * the guest domain. Sets up a TTY end point on the control
+ * domain. Sets up VIO/LDC link between the guest & control
+ * domain endpoints.
+ *
+ * Return: status of the probe
+ */
+static int vcc_probe(struct vio_dev *vdev, const struct vio_device_id *id)
+{
+ struct mdesc_handle *hp;
+ struct vcc_port *port;
+ struct device *dev;
+ const char *domain;
+ char *name;
+ u64 node;
+ int rv;
+
+ vccdbg("VCC: name=%s\n", dev_name(&vdev->dev));
+
+ if (!vcc_tty_driver) {
+ pr_err("VCC: TTY driver not registered\n");
+ return -ENODEV;
+ }
+
+ port = kzalloc(sizeof(struct vcc_port), GFP_KERNEL);
+ if (!port)
+ return -ENOMEM;
+
+ name = kstrdup(dev_name(&vdev->dev), GFP_KERNEL);
+
+ rv = vio_driver_init(&port->vio, vdev, VDEV_CONSOLE_CON, vcc_versions,
+ ARRAY_SIZE(vcc_versions), NULL, name);
+ if (rv)
+ goto free_port;
+
+ port->vio.debug = vcc_dbg_vio;
+ vcc_ldc_cfg.debug = vcc_dbg_ldc;
+
+ rv = vio_ldc_alloc(&port->vio, &vcc_ldc_cfg, port);
+ if (rv)
+ goto free_port;
+
+ spin_lock_init(&port->lock);
+
+ port->index = vcc_table_add(port);
+ if (port->index == -1) {
+ pr_err("VCC: no more TTY indices left for allocation\n");
+ goto free_ldc;
+ }
+
+ /* Register the device using VCC table index as TTY index */
+ dev = tty_register_device(vcc_tty_driver, port->index, &vdev->dev);
+ if (IS_ERR(dev)) {
+ rv = PTR_ERR(dev);
+ goto free_table;
+ }
+
+ hp = mdesc_grab();
+
+ node = vio_vdev_node(hp, vdev);
+ if (node == MDESC_NODE_NULL) {
+ rv = -ENXIO;
+ mdesc_release(hp);
+ goto unreg_tty;
+ }
+
+ domain = mdesc_get_property(hp, node, "vcc-domain-name", NULL);
+ if (!domain) {
+ rv = -ENXIO;
+ mdesc_release(hp);
+ goto unreg_tty;
+ }
+ port->domain = kstrdup(domain, GFP_KERNEL);
+
+ mdesc_release(hp);
+
+ rv = sysfs_create_group(&vdev->dev.kobj, &vcc_attribute_group);
+ if (rv)
+ goto free_domain;
+
+ init_timer(&port->rx_timer);
+ port->rx_timer.function = vcc_rx_timer;
+ port->rx_timer.data = port->index;
+
+ init_timer(&port->tx_timer);
+ port->tx_timer.function = vcc_tx_timer;
+ port->tx_timer.data = port->index;
+
+ dev_set_drvdata(&vdev->dev, port);
+
+ /* It's possible to receive IRQs in the middle of vio_port_up. Disable
+ * IRQs until the port is up.
+ */
+ disable_irq_nosync(vdev->rx_irq);
+ vio_port_up(&port->vio);
+ enable_irq(vdev->rx_irq);
+
+ return 0;
+
+free_domain:
+ kfree(port->domain);
+unreg_tty:
+ tty_unregister_device(vcc_tty_driver, port->index);
+free_table:
+ vcc_table_remove(port->index);
+free_ldc:
+ vio_ldc_free(&port->vio);
+free_port:
+ kfree(name);
+ kfree(port);
+
+ return rv;
+}
+
+/**
+ * vcc_remove() - Terminate a VCC port
+ * @vdev: Pointer to VIO device of the VCC port
+ *
+ * Terminates a VCC port. Sets up the teardown of TTY and
+ * VIO/LDC link between guest and primary domains.
+ *
+ * Return: status of removal
+ */
+static int vcc_remove(struct vio_dev *vdev)
+{
+ struct vcc_port *port = dev_get_drvdata(&vdev->dev);
+
+ if (!port)
+ return -ENODEV;
+
+ del_timer_sync(&port->rx_timer);
+ del_timer_sync(&port->tx_timer);
+
+ /* If there's a process with the device open, do a synchronous
+ * hangup of the TTY. This *may* cause the process to call close
+ * asynchronously, but it's not guaranteed.
+ */
+ if (port->tty)
+ tty_vhangup(port->tty);
+
+ /* Get exclusive reference to VCC, ensures that there are no other
+ * clients to this port
+ */
+ port = vcc_get(port->index, true);
+
+ if (WARN_ON(!port))
+ return -ENODEV;
+
+ tty_unregister_device(vcc_tty_driver, port->index);
+
+ del_timer_sync(&port->vio.timer);
+ vio_ldc_free(&port->vio);
+ sysfs_remove_group(&vdev->dev.kobj, &vcc_attribute_group);
+ dev_set_drvdata(&vdev->dev, NULL);
+ if (port->tty) {
+ port->removed = true;
+ vcc_put(port, true);
+ } else {
+ vcc_table_remove(port->index);
+
+ kfree(port->vio.name);
+ kfree(port->domain);
+ kfree(port);
+ }
+
+ return 0;
+}
+
+static const struct vio_device_id vcc_match[] = {
+ {
+ .type = "vcc-port",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(vio, vcc_match);
+
+static struct vio_driver vcc_driver = {
+ .id_table = vcc_match,
+ .probe = vcc_probe,
+ .remove = vcc_remove,
+ .name = "vcc",
+};
+
+static int vcc_open(struct tty_struct *tty, struct file *vcc_file)
+{
+ struct vcc_port *port;
+
+ if (unlikely(!tty)) {
+ pr_err("VCC: open: Invalid TTY handle\n");
+ return -ENXIO;
+ }
+
+ if (tty->count > 1)
+ return -EBUSY;
+
+ port = vcc_get_ne(tty->index);
+ if (unlikely(!port)) {
+ pr_err("VCC: open: Failed to find VCC port\n");
+ return -ENODEV;
+ }
+
+ if (unlikely(!port->vio.lp)) {
+ pr_err("VCC: open: LDC channel not configured\n");
+ vcc_put(port, false);
+ return -EPIPE;
+ }
+ vccdbgl(port->vio.lp);
+
+ vcc_put(port, false);
+
+ if (unlikely(!tty->port)) {
+ pr_err("VCC: open: TTY port not found\n");
+ return -ENXIO;
+ }
+
+ if (unlikely(!tty->port->ops)) {
+ pr_err("VCC: open: TTY ops not defined\n");
+ return -ENXIO;
+ }
+
+ return tty_port_open(tty->port, tty, vcc_file);
+}
+
+static void vcc_close(struct tty_struct *tty, struct file *vcc_file)
+{
+ if (unlikely(!tty)) {
+ pr_err("VCC: close: Invalid TTY handle\n");
+ return;
+ }
+
+ if (unlikely(tty->count > 1))
+ return;
+
+ if (unlikely(!tty->port)) {
+ pr_err("VCC: close: TTY port not found\n");
+ return;
+ }
+
+ tty_port_close(tty->port, tty, vcc_file);
+}
+
+static void vcc_ldc_hup(struct vcc_port *port)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ if (vcc_send_ctl(port, VCC_CTL_HUP) < 0)
+ vcc_kick_tx(port);
+
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void vcc_hangup(struct tty_struct *tty)
+{
+ struct vcc_port *port;
+
+ if (unlikely(!tty)) {
+ pr_err("VCC: hangup: Invalid TTY handle\n");
+ return;
+ }
+
+ port = vcc_get_ne(tty->index);
+ if (unlikely(!port)) {
+ pr_err("VCC: hangup: Failed to find VCC port\n");
+ return;
+ }
+
+ if (unlikely(!tty->port)) {
+ pr_err("VCC: hangup: TTY port not found\n");
+ vcc_put(port, false);
+ return;
+ }
+
+ vcc_ldc_hup(port);
+
+ vcc_put(port, false);
+
+ tty_port_hangup(tty->port);
+}
+
+static int vcc_write(struct tty_struct *tty, const unsigned char *buf,
+ int count)
+{
+ struct vcc_port *port;
+ struct vio_vcc *pkt;
+ unsigned long flags;
+ int total_sent = 0;
+ int tosend = 0;
+ int rv = -EINVAL;
+
+ if (unlikely(!tty)) {
+ pr_err("VCC: write: Invalid TTY handle\n");
+ return -ENXIO;
+ }
+
+ port = vcc_get_ne(tty->index);
+ if (unlikely(!port)) {
+ pr_err("VCC: write: Failed to find VCC port");
+ return -ENODEV;
+ }
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ pkt = &port->buffer;
+ pkt->tag.type = VIO_TYPE_DATA;
+
+ while (count > 0) {
+ /* Minimum of data to write and space available */
+ tosend = min(count, (VCC_BUFF_LEN - port->chars_in_buffer));
+
+ if (!tosend)
+ break;
+
+ memcpy(&pkt->data[port->chars_in_buffer], &buf[total_sent],
+ tosend);
+ port->chars_in_buffer += tosend;
+ pkt->tag.stype = tosend;
+
+ vccdbg("TAG [%02x:%02x:%04x:%08x]\n", pkt->tag.type,
+ pkt->tag.stype, pkt->tag.stype_env, pkt->tag.sid);
+ vccdbg("DATA [%s]\n", pkt->data);
+ vccdbgl(port->vio.lp);
+
+ /* Since we know we have enough room in VCC buffer for tosend
+ * we record that it was sent regardless of whether the
+ * hypervisor actually took it because we have it buffered.
+ */
+ rv = ldc_write(port->vio.lp, pkt, (VIO_TAG_SIZE + tosend));
+ vccdbg("VCC: write: ldc_write(%d)=%d\n",
+ (VIO_TAG_SIZE + tosend), rv);
+
+ total_sent += tosend;
+ count -= tosend;
+ if (rv < 0) {
+ vcc_kick_tx(port);
+ break;
+ }
+
+ port->chars_in_buffer = 0;
+ }
+
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ vcc_put(port, false);
+
+ vccdbg("VCC: write: total=%d rv=%d", total_sent, rv);
+
+ return total_sent ? total_sent : rv;
+}
+
+static int vcc_write_room(struct tty_struct *tty)
+{
+ struct vcc_port *port;
+ u64 num;
+
+ if (unlikely(!tty)) {
+ pr_err("VCC: write_room: Invalid TTY handle\n");
+ return -ENXIO;
+ }
+
+ port = vcc_get_ne(tty->index);
+ if (unlikely(!port)) {
+ pr_err("VCC: write_room: Failed to find VCC port\n");
+ return -ENODEV;
+ }
+
+ num = VCC_BUFF_LEN - port->chars_in_buffer;
+
+ vcc_put(port, false);
+
+ return num;
+}
+
+static int vcc_chars_in_buffer(struct tty_struct *tty)
+{
+ struct vcc_port *port;
+ u64 num;
+
+ if (unlikely(!tty)) {
+ pr_err("VCC: chars_in_buffer: Invalid TTY handle\n");
+ return -ENXIO;
+ }
+
+ port = vcc_get_ne(tty->index);
+ if (unlikely(!port)) {
+ pr_err("VCC: chars_in_buffer: Failed to find VCC port\n");
+ return -ENODEV;
+ }
+
+ num = port->chars_in_buffer;
+
+ vcc_put(port, false);
+
+ return num;
+}
+
+static int vcc_break_ctl(struct tty_struct *tty, int state)
+{
+ struct vcc_port *port;
+ unsigned long flags;
+
+ if (unlikely(!tty)) {
+ pr_err("VCC: break_ctl: Invalid TTY handle\n");
+ return -ENXIO;
+ }
+
+ port = vcc_get_ne(tty->index);
+ if (unlikely(!port)) {
+ pr_err("VCC: break_ctl: Failed to find VCC port\n");
+ return -ENODEV;
+ }
+
+ /* Turn off break */
+ if (state == 0) {
+ vcc_put(port, false);
+ return 0;
+ }
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ if (vcc_send_ctl(port, VCC_CTL_BREAK) < 0)
+ vcc_kick_tx(port);
+
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ vcc_put(port, false);
+
+ return 0;
+}
+
+static int vcc_install(struct tty_driver *driver, struct tty_struct *tty)
+{
+ struct vcc_port *port_vcc;
+ struct tty_port *port_tty;
+ int ret;
+
+ if (unlikely(!tty)) {
+ pr_err("VCC: install: Invalid TTY handle\n");
+ return -ENXIO;
+ }
+
+ if (tty->index >= VCC_MAX_PORTS)
+ return -EINVAL;
+
+ ret = tty_standard_install(driver, tty);
+ if (ret)
+ return ret;
+
+ port_tty = kzalloc(sizeof(struct tty_port), GFP_KERNEL);
+ if (!port_tty)
+ return -ENOMEM;
+
+ port_vcc = vcc_get(tty->index, true);
+ if (!port_vcc) {
+ pr_err("VCC: install: Failed to find VCC port\n");
+ tty->port = NULL;
+ kfree(port_tty);
+ return -ENODEV;
+ }
+
+ tty_port_init(port_tty);
+ port_tty->ops = &vcc_port_ops;
+ tty->port = port_tty;
+
+ port_vcc->tty = tty;
+
+ vcc_put(port_vcc, true);
+
+ return 0;
+}
+
+static void vcc_cleanup(struct tty_struct *tty)
+{
+ struct vcc_port *port;
+
+ if (unlikely(!tty)) {
+ pr_err("VCC: cleanup: Invalid TTY handle\n");
+ return;
+ }
+
+ port = vcc_get(tty->index, true);
+ if (port) {
+ port->tty = NULL;
+
+ if (port->removed) {
+ vcc_table_remove(tty->index);
+ kfree(port->vio.name);
+ kfree(port->domain);
+ kfree(port);
+ } else {
+ vcc_put(port, true);
+ }
+ }
+
+ tty_port_destroy(tty->port);
+ kfree(tty->port);
+ tty->port = NULL;
+}
+
+static const struct tty_operations vcc_ops = {
+ .open = vcc_open,
+ .close = vcc_close,
+ .hangup = vcc_hangup,
+ .write = vcc_write,
+ .write_room = vcc_write_room,
+ .chars_in_buffer = vcc_chars_in_buffer,
+ .break_ctl = vcc_break_ctl,
+ .install = vcc_install,
+ .cleanup = vcc_cleanup,
+};
+
+#define VCC_TTY_FLAGS (TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_REAL_RAW)
+
+static int vcc_tty_init(void)
+{
+ int rv;
+
+ pr_info("VCC: %s\n", version);
+
+ vcc_tty_driver = tty_alloc_driver(VCC_MAX_PORTS, VCC_TTY_FLAGS);
+ if (IS_ERR(vcc_tty_driver)) {
+ pr_err("VCC: TTY driver alloc failed\n");
+ return PTR_ERR(vcc_tty_driver);
+ }
+
+ vcc_tty_driver->driver_name = vcc_driver_name;
+ vcc_tty_driver->name = vcc_device_node;
+
+ vcc_tty_driver->minor_start = VCC_MINOR_START;
+ vcc_tty_driver->type = TTY_DRIVER_TYPE_SYSTEM;
+ vcc_tty_driver->init_termios = vcc_tty_termios;
+
+ tty_set_operations(vcc_tty_driver, &vcc_ops);
+
+ rv = tty_register_driver(vcc_tty_driver);
+ if (rv) {
+ pr_err("VCC: TTY driver registration failed\n");
+ put_tty_driver(vcc_tty_driver);
+ vcc_tty_driver = NULL;
+ return rv;
+ }
+
+ vccdbg("VCC: TTY driver registered\n");
+
+ return 0;
+}
+
+static void vcc_tty_exit(void)
+{
+ tty_unregister_driver(vcc_tty_driver);
+ put_tty_driver(vcc_tty_driver);
+ vccdbg("VCC: TTY driver unregistered\n");
+
+ vcc_tty_driver = NULL;
+}
+
+static int __init vcc_init(void)
+{
+ int rv;
+
+ rv = vcc_tty_init();
+ if (rv) {
+ pr_err("VCC: TTY init failed\n");
+ return rv;
+ }
+
+ rv = vio_register_driver(&vcc_driver);
+ if (rv) {
+ pr_err("VCC: VIO driver registration failed\n");
+ vcc_tty_exit();
+ } else {
+ vccdbg("VCC: VIO driver registered successfully\n");
+ }
+
+ return rv;
+}
+
+static void __exit vcc_exit(void)
+{
+ vio_unregister_driver(&vcc_driver);
+ vccdbg("VCC: VIO driver unregistered\n");
+ vcc_tty_exit();
+ vccdbg("VCC: TTY driver unregistered\n");
+}
+
+module_init(vcc_init);
+module_exit(vcc_exit);
diff --git a/drivers/tty/vt/Makefile b/drivers/tty/vt/Makefile
index 17ae94cb29f8..edbbe0ccdb83 100644
--- a/drivers/tty/vt/Makefile
+++ b/drivers/tty/vt/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
#
# This file contains the font map for the default (hardware) font
#
diff --git a/drivers/tty/vt/consolemap.c b/drivers/tty/vt/consolemap.c
index 1f6e17fc3fb0..a5f88cf0f61d 100644
--- a/drivers/tty/vt/consolemap.c
+++ b/drivers/tty/vt/consolemap.c
@@ -322,15 +322,13 @@ int con_set_trans_old(unsigned char __user * arg)
{
int i;
unsigned short inbuf[E_TABSZ];
+ unsigned char ubuf[E_TABSZ];
- if (!access_ok(VERIFY_READ, arg, E_TABSZ))
+ if (copy_from_user(ubuf, arg, E_TABSZ))
return -EFAULT;
- for (i = 0; i < E_TABSZ ; i++) {
- unsigned char uc;
- __get_user(uc, arg+i);
- inbuf[i] = UNI_DIRECT_BASE | uc;
- }
+ for (i = 0; i < E_TABSZ ; i++)
+ inbuf[i] = UNI_DIRECT_BASE | ubuf[i];
console_lock();
memcpy(translations[USER_MAP], inbuf, sizeof(inbuf));
@@ -345,9 +343,6 @@ int con_get_trans_old(unsigned char __user * arg)
unsigned short *p = translations[USER_MAP];
unsigned char outbuf[E_TABSZ];
- if (!access_ok(VERIFY_WRITE, arg, E_TABSZ))
- return -EFAULT;
-
console_lock();
for (i = 0; i < E_TABSZ ; i++)
{
@@ -356,22 +351,16 @@ int con_get_trans_old(unsigned char __user * arg)
}
console_unlock();
- for (i = 0; i < E_TABSZ ; i++)
- __put_user(outbuf[i], arg+i);
- return 0;
+ return copy_to_user(arg, outbuf, sizeof(outbuf)) ? -EFAULT : 0;
}
int con_set_trans_new(ushort __user * arg)
{
- int i;
unsigned short inbuf[E_TABSZ];
- if (!access_ok(VERIFY_READ, arg, E_TABSZ*sizeof(unsigned short)))
+ if (copy_from_user(inbuf, arg, sizeof(inbuf)))
return -EFAULT;
- for (i = 0; i < E_TABSZ ; i++)
- __get_user(inbuf[i], arg+i);
-
console_lock();
memcpy(translations[USER_MAP], inbuf, sizeof(inbuf));
update_user_maps();
@@ -381,19 +370,13 @@ int con_set_trans_new(ushort __user * arg)
int con_get_trans_new(ushort __user * arg)
{
- int i;
unsigned short outbuf[E_TABSZ];
- if (!access_ok(VERIFY_WRITE, arg, E_TABSZ*sizeof(unsigned short)))
- return -EFAULT;
-
console_lock();
memcpy(outbuf, translations[USER_MAP], sizeof(outbuf));
console_unlock();
- for (i = 0; i < E_TABSZ ; i++)
- __put_user(outbuf[i], arg+i);
- return 0;
+ return copy_to_user(arg, outbuf, sizeof(outbuf)) ? -EFAULT : 0;
}
/*
@@ -557,14 +540,9 @@ int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair __user *list)
if (!ct)
return 0;
- unilist = kmalloc_array(ct, sizeof(struct unipair), GFP_KERNEL);
- if (!unilist)
- return -ENOMEM;
-
- for (i = ct, plist = unilist; i; i--, plist++, list++) {
- __get_user(plist->unicode, &list->unicode);
- __get_user(plist->fontpos, &list->fontpos);
- }
+ unilist = memdup_user(list, ct * sizeof(struct unipair));
+ if (IS_ERR(unilist))
+ return PTR_ERR(unilist);
console_lock();
@@ -757,11 +735,11 @@ EXPORT_SYMBOL(con_copy_unimap);
*/
int con_get_unimap(struct vc_data *vc, ushort ct, ushort __user *uct, struct unipair __user *list)
{
- int i, j, k;
+ int i, j, k, ret = 0;
ushort ect;
u16 **p1, *p2;
struct uni_pagedir *p;
- struct unipair *unilist, *plist;
+ struct unipair *unilist;
unilist = kmalloc_array(ct, sizeof(struct unipair), GFP_KERNEL);
if (!unilist)
@@ -792,13 +770,11 @@ int con_get_unimap(struct vc_data *vc, ushort ct, ushort __user *uct, struct uni
}
}
console_unlock();
- for (i = min(ect, ct), plist = unilist; i; i--, list++, plist++) {
- __put_user(plist->unicode, &list->unicode);
- __put_user(plist->fontpos, &list->fontpos);
- }
- __put_user(ect, uct);
+ if (copy_to_user(list, unilist, min(ect, ct) * sizeof(struct unipair)))
+ ret = -EFAULT;
+ put_user(ect, uct);
kfree(unilist);
- return ((ect <= ct) ? 0 : -ENOMEM);
+ return ret ? ret : (ect <= ct) ? 0 : -ENOMEM;
}
/*
diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c
index 8af8d9542663..f4166263bb3a 100644
--- a/drivers/tty/vt/keyboard.c
+++ b/drivers/tty/vt/keyboard.c
@@ -1203,8 +1203,7 @@ DECLARE_TASKLET_DISABLED(keyboard_tasklet, kbd_bh, 0);
#if defined(CONFIG_X86) || defined(CONFIG_IA64) || defined(CONFIG_ALPHA) ||\
defined(CONFIG_MIPS) || defined(CONFIG_PPC) || defined(CONFIG_SPARC) ||\
defined(CONFIG_PARISC) || defined(CONFIG_SUPERH) ||\
- (defined(CONFIG_ARM) && defined(CONFIG_KEYBOARD_ATKBD) && !defined(CONFIG_ARCH_RPC)) ||\
- defined(CONFIG_AVR32)
+ (defined(CONFIG_ARM) && defined(CONFIG_KEYBOARD_ATKBD) && !defined(CONFIG_ARCH_RPC))
#define HW_RAW(dev) (test_bit(EV_MSC, dev->evbit) && test_bit(MSC_RAW, dev->mscbit) &&\
((dev)->id.bustype == BUS_I8042) && ((dev)->id.vendor == 0x0001) && ((dev)->id.product == 0x0001))
diff --git a/drivers/tty/vt/selection.c b/drivers/tty/vt/selection.c
index accbd1257bc4..7a4c8022c023 100644
--- a/drivers/tty/vt/selection.c
+++ b/drivers/tty/vt/selection.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* This module exports the functions:
*
diff --git a/drivers/tty/vt/vc_screen.c b/drivers/tty/vt/vc_screen.c
index 56dcff6059d3..85b6634f518a 100644
--- a/drivers/tty/vt/vc_screen.c
+++ b/drivers/tty/vt/vc_screen.c
@@ -1,3 +1,4 @@
+// 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)
diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index 9c9945284bcf..2ebaba16f785 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -425,7 +425,7 @@ static u8 build_attr(struct vc_data *vc, u8 _color, u8 _intensity, u8 _blink,
else if (_underline)
a = (a & 0xf0) | vc->vc_ulcolor;
else if (_intensity == 0)
- a = (a & 0xf0) | vc->vc_ulcolor;
+ a = (a & 0xf0) | vc->vc_halfcolor;
if (_reverse)
a = ((a) & 0x88) | ((((a) >> 4) | ((a) << 4)) & 0x77);
if (_blink)
@@ -2709,13 +2709,13 @@ int tioclinux(struct tty_struct *tty, unsigned long arg)
* related to the kernel should not use this.
*/
data = vt_get_shift_state();
- ret = __put_user(data, p);
+ ret = put_user(data, p);
break;
case TIOCL_GETMOUSEREPORTING:
console_lock(); /* May be overkill */
data = mouse_reporting();
console_unlock();
- ret = __put_user(data, p);
+ ret = put_user(data, p);
break;
case TIOCL_SETVESABLANK:
console_lock();
@@ -2724,7 +2724,7 @@ int tioclinux(struct tty_struct *tty, unsigned long arg)
break;
case TIOCL_GETKMSGREDIRECT:
data = vt_get_kmsg_redirect();
- ret = __put_user(data, p);
+ ret = put_user(data, p);
break;
case TIOCL_SETKMSGREDIRECT:
if (!capable(CAP_SYS_ADMIN)) {
diff --git a/drivers/tty/vt/vt_ioctl.c b/drivers/tty/vt/vt_ioctl.c
index 0cbfe1ff6f6c..2d2b420598b2 100644
--- a/drivers/tty/vt/vt_ioctl.c
+++ b/drivers/tty/vt/vt_ioctl.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 1992 obz under the linux copyright
*
@@ -266,10 +267,6 @@ do_unimap_ioctl(int cmd, struct unimapdesc __user *user_ud, int perm, struct vc_
if (copy_from_user(&tmp, user_ud, sizeof tmp))
return -EFAULT;
- if (tmp.entries)
- if (!access_ok(VERIFY_WRITE, tmp.entries,
- tmp.entry_ct*sizeof(struct unipair)))
- return -EFAULT;
switch (cmd) {
case PIO_UNIMAP:
if (!perm)
@@ -1170,10 +1167,6 @@ compat_unimap_ioctl(unsigned int cmd, struct compat_unimapdesc __user *user_ud,
if (copy_from_user(&tmp, user_ud, sizeof tmp))
return -EFAULT;
tmp_entries = compat_ptr(tmp.entries);
- if (tmp_entries)
- if (!access_ok(VERIFY_WRITE, tmp_entries,
- tmp.entry_ct*sizeof(struct unipair)))
- return -EFAULT;
switch (cmd) {
case PIO_UNIMAP:
if (!perm)