aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/mips/alchemy/common/platform.c10
-rw-r--r--drivers/tty/serial/8250/8250.h10
-rw-r--r--drivers/tty/serial/8250/8250_aspeed_vuart.c3
-rw-r--r--drivers/tty/serial/8250/8250_bcm7271.c4
-rw-r--r--drivers/tty/serial/8250/8250_core.c91
-rw-r--r--drivers/tty/serial/8250/8250_early.c21
-rw-r--r--drivers/tty/serial/8250/8250_em.c4
-rw-r--r--drivers/tty/serial/8250/8250_exar.c4
-rw-r--r--drivers/tty/serial/8250/8250_fsl.c14
-rw-r--r--drivers/tty/serial/8250/8250_mtk.c9
-rw-r--r--drivers/tty/serial/8250/8250_of.c4
-rw-r--r--drivers/tty/serial/8250/8250_omap.c195
-rw-r--r--drivers/tty/serial/8250/8250_port.c156
-rw-r--r--drivers/tty/serial/8250/8250_pxa.c2
-rw-r--r--drivers/tty/serial/8250/8250_rt288x.c136
-rw-r--r--drivers/tty/serial/8250/8250_uniphier.c4
-rw-r--r--drivers/tty/serial/8250/Kconfig10
-rw-r--r--drivers/tty/serial/8250/Makefile1
-rw-r--r--drivers/tty/serial/Makefile5
-rw-r--r--drivers/tty/serial/amba-pl011.c7
-rw-r--r--drivers/tty/serial/atmel_serial.c5
-rw-r--r--drivers/tty/serial/clps711x.c4
-rw-r--r--drivers/tty/serial/cpm_uart/cpm_uart_core.c5
-rw-r--r--drivers/tty/serial/fsl_lpuart.c56
-rw-r--r--drivers/tty/serial/imx.c4
-rw-r--r--drivers/tty/serial/lantiq.c4
-rw-r--r--drivers/tty/serial/max310x.c2
-rw-r--r--drivers/tty/serial/sc16is7xx.c2
-rw-r--r--drivers/tty/serial/serial_base.h46
-rw-r--r--drivers/tty/serial/serial_base_bus.c209
-rw-r--r--drivers/tty/serial/serial_core.c210
-rw-r--r--drivers/tty/serial/serial_ctrl.c68
-rw-r--r--drivers/tty/serial/serial_port.c105
-rw-r--r--drivers/tty/serial/st-asc.c4
-rw-r--r--drivers/tty/serial/stm32-usart.c5
-rw-r--r--drivers/tty/serial/uartlite.c12
-rw-r--r--drivers/tty/serial/xilinx_uartps.c5
-rw-r--r--include/linux/serial_8250.h44
-rw-r--r--include/linux/serial_core.h7
39 files changed, 1198 insertions, 289 deletions
diff --git a/arch/mips/alchemy/common/platform.c b/arch/mips/alchemy/common/platform.c
index b8f3397c59c9..d4ab34b3b404 100644
--- a/arch/mips/alchemy/common/platform.c
+++ b/arch/mips/alchemy/common/platform.c
@@ -51,9 +51,9 @@ static void alchemy_8250_pm(struct uart_port *port, unsigned int state,
#define PORT(_base, _irq) \
{ \
.mapbase = _base, \
+ .mapsize = 0x1000, \
.irq = _irq, \
.regshift = 2, \
- .iotype = UPIO_AU, \
.flags = UPF_SKIP_TEST | UPF_IOREMAP | \
UPF_FIXED_TYPE, \
.type = PORT_16550A, \
@@ -124,8 +124,14 @@ static void __init alchemy_setup_uarts(int ctype)
au1xx0_uart_device.dev.platform_data = ports;
/* Fill up uartclk. */
- for (s = 0; s < c; s++)
+ for (s = 0; s < c; s++) {
ports[s].uartclk = uartclk;
+ if (au_platform_setup(&ports[s]) < 0) {
+ kfree(ports);
+ printk(KERN_INFO "Alchemy: missing support for UARTs\n");
+ return;
+ }
+ }
if (platform_device_register(&au1xx0_uart_device))
printk(KERN_INFO "Alchemy: failed to register UARTs\n");
}
diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h
index 1e8fe44a7099..471c6bc5f78f 100644
--- a/drivers/tty/serial/8250/8250.h
+++ b/drivers/tty/serial/8250/8250.h
@@ -167,18 +167,21 @@ static unsigned int __maybe_unused serial_icr_read(struct uart_8250_port *up,
void serial8250_clear_and_reinit_fifos(struct uart_8250_port *p);
-static inline int serial_dl_read(struct uart_8250_port *up)
+static inline u32 serial_dl_read(struct uart_8250_port *up)
{
return up->dl_read(up);
}
-static inline void serial_dl_write(struct uart_8250_port *up, int value)
+static inline void serial_dl_write(struct uart_8250_port *up, u32 value)
{
up->dl_write(up, value);
}
static inline bool serial8250_set_THRI(struct uart_8250_port *up)
{
+ /* Port locked to synchronize UART_IER access against the console. */
+ lockdep_assert_held_once(&up->port.lock);
+
if (up->ier & UART_IER_THRI)
return false;
up->ier |= UART_IER_THRI;
@@ -188,6 +191,9 @@ static inline bool serial8250_set_THRI(struct uart_8250_port *up)
static inline bool serial8250_clear_THRI(struct uart_8250_port *up)
{
+ /* Port locked to synchronize UART_IER access against the console. */
+ lockdep_assert_held_once(&up->port.lock);
+
if (!(up->ier & UART_IER_THRI))
return false;
up->ier &= ~UART_IER_THRI;
diff --git a/drivers/tty/serial/8250/8250_aspeed_vuart.c b/drivers/tty/serial/8250/8250_aspeed_vuart.c
index 9d2a7856784f..4a9e71b2dbbc 100644
--- a/drivers/tty/serial/8250/8250_aspeed_vuart.c
+++ b/drivers/tty/serial/8250/8250_aspeed_vuart.c
@@ -275,6 +275,9 @@ static void __aspeed_vuart_set_throttle(struct uart_8250_port *up,
{
unsigned char irqs = UART_IER_RLSI | UART_IER_RDI;
+ /* Port locked to synchronize UART_IER access against the console. */
+ lockdep_assert_held_once(&up->port.lock);
+
up->ier &= ~irqs;
if (!throttle)
up->ier |= irqs;
diff --git a/drivers/tty/serial/8250/8250_bcm7271.c b/drivers/tty/serial/8250/8250_bcm7271.c
index af0e1c070187..d4b05d7ad9e8 100644
--- a/drivers/tty/serial/8250/8250_bcm7271.c
+++ b/drivers/tty/serial/8250/8250_bcm7271.c
@@ -605,9 +605,13 @@ static int brcmuart_startup(struct uart_port *port)
/*
* Disable the Receive Data Interrupt because the DMA engine
* will handle this.
+ *
+ * Synchronize UART_IER access against the console.
*/
+ spin_lock_irq(&port->lock);
up->ier &= ~UART_IER_RDI;
serial_port_out(port, UART_IER, up->ier);
+ spin_unlock_irq(&port->lock);
priv->tx_running = false;
priv->dma.rx_dma = NULL;
diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
index 13bf535eedcd..914e0e6251bf 100644
--- a/drivers/tty/serial/8250/8250_core.c
+++ b/drivers/tty/serial/8250/8250_core.c
@@ -488,6 +488,34 @@ static inline void serial8250_apply_quirks(struct uart_8250_port *up)
up->port.quirks |= skip_txen_test ? UPQ_NO_TXEN_TEST : 0;
}
+static struct uart_8250_port *serial8250_setup_port(int index)
+{
+ struct uart_8250_port *up;
+
+ if (index >= UART_NR)
+ return NULL;
+
+ up = &serial8250_ports[index];
+ up->port.line = index;
+
+ serial8250_init_port(up);
+ if (!base_ops)
+ base_ops = up->port.ops;
+ up->port.ops = &univ8250_port_ops;
+
+ timer_setup(&up->timer, serial8250_timeout, 0);
+
+ up->ops = &univ8250_driver_ops;
+
+ if (IS_ENABLED(CONFIG_ALPHA_JENSEN) ||
+ (IS_ENABLED(CONFIG_ALPHA_GENERIC) && alpha_jensen()))
+ up->port.set_mctrl = alpha_jensen_set_mctrl;
+
+ serial8250_set_defaults(up);
+
+ return up;
+}
+
static void __init serial8250_isa_init_ports(void)
{
struct uart_8250_port *up;
@@ -501,26 +529,13 @@ static void __init serial8250_isa_init_ports(void)
if (nr_uarts > UART_NR)
nr_uarts = UART_NR;
- for (i = 0; i < nr_uarts; i++) {
- struct uart_8250_port *up = &serial8250_ports[i];
- struct uart_port *port = &up->port;
-
- port->line = i;
- serial8250_init_port(up);
- if (!base_ops)
- base_ops = port->ops;
- port->ops = &univ8250_port_ops;
-
- timer_setup(&up->timer, serial8250_timeout, 0);
-
- up->ops = &univ8250_driver_ops;
-
- if (IS_ENABLED(CONFIG_ALPHA_JENSEN) ||
- (IS_ENABLED(CONFIG_ALPHA_GENERIC) && alpha_jensen()))
- port->set_mctrl = alpha_jensen_set_mctrl;
-
- serial8250_set_defaults(up);
- }
+ /*
+ * Set up initial isa ports based on nr_uart module param, or else
+ * default to CONFIG_SERIAL_8250_RUNTIME_UARTS. Note that we do not
+ * need to increase nr_uarts when setting up the initial isa ports.
+ */
+ for (i = 0; i < nr_uarts; i++)
+ serial8250_setup_port(i);
/* chain base port ops to support Remote Supervisor Adapter */
univ8250_port_ops = *base_ops;
@@ -586,16 +601,29 @@ static void univ8250_console_write(struct console *co, const char *s,
static int univ8250_console_setup(struct console *co, char *options)
{
+ struct uart_8250_port *up;
struct uart_port *port;
- int retval;
+ int retval, i;
/*
* Check whether an invalid uart number has been specified, and
* if so, search for the first available port that does have
* console support.
*/
- if (co->index >= nr_uarts)
+ if (co->index >= UART_NR)
co->index = 0;
+
+ /*
+ * If the console is past the initial isa ports, init more ports up to
+ * co->index as needed and increment nr_uarts accordingly.
+ */
+ for (i = nr_uarts; i <= co->index; i++) {
+ up = serial8250_setup_port(i);
+ if (!up)
+ return -ENODEV;
+ nr_uarts++;
+ }
+
port = &serial8250_ports[co->index].port;
/* link port to console */
port->cons = co;
@@ -822,12 +850,16 @@ static int serial8250_probe(struct platform_device *dev)
uart.port.iotype = p->iotype;
uart.port.flags = p->flags;
uart.port.mapbase = p->mapbase;
+ uart.port.mapsize = p->mapsize;
uart.port.hub6 = p->hub6;
uart.port.has_sysrq = p->has_sysrq;
uart.port.private_data = p->private_data;
uart.port.type = p->type;
+ uart.bugs = p->bugs;
uart.port.serial_in = p->serial_in;
uart.port.serial_out = p->serial_out;
+ uart.dl_read = p->dl_read;
+ uart.dl_write = p->dl_write;
uart.port.handle_irq = p->handle_irq;
uart.port.handle_break = p->handle_break;
uart.port.set_termios = p->set_termios;
@@ -990,12 +1022,24 @@ int serial8250_register_8250_port(const struct uart_8250_port *up)
mutex_lock(&serial_mutex);
uart = serial8250_find_match_or_unused(&up->port);
- if (uart && uart->port.type != PORT_8250_CIR) {
+ if (!uart) {
+ /*
+ * If the port is past the initial isa ports, initialize a new
+ * port and increment nr_uarts accordingly.
+ */
+ uart = serial8250_setup_port(nr_uarts);
+ if (!uart)
+ goto unlock;
+ nr_uarts++;
+ }
+
+ if (uart->port.type != PORT_8250_CIR) {
struct mctrl_gpios *gpios;
if (uart->port.dev)
uart_remove_one_port(&serial8250_reg, &uart->port);
+ uart->port.ctrl_id = up->port.ctrl_id;
uart->port.iobase = up->port.iobase;
uart->port.membase = up->port.membase;
uart->port.irq = up->port.irq;
@@ -1120,6 +1164,7 @@ int serial8250_register_8250_port(const struct uart_8250_port *up)
}
}
+unlock:
mutex_unlock(&serial_mutex);
return ret;
diff --git a/drivers/tty/serial/8250/8250_early.c b/drivers/tty/serial/8250/8250_early.c
index 0ebde0ab8167..4299a8bd83d9 100644
--- a/drivers/tty/serial/8250/8250_early.c
+++ b/drivers/tty/serial/8250/8250_early.c
@@ -36,7 +36,6 @@
static unsigned int serial8250_early_in(struct uart_port *port, int offset)
{
- int reg_offset = offset;
offset <<= port->regshift;
switch (port->iotype) {
@@ -50,8 +49,6 @@ static unsigned int serial8250_early_in(struct uart_port *port, int offset)
return ioread32be(port->membase + offset);
case UPIO_PORT:
return inb(port->iobase + offset);
- case UPIO_AU:
- return port->serial_in(port, reg_offset);
default:
return 0;
}
@@ -59,7 +56,6 @@ static unsigned int serial8250_early_in(struct uart_port *port, int offset)
static void serial8250_early_out(struct uart_port *port, int offset, int value)
{
- int reg_offset = offset;
offset <<= port->regshift;
switch (port->iotype) {
@@ -78,9 +74,6 @@ static void serial8250_early_out(struct uart_port *port, int offset, int value)
case UPIO_PORT:
outb(value, port->iobase + offset);
break;
- case UPIO_AU:
- port->serial_out(port, reg_offset, value);
- break;
}
}
@@ -199,17 +192,3 @@ OF_EARLYCON_DECLARE(omap8250, "ti,omap3-uart", early_omap8250_setup);
OF_EARLYCON_DECLARE(omap8250, "ti,omap4-uart", early_omap8250_setup);
#endif
-
-#ifdef CONFIG_SERIAL_8250_RT288X
-
-static int __init early_au_setup(struct earlycon_device *dev, const char *opt)
-{
- dev->port.serial_in = au_serial_in;
- dev->port.serial_out = au_serial_out;
- dev->port.iotype = UPIO_AU;
- dev->con->write = early_serial8250_write;
- return 0;
-}
-OF_EARLYCON_DECLARE(palmchip, "ralink,rt2880-uart", early_au_setup);
-
-#endif
diff --git a/drivers/tty/serial/8250/8250_em.c b/drivers/tty/serial/8250/8250_em.c
index 25a9ecf26be6..ef5019e944ea 100644
--- a/drivers/tty/serial/8250/8250_em.c
+++ b/drivers/tty/serial/8250/8250_em.c
@@ -139,12 +139,12 @@ static void serial8250_em_serial_out(struct uart_port *p, int offset, int value)
}
}
-static int serial8250_em_serial_dl_read(struct uart_8250_port *up)
+static u32 serial8250_em_serial_dl_read(struct uart_8250_port *up)
{
return serial_in(up, UART_DLL_EM) | serial_in(up, UART_DLM_EM) << 8;
}
-static void serial8250_em_serial_dl_write(struct uart_8250_port *up, int value)
+static void serial8250_em_serial_dl_write(struct uart_8250_port *up, u32 value)
{
serial_out(up, UART_DLL_EM, value & 0xff);
serial_out(up, UART_DLM_EM, value >> 8 & 0xff);
diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c
index b406cba10b0e..077c3ba3539e 100644
--- a/drivers/tty/serial/8250/8250_exar.c
+++ b/drivers/tty/serial/8250/8250_exar.c
@@ -198,8 +198,12 @@ static int xr17v35x_startup(struct uart_port *port)
/*
* Make sure all interrups are masked until initialization is
* complete and the FIFOs are cleared
+ *
+ * Synchronize UART_IER access against the console.
*/
+ spin_lock_irq(&port->lock);
serial_port_out(port, UART_IER, 0);
+ spin_unlock_irq(&port->lock);
return serial8250_do_startup(port);
}
diff --git a/drivers/tty/serial/8250/8250_fsl.c b/drivers/tty/serial/8250/8250_fsl.c
index 8adfaa183f77..00f46b9a8b09 100644
--- a/drivers/tty/serial/8250/8250_fsl.c
+++ b/drivers/tty/serial/8250/8250_fsl.c
@@ -38,7 +38,19 @@ int fsl8250_handle_irq(struct uart_port *port)
return 0;
}
- /* This is the WAR; if last event was BRK, then read and return */
+ /*
+ * For a single break the hardware reports LSR.BI for each character
+ * time. This is described in the MPC8313E chip errata as "General17".
+ * A typical break has a duration of 0.3s, with a 115200n8 configuration
+ * that (theoretically) corresponds to ~3500 interrupts in these 0.3s.
+ * In practise it's less (around 500) because of hardware
+ * and software latencies. The workaround recommended by the vendor is
+ * to read the RX register (to clear LSR.DR and thus prevent a FIFO
+ * aging interrupt). To prevent the irq from retriggering LSR must not be
+ * read. (This would clear LSR.BI, hardware would reassert the BI event
+ * immediately and interrupt the CPU again. The hardware clears LSR.BI
+ * when the next valid char is read.)
+ */
if (unlikely(up->lsr_saved_flags & UART_LSR_BI)) {
up->lsr_saved_flags &= ~UART_LSR_BI;
port->serial_in(port, UART_RX);
diff --git a/drivers/tty/serial/8250/8250_mtk.c b/drivers/tty/serial/8250/8250_mtk.c
index fb1d5ec0940e..aa8e98164d68 100644
--- a/drivers/tty/serial/8250/8250_mtk.c
+++ b/drivers/tty/serial/8250/8250_mtk.c
@@ -222,11 +222,17 @@ static void mtk8250_shutdown(struct uart_port *port)
static void mtk8250_disable_intrs(struct uart_8250_port *up, int mask)
{
+ /* Port locked to synchronize UART_IER access against the console. */
+ lockdep_assert_held_once(&up->port.lock);
+
serial_out(up, UART_IER, serial_in(up, UART_IER) & (~mask));
}
static void mtk8250_enable_intrs(struct uart_8250_port *up, int mask)
{
+ /* Port locked to synchronize UART_IER access against the console. */
+ lockdep_assert_held_once(&up->port.lock);
+
serial_out(up, UART_IER, serial_in(up, UART_IER) | mask);
}
@@ -235,6 +241,9 @@ static void mtk8250_set_flow_ctrl(struct uart_8250_port *up, int mode)
struct uart_port *port = &up->port;
int lcr = serial_in(up, UART_LCR);
+ /* Port locked to synchronize UART_IER access against the console. */
+ lockdep_assert_held_once(&port->lock);
+
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
serial_out(up, MTK_UART_EFR, UART_EFR_ECB);
serial_out(up, UART_LCR, lcr);
diff --git a/drivers/tty/serial/8250/8250_of.c b/drivers/tty/serial/8250/8250_of.c
index 1b461fba15a3..c9f6bd7a7038 100644
--- a/drivers/tty/serial/8250/8250_of.c
+++ b/drivers/tty/serial/8250/8250_of.c
@@ -171,7 +171,9 @@ static int of_platform_serial_setup(struct platform_device *ofdev,
switch (type) {
case PORT_RT2880:
- port->iotype = UPIO_AU;
+ ret = rt288x_setup(port);
+ if (ret)
+ goto err_unprepare;
break;
}
diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c
index 734f092ef839..fe26ebc7b834 100644
--- a/drivers/tty/serial/8250/8250_omap.c
+++ b/drivers/tty/serial/8250/8250_omap.c
@@ -32,6 +32,7 @@
#include "8250.h"
#define DEFAULT_CLK_SPEED 48000000
+#define OMAP_UART_REGSHIFT 2
#define UART_ERRATA_i202_MDR1_ACCESS (1 << 0)
#define OMAP_UART_WER_HAS_TX_WAKEUP (1 << 1)
@@ -115,6 +116,7 @@
#define UART_OMAP_RX_LVL 0x19
struct omap8250_priv {
+ void __iomem *membase;
int line;
u8 habit;
u8 mdr1;
@@ -159,9 +161,9 @@ static void omap_8250_rx_dma_flush(struct uart_8250_port *p);
static inline void omap_8250_rx_dma_flush(struct uart_8250_port *p) { }
#endif
-static u32 uart_read(struct uart_8250_port *up, u32 reg)
+static u32 uart_read(struct omap8250_priv *priv, u32 reg)
{
- return readl(up->port.membase + (reg << up->port.regshift));
+ return readl(priv->membase + (reg << OMAP_UART_REGSHIFT));
}
/*
@@ -302,6 +304,9 @@ static void omap8250_restore_regs(struct uart_8250_port *up)
struct uart_8250_dma *dma = up->dma;
u8 mcr = serial8250_in_MCR(up);
+ /* Port locked to synchronize UART_IER access against the console. */
+ lockdep_assert_held_once(&up->port.lock);
+
if (dma && dma->tx_running) {
/*
* TCSANOW requests the change to occur immediately however if
@@ -523,6 +528,10 @@ static void omap_8250_pm(struct uart_port *port, unsigned int state,
u8 efr;
pm_runtime_get_sync(port->dev);
+
+ /* Synchronize UART_IER access against the console. */
+ spin_lock_irq(&port->lock);
+
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
efr = serial_in(up, UART_EFR);
serial_out(up, UART_EFR, efr | UART_EFR_ECB);
@@ -533,6 +542,8 @@ static void omap_8250_pm(struct uart_port *port, unsigned int state,
serial_out(up, UART_EFR, efr);
serial_out(up, UART_LCR, 0);
+ spin_unlock_irq(&port->lock);
+
pm_runtime_mark_last_busy(port->dev);
pm_runtime_put_autosuspend(port->dev);
}
@@ -548,7 +559,7 @@ static void omap_serial_fill_features_erratas(struct uart_8250_port *up,
u32 mvr, scheme;
u16 revision, major, minor;
- mvr = uart_read(up, UART_OMAP_MVER);
+ mvr = uart_read(priv, UART_OMAP_MVER);
/* Check revision register scheme */
scheme = mvr >> OMAP_UART_MVR_SCHEME_SHIFT;
@@ -616,9 +627,9 @@ static int omap_8250_dma_handle_irq(struct uart_port *port);
static irqreturn_t omap8250_irq(int irq, void *dev_id)
{
- struct uart_port *port = dev_id;
- struct omap8250_priv *priv = port->private_data;
- struct uart_8250_port *up = up_to_u8250p(port);
+ struct omap8250_priv *priv = dev_id;
+ struct uart_8250_port *up = serial8250_get_port(priv->line);
+ struct uart_port *port = &up->port;
unsigned int iir, lsr;
int ret;
@@ -649,6 +660,8 @@ static irqreturn_t omap8250_irq(int irq, void *dev_id)
if ((lsr & UART_LSR_OE) && up->overrun_backoff_time_ms > 0) {
unsigned long delay;
+ /* Synchronize UART_IER access against the console. */
+ spin_lock(&port->lock);
up->ier = port->serial_in(port, UART_IER);
if (up->ier & (UART_IER_RLSI | UART_IER_RDI)) {
port->ops->stop_rx(port);
@@ -658,6 +671,7 @@ static irqreturn_t omap8250_irq(int irq, void *dev_id)
*/
cancel_delayed_work(&up->overrun_backoff);
}
+ spin_unlock(&port->lock);
delay = msecs_to_jiffies(up->overrun_backoff_time_ms);
schedule_delayed_work(&up->overrun_backoff, delay);
@@ -672,6 +686,7 @@ static int omap_8250_startup(struct uart_port *port)
{
struct uart_8250_port *up = up_to_u8250p(port);
struct omap8250_priv *priv = port->private_data;
+ struct uart_8250_dma *dma = &priv->omap8250_dma;
int ret;
if (priv->wakeirq) {
@@ -690,25 +705,23 @@ static int omap_8250_startup(struct uart_port *port)
up->msr_saved_flags = 0;
/* Disable DMA for console UART */
- if (uart_console(port))
- up->dma = NULL;
-
- if (up->dma) {
+ if (dma->fn && !uart_console(port)) {
+ up->dma = &priv->omap8250_dma;
ret = serial8250_request_dma(up);
if (ret) {
dev_warn_ratelimited(port->dev,
"failed to request DMA\n");
up->dma = NULL;
}
+ } else {
+ up->dma = NULL;
}
- ret = request_irq(port->irq, omap8250_irq, IRQF_SHARED,
- dev_name(port->dev), port);
- if (ret < 0)
- goto err;
-
+ /* Synchronize UART_IER access against the console. */
+ spin_lock_irq(&port->lock);
up->ier = UART_IER_RLSI | UART_IER_RDI;
serial_out(up, UART_IER, up->ier);
+ spin_unlock_irq(&port->lock);
#ifdef CONFIG_PM
up->capabilities |= UART_CAP_RPM;
@@ -720,17 +733,17 @@ static int omap_8250_startup(struct uart_port *port)
priv->wer |= OMAP_UART_TX_WAKEUP_EN;
serial_out(up, UART_OMAP_WER, priv->wer);
- if (up->dma && !(priv->habit & UART_HAS_EFR2))
+ if (up->dma && !(priv->habit & UART_HAS_EFR2)) {
+ spin_lock_irq(&port->lock);
up->dma->rx_dma(up);
+ spin_unlock_irq(&port->lock);
+ }
+
+ enable_irq(up->port.irq);
pm_runtime_mark_last_busy(port->dev);
pm_runtime_put_autosuspend(port->dev);
return 0;
-err:
- pm_runtime_mark_last_busy(port->dev);
- pm_runtime_put_autosuspend(port->dev);
- dev_pm_clear_wake_irq(port->dev);
- return ret;
}
static void omap_8250_shutdown(struct uart_port *port)
@@ -748,11 +761,16 @@ static void omap_8250_shutdown(struct uart_port *port)
if (priv->habit & UART_HAS_EFR2)
serial_out(up, UART_OMAP_EFR2, 0x0);
+ /* Synchronize UART_IER access against the console. */
+ spin_lock_irq(&port->lock);
up->ier = 0;
serial_out(up, UART_IER, 0);
+ spin_unlock_irq(&port->lock);
+ disable_irq_nosync(up->port.irq);
+ dev_pm_clear_wake_irq(port->dev);
- if (up->dma)
- serial8250_release_dma(up);
+ serial8250_release_dma(up);
+ up->dma = NULL;
/*
* Disable break condition and FIFOs
@@ -763,8 +781,6 @@ static void omap_8250_shutdown(struct uart_port *port)
pm_runtime_mark_last_busy(port->dev);
pm_runtime_put_autosuspend(port->dev);
- free_irq(port->irq, port);
- dev_pm_clear_wake_irq(port->dev);
}
static void omap_8250_throttle(struct uart_port *port)
@@ -791,6 +807,7 @@ static void omap_8250_unthrottle(struct uart_port *port)
pm_runtime_get_sync(port->dev);
+ /* Synchronize UART_IER access against the console. */
spin_lock_irqsave(&port->lock, flags);
priv->throttled = false;
if (up->dma)
@@ -941,6 +958,7 @@ static void __dma_rx_complete(void *param)
struct dma_tx_state state;
unsigned long flags;
+ /* Synchronize UART_IER access against the console. */
spin_lock_irqsave(&p->port.lock, flags);
/*
@@ -998,6 +1016,9 @@ static int omap_8250_rx_dma(struct uart_8250_port *p)
unsigned long flags;
u32 reg;
+ /* Port locked to synchronize UART_IER access against the console. */
+ lockdep_assert_held_once(&p->port.lock);
+
if (priv->rx_dma_broken)
return -EINVAL;
@@ -1212,6 +1233,9 @@ static u16 omap_8250_handle_rx_dma(struct uart_8250_port *up, u8 iir, u16 status
static void am654_8250_handle_rx_dma(struct uart_8250_port *up, u8 iir,
u16 status)
{
+ /* Port locked to synchronize UART_IER access against the console. */
+ lockdep_assert_held_once(&up->port.lock);
+
/*
* Queue a new transfer if FIFO has data.
*/
@@ -1394,7 +1418,7 @@ static int omap8250_probe(struct platform_device *pdev)
UPF_HARD_FLOW;
up.port.private_data = priv;
- up.port.regshift = 2;
+ up.port.regshift = OMAP_UART_REGSHIFT;
up.port.fifosize = 64;
up.tx_loadsz = 64;
up.capabilities = UART_CAP_FIFO;
@@ -1444,8 +1468,6 @@ static int omap8250_probe(struct platform_device *pdev)
&up.overrun_backoff_time_ms) != 0)
up.overrun_backoff_time_ms = 0;
- priv->wakeirq = irq_of_parse_and_map(np, 1);
-
pdata = of_device_get_match_data(&pdev->dev);
if (pdata)
priv->habit |= pdata->habit;
@@ -1457,6 +1479,8 @@ static int omap8250_probe(struct platform_device *pdev)
DEFAULT_CLK_SPEED);
}
+ priv->membase = membase;
+ priv->line = -ENODEV;
priv->latency = PM_QOS_CPU_LATENCY_DEFAULT_VALUE;
priv->calc_latency = PM_QOS_CPU_LATENCY_DEFAULT_VALUE;
cpu_latency_qos_add_request(&priv->pm_qos_request, priv->latency);
@@ -1464,6 +1488,8 @@ static int omap8250_probe(struct platform_device *pdev)
spin_lock_init(&priv->rx_dma_lock);
+ platform_set_drvdata(pdev, priv);
+
device_init_wakeup(&pdev->dev, true);
pm_runtime_enable(&pdev->dev);
pm_runtime_use_autosuspend(&pdev->dev);
@@ -1498,64 +1524,77 @@ static int omap8250_probe(struct platform_device *pdev)
ret = of_property_count_strings(np, "dma-names");
if (ret == 2) {
struct omap8250_dma_params *dma_params = NULL;
+ struct uart_8250_dma *dma = &priv->omap8250_dma;
- up.dma = &priv->omap8250_dma;
- up.dma->fn = the_no_dma_filter_fn;
- up.dma->tx_dma = omap_8250_tx_dma;
- up.dma->rx_dma = omap_8250_rx_dma;
+ dma->fn = the_no_dma_filter_fn;
+ dma->tx_dma = omap_8250_tx_dma;
+ dma->rx_dma = omap_8250_rx_dma;
if (pdata)
dma_params = pdata->dma_params;
if (dma_params) {
- up.dma->rx_size = dma_params->rx_size;
- up.dma->rxconf.src_maxburst = dma_params->rx_trigger;
- up.dma->txconf.dst_maxburst = dma_params->tx_trigger;
+ dma->rx_size = dma_params->rx_size;
+ dma->rxconf.src_maxburst = dma_params->rx_trigger;
+ dma->txconf.dst_maxburst = dma_params->tx_trigger;
priv->rx_trigger = dma_params->rx_trigger;
priv->tx_trigger = dma_params->tx_trigger;
} else {
- up.dma->rx_size = RX_TRIGGER;
- up.dma->rxconf.src_maxburst = RX_TRIGGER;
- up.dma->txconf.dst_maxburst = TX_TRIGGER;
+ dma->rx_size = RX_TRIGGER;
+ dma->rxconf.src_maxburst = RX_TRIGGER;
+ dma->txconf.dst_maxburst = TX_TRIGGER;
}
}
#endif
+
+ irq_set_status_flags(irq, IRQ_NOAUTOEN);
+ ret = devm_request_irq(&pdev->dev, irq, omap8250_irq, 0,
+ dev_name(&pdev->dev), priv);
+ if (ret < 0)
+ return ret;
+
+ priv->wakeirq = irq_of_parse_and_map(np, 1);
+
ret = serial8250_register_8250_port(&up);
if (ret < 0) {
dev_err(&pdev->dev, "unable to register 8250 port\n");
goto err;
}
priv->line = ret;
- platform_set_drvdata(pdev, priv);
pm_runtime_mark_last_busy(&pdev->dev);
pm_runtime_put_autosuspend(&pdev->dev);
return 0;
err:
pm_runtime_dont_use_autosuspend(&pdev->dev);
pm_runtime_put_sync(&pdev->dev);
+ flush_work(&priv->qos_work);
pm_runtime_disable(&pdev->dev);
+ cpu_latency_qos_remove_request(&priv->pm_qos_request);
return ret;
}
static int omap8250_remove(struct platform_device *pdev)
{
struct omap8250_priv *priv = platform_get_drvdata(pdev);
+ struct uart_8250_port *up;
int err;
err = pm_runtime_resume_and_get(&pdev->dev);
if (err)
return err;
+ up = serial8250_get_port(priv->line);
+ omap_8250_shutdown(&up->port);
+ serial8250_unregister_port(priv->line);
+ priv->line = -ENODEV;
pm_runtime_dont_use_autosuspend(&pdev->dev);
pm_runtime_put_sync(&pdev->dev);
flush_work(&priv->qos_work);
pm_runtime_disable(&pdev->dev);
- serial8250_unregister_port(priv->line);
cpu_latency_qos_remove_request(&priv->pm_qos_request);
device_init_wakeup(&pdev->dev, false);
return 0;
}
-#ifdef CONFIG_PM_SLEEP
static int omap8250_prepare(struct device *dev)
{
struct omap8250_priv *priv = dev_get_drvdata(dev);
@@ -1600,12 +1639,7 @@ static int omap8250_resume(struct device *dev)
serial8250_resume_port(priv->line);
return 0;
}
-#else
-#define omap8250_prepare NULL
-#define omap8250_complete NULL
-#endif
-#ifdef CONFIG_PM
static int omap8250_lost_context(struct uart_8250_port *up)
{
u32 val;
@@ -1621,11 +1655,15 @@ static int omap8250_lost_context(struct uart_8250_port *up)
return 0;
}
+static void uart_write(struct omap8250_priv *priv, u32 reg, u32 val)
+{
+ writel(val, priv->membase + (reg << OMAP_UART_REGSHIFT));
+}
+
/* TODO: in future, this should happen via API in drivers/reset/ */
static int omap8250_soft_reset(struct device *dev)
{
struct omap8250_priv *priv = dev_get_drvdata(dev);
- struct uart_8250_port *up = serial8250_get_port(priv->line);
int timeout = 100;
int sysc;
int syss;
@@ -1639,20 +1677,20 @@ static int omap8250_soft_reset(struct device *dev)
* needing omap8250_soft_reset() quirk. Do it in two writes as
* recommended in the comment for omap8250_update_scr().
*/
- serial_out(up, UART_OMAP_SCR, OMAP_UART_SCR_DMAMODE_1);
- serial_out(up, UART_OMAP_SCR,
+ uart_write(priv, UART_OMAP_SCR, OMAP_UART_SCR_DMAMODE_1);
+ uart_write(priv, UART_OMAP_SCR,
OMAP_UART_SCR_DMAMODE_1 | OMAP_UART_SCR_DMAMODE_CTL);
- sysc = serial_in(up, UART_OMAP_SYSC);
+ sysc = uart_read(priv, UART_OMAP_SYSC);
/* softreset the UART */
sysc |= OMAP_UART_SYSC_SOFTRESET;
- serial_out(up, UART_OMAP_SYSC, sysc);
+ uart_write(priv, UART_OMAP_SYSC, sysc);
/* By experiments, 1us enough for reset complete on AM335x */
do {
udelay(1);
- syss = serial_in(up, UART_OMAP_SYSS);
+ syss = uart_read(priv, UART_OMAP_SYSS);
} while (--timeout && !(syss & OMAP_UART_SYSS_RESETDONE));
if (!timeout) {
@@ -1666,13 +1704,10 @@ static int omap8250_soft_reset(struct device *dev)
static int omap8250_runtime_suspend(struct device *dev)
{
struct omap8250_priv *priv = dev_get_drvdata(dev);
- struct uart_8250_port *up;
+ struct uart_8250_port *up = NULL;
- /* In case runtime-pm tries this before we are setup */
- if (!priv)
- return 0;
-
- up = serial8250_get_port(priv->line);
+ if (priv->line >= 0)
+ up = serial8250_get_port(priv->line);
/*
* When using 'no_console_suspend', the console UART must not be
* suspended. Since driver suspend is managed by runtime suspend,
@@ -1680,7 +1715,7 @@ static int omap8250_runtime_suspend(struct device *dev)
* active during suspend.
*/
if (priv->is_suspending && !console_suspend_enabled) {
- if (uart_console(&up->port))
+ if (up && uart_console(&up->port))
return -EBUSY;
}
@@ -1691,13 +1726,15 @@ static int omap8250_runtime_suspend(struct device *dev)
if (ret)
return ret;
- /* Restore to UART mode after reset (for wakeup) */
- omap8250_update_mdr1(up, priv);
- /* Restore wakeup enable register */
- serial_out(up, UART_OMAP_WER, priv->wer);
+ if (up) {
+ /* Restore to UART mode after reset (for wakeup) */
+ omap8250_update_mdr1(up, priv);
+ /* Restore wakeup enable register */
+ serial_out(up, UART_OMAP_WER, priv->wer);
+ }
}
- if (up->dma && up->dma->rxchan)
+ if (up && up->dma && up->dma->rxchan)
omap_8250_rx_dma_flush(up);
priv->latency = PM_QOS_CPU_LATENCY_DEFAULT_VALUE;
@@ -1709,25 +1746,27 @@ static int omap8250_runtime_suspend(struct device *dev)
static int omap8250_runtime_resume(struct device *dev)
{
struct omap8250_priv *priv = dev_get_drvdata(dev);
- struct uart_8250_port *up;
-
- /* In case runtime-pm tries this before we are setup */
- if (!priv)
- return 0;
+ struct uart_8250_port *up = NULL;
- up = serial8250_get_port(priv->line);
+ if (priv->line >= 0)
+ up = serial8250_get_port(priv->line);
- if (omap8250_lost_context(up))
+ if (up && omap8250_lost_context(up)) {
+ spin_lock_irq(&up->port.lock);
omap8250_restore_regs(up);
+ spin_unlock_irq(&up->port.lock);
+ }
- if (up->dma && up->dma->rxchan && !(priv->habit & UART_HAS_EFR2))
+ if (up && up->dma && up->dma->rxchan && !(priv->habit & UART_HAS_EFR2)) {
+ spin_lock_irq(&up->port.lock);
omap_8250_rx_dma(up);
+ spin_unlock_irq(&up->port.lock);
+ }
priv->latency = priv->calc_latency;
schedule_work(&priv->qos_work);
return 0;
}
-#endif
#ifdef CONFIG_SERIAL_8250_OMAP_TTYO_FIXUP
static int __init omap8250_console_fixup(void)
@@ -1770,17 +1809,17 @@ console_initcall(omap8250_console_fixup);
#endif
static const struct dev_pm_ops omap8250_dev_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(omap8250_suspend, omap8250_resume)
- SET_RUNTIME_PM_OPS(omap8250_runtime_suspend,
+ SYSTEM_SLEEP_PM_OPS(omap8250_suspend, omap8250_resume)
+ RUNTIME_PM_OPS(omap8250_runtime_suspend,
omap8250_runtime_resume, NULL)
- .prepare = omap8250_prepare,
- .complete = omap8250_complete,
+ .prepare = pm_sleep_ptr(omap8250_prepare),
+ .complete = pm_sleep_ptr(omap8250_complete),
};
static struct platform_driver omap8250_platform_driver = {
.driver = {
.name = "omap8250",
- .pm = &omap8250_dev_pm_ops,
+ .pm = pm_ptr(&omap8250_dev_pm_ops),
.of_match_table = omap8250_dt_ids,
},
.probe = omap8250_probe,
diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c
index c153ba3a018a..dfb51a854e77 100644
--- a/drivers/tty/serial/8250/8250_port.c
+++ b/drivers/tty/serial/8250/8250_port.c
@@ -325,7 +325,7 @@ static const struct serial8250_config uart_config[] = {
};
/* Uart divisor latch read */
-static int default_serial_dl_read(struct uart_8250_port *up)
+static u32 default_serial_dl_read(struct uart_8250_port *up)
{
/* Assign these in pieces to truncate any bits above 7. */
unsigned char dll = serial_in(up, UART_DLL);
@@ -335,72 +335,12 @@ static int default_serial_dl_read(struct uart_8250_port *up)
}
/* Uart divisor latch write */
-static void default_serial_dl_write(struct uart_8250_port *up, int value)
+static void default_serial_dl_write(struct uart_8250_port *up, u32 value)
{
serial_out(up, UART_DLL, value & 0xff);
serial_out(up, UART_DLM, value >> 8 & 0xff);
}
-#ifdef CONFIG_SERIAL_8250_RT288X
-
-#define UART_REG_UNMAPPED -1
-
-/* Au1x00/RT288x UART hardware has a weird register layout */
-static const s8 au_io_in_map[8] = {
- [UART_RX] = 0,
- [UART_IER] = 2,
- [UART_IIR] = 3,
- [UART_LCR] = 5,
- [UART_MCR] = 6,
- [UART_LSR] = 7,
- [UART_MSR] = 8,
- [UART_SCR] = UART_REG_UNMAPPED,
-};
-
-static const s8 au_io_out_map[8] = {
- [UART_TX] = 1,
- [UART_IER] = 2,
- [UART_FCR] = 4,
- [UART_LCR] = 5,
- [UART_MCR] = 6,
- [UART_LSR] = UART_REG_UNMAPPED,
- [UART_MSR] = UART_REG_UNMAPPED,
- [UART_SCR] = UART_REG_UNMAPPED,
-};
-
-unsigned int au_serial_in(struct uart_port *p, int offset)
-{
- if (offset >= ARRAY_SIZE(au_io_in_map))
- return UINT_MAX;
- offset = au_io_in_map[offset];
- if (offset == UART_REG_UNMAPPED)
- return UINT_MAX;
- return __raw_readl(p->membase + (offset << p->regshift));
-}
-
-void au_serial_out(struct uart_port *p, int offset, int value)
-{
- if (offset >= ARRAY_SIZE(au_io_out_map))
- return;
- offset = au_io_out_map[offset];
- if (offset == UART_REG_UNMAPPED)
- return;
- __raw_writel(value, p->membase + (offset << p->regshift));
-}
-
-/* Au1x00 haven't got a standard divisor latch */
-static int au_serial_dl_read(struct uart_8250_port *up)
-{
- return __raw_readl(up->port.membase + 0x28);
-}
-
-static void au_serial_dl_write(struct uart_8250_port *up, int value)
-{
- __raw_writel(value, up->port.membase + 0x28);
-}
-
-#endif
-
static unsigned int hub6_serial_in(struct uart_port *p, int offset)
{
offset = offset << p->regshift;
@@ -510,15 +450,6 @@ static void set_io_from_upio(struct uart_port *p)
p->serial_out = mem32be_serial_out;
break;
-#ifdef CONFIG_SERIAL_8250_RT288X
- case UPIO_AU:
- p->serial_in = au_serial_in;
- p->serial_out = au_serial_out;
- up->dl_read = au_serial_dl_read;
- up->dl_write = au_serial_dl_write;
- break;
-#endif
-
default:
p->serial_in = io_serial_in;
p->serial_out = io_serial_out;
@@ -608,6 +539,9 @@ EXPORT_SYMBOL_GPL(serial8250_rpm_put);
*/
static int serial8250_em485_init(struct uart_8250_port *p)
{
+ /* Port locked to synchronize UART_IER access against the console. */
+ lockdep_assert_held_once(&p->port.lock);
+
if (p->em485)
goto deassert_rts;
@@ -746,6 +680,8 @@ static void serial8250_set_sleep(struct uart_8250_port *p, int sleep)
serial8250_rpm_get(p);
if (p->capabilities & UART_CAP_SLEEP) {
+ /* Synchronize UART_IER access against the console. */
+ spin_lock_irq(&p->port.lock);
if (p->capabilities & UART_CAP_EFR) {
lcr = serial_in(p, UART_LCR);
efr = serial_in(p, UART_EFR);
@@ -759,6 +695,7 @@ static void serial8250_set_sleep(struct uart_8250_port *p, int sleep)
serial_out(p, UART_EFR, efr);
serial_out(p, UART_LCR, lcr);
}
+ spin_unlock_irq(&p->port.lock);
}
serial8250_rpm_put(p);
@@ -766,6 +703,9 @@ static void serial8250_set_sleep(struct uart_8250_port *p, int sleep)
static void serial8250_clear_IER(struct uart_8250_port *up)
{
+ /* Port locked to synchronize UART_IER access against the console. */
+ lockdep_assert_held_once(&up->port.lock);
+
if (up->capabilities & UART_CAP_UUE)
serial_out(up, UART_IER, UART_IER_UUE);
else
@@ -848,7 +788,7 @@ static void disable_rsa(struct uart_8250_port *up)
static int size_fifo(struct uart_8250_port *up)
{
unsigned char old_fcr, old_mcr, old_lcr;
- unsigned short old_dl;
+ u32 old_dl;
int count;
old_lcr = serial_in(up, UART_LCR);
@@ -1038,6 +978,9 @@ static void autoconfig_16550a(struct uart_8250_port *up)
unsigned char status1, status2;
unsigned int iersave;
+ /* Port locked to synchronize UART_IER access against the console. */
+ lockdep_assert_held_once(&up->port.lock);
+
up->port.type = PORT_16550A;
up->capabilities |= UART_CAP_FIFO;
@@ -1221,6 +1164,8 @@ static void autoconfig(struct uart_8250_port *up)
/*
* We really do need global IRQs disabled here - we're going to
* be frobbing the chips IRQ enable register to see if it exists.
+ *
+ * Synchronize UART_IER access against the console.
*/
spin_lock_irqsave(&port->lock, flags);
@@ -1393,7 +1338,10 @@ static void autoconfig_irq(struct uart_8250_port *up)
/* forget possible initially masked and pending IRQ */
probe_irq_off(probe_irq_on());
save_mcr = serial8250_in_MCR(up);
+ /* Synchronize UART_IER access against the console. */
+ spin_lock_irq(&port->lock);
save_ier = serial_in(up, UART_IER);
+ spin_unlock_irq(&port->lock);
serial8250_out_MCR(up, UART_MCR_OUT1 | UART_MCR_OUT2);
irqs = probe_irq_on();
@@ -1405,7 +1353,10 @@ static void autoconfig_irq(struct uart_8250_port *up)
serial8250_out_MCR(up,
UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2);
}
+ /* Synchronize UART_IER access against the console. */
+ spin_lock_irq(&port->lock);
serial_out(up, UART_IER, UART_IER_ALL_INTR);
+ spin_unlock_irq(&port->lock);
serial_in(up, UART_LSR);
serial_in(up, UART_RX);
serial_in(up, UART_IIR);
@@ -1415,7 +1366,10 @@ static void autoconfig_irq(struct uart_8250_port *up)
irq = probe_irq_off(irqs);
serial8250_out_MCR(up, save_mcr);
+ /* Synchronize UART_IER access against the console. */
+ spin_lock_irq(&port->lock);
serial_out(up, UART_IER, save_ier);
+ spin_unlock_irq(&port->lock);
if (port->flags & UPF_FOURPORT)
outb_p(save_ICP, ICP);
@@ -1430,6 +1384,9 @@ static void serial8250_stop_rx(struct uart_port *port)
{
struct uart_8250_port *up = up_to_u8250p(port);
+ /* Port locked to synchronize UART_IER access against the console. */
+ lockdep_assert_held_once(&port->lock);
+
serial8250_rpm_get(up);
up->ier &= ~(UART_IER_RLSI | UART_IER_RDI);
@@ -1449,6 +1406,9 @@ void serial8250_em485_stop_tx(struct uart_8250_port *p)
{
unsigned char mcr = serial8250_in_MCR(p);
+ /* Port locked to synchronize UART_IER access against the console. */
+ lockdep_assert_held_once(&p->port.lock);
+
if (p->port.rs485.flags & SER_RS485_RTS_AFTER_SEND)
mcr |= UART_MCR_RTS;
else
@@ -1498,6 +1458,9 @@ static void __stop_tx_rs485(struct uart_8250_port *p, u64 stop_delay)
{
struct uart_8250_em485 *em485 = p->em485;
+ /* Port locked to synchronize UART_IER access against the console. */
+ lockdep_assert_held_once(&p->port.lock);
+
stop_delay += (u64)p->port.rs485.delay_rts_after_send * NSEC_PER_MSEC;
/*
@@ -1677,6 +1640,9 @@ static void serial8250_start_tx(struct uart_port *port)
struct uart_8250_port *up = up_to_u8250p(port);
struct uart_8250_em485 *em485 = up->em485;
+ /* Port locked to synchronize UART_IER access against the console. */
+ lockdep_assert_held_once(&port->lock);
+
if (!port->x_char && uart_circ_empty(&port->state->xmit))
return;
@@ -1704,6 +1670,9 @@ static void serial8250_disable_ms(struct uart_port *port)
{
struct uart_8250_port *up = up_to_u8250p(port);
+ /* Port locked to synchronize UART_IER access against the console. */
+ lockdep_assert_held_once(&port->lock);
+
/* no MSR capabilities */
if (up->bugs & UART_BUG_NOMSR)
return;
@@ -1718,6 +1687,9 @@ static void serial8250_enable_ms(struct uart_port *port)
{
struct uart_8250_port *up = up_to_u8250p(port);
+ /* Port locked to synchronize UART_IER access against the console. */
+ lockdep_assert_held_once(&port->lock);
+
/* no MSR capabilities */
if (up->bugs & UART_BUG_NOMSR)
return;
@@ -2174,6 +2146,14 @@ static void serial8250_put_poll_char(struct uart_port *port,
unsigned int ier;
struct uart_8250_port *up = up_to_u8250p(port);
+ /*
+ * Normally the port is locked to synchronize UART_IER access
+ * against the console. However, this function is only used by
+ * KDB/KGDB, where it may not be possible to acquire the port
+ * lock because all other CPUs are quiesced. The quiescence
+ * should allow safe lockless usage here.
+ */
+
serial8250_rpm_get(up);
/*
* First save the IER then disable the interrupts
@@ -2219,7 +2199,12 @@ int serial8250_do_startup(struct uart_port *port)
serial8250_rpm_get(up);
if (port->type == PORT_16C950) {
- /* Wake up and initialize UART */
+ /*
+ * Wake up and initialize UART
+ *
+ * Synchronize UART_IER access against the console.
+ */
+ spin_lock_irqsave(&port->lock, flags);
up->acr = 0;
serial_port_out(port, UART_LCR, UART_LCR_CONF_MODE_B);
serial_port_out(port, UART_EFR, UART_EFR_ECB);
@@ -2229,12 +2214,19 @@ int serial8250_do_startup(struct uart_port *port)
serial_port_out(port, UART_LCR, UART_LCR_CONF_MODE_B);
serial_port_out(port, UART_EFR, UART_EFR_ECB);
serial_port_out(port, UART_LCR, 0);
+ spin_unlock_irqrestore(&port->lock, flags);
}
if (port->type == PORT_DA830) {
- /* Reset the port */
+ /*
+ * Reset the port
+ *
+ * Synchronize UART_IER access against the console.
+ */
+ spin_lock_irqsave(&port->lock, flags);
serial_port_out(port, UART_IER, 0);
serial_port_out(port, UART_DA830_PWREMU_MGMT, 0);
+ spin_unlock_irqrestore(&port->lock, flags);
mdelay(10);
/* Enable Tx, Rx and free run mode */
@@ -2345,6 +2337,8 @@ int serial8250_do_startup(struct uart_port *port)
* this interrupt whenever the transmitter is idle and
* the interrupt is enabled. Delays are necessary to
* allow register changes to become visible.
+ *
+ * Synchronize UART_IER access against the console.
*/
spin_lock_irqsave(&port->lock, flags);
@@ -2495,6 +2489,8 @@ void serial8250_do_shutdown(struct uart_port *port)
serial8250_rpm_get(up);
/*
* Disable interrupts from this port
+ *
+ * Synchronize UART_IER access against the console.
*/
spin_lock_irqsave(&port->lock, flags);
up->ier = 0;
@@ -2794,6 +2790,8 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
/*
* Ok, we're now changing the port state. Do it with
* interrupts disabled.
+ *
+ * Synchronize UART_IER access against the console.
*/
serial8250_rpm_get(up);
spin_lock_irqsave(&port->lock, flags);
@@ -2969,11 +2967,6 @@ static unsigned int serial8250_port_size(struct uart_8250_port *pt)
{
if (pt->port.mapsize)
return pt->port.mapsize;
- if (pt->port.iotype == UPIO_AU) {
- if (pt->port.type == PORT_RT2880)
- return 0x100;
- return 0x1000;
- }
if (is_omap1_8250(pt))
return 0x16 << pt->port.regshift;
@@ -3223,10 +3216,6 @@ static void serial8250_config_port(struct uart_port *port, int flags)
if (flags & UART_CONFIG_TYPE)
autoconfig(up);
- /* if access method is AU, it is a 16550 with a quirk */
- if (port->type == PORT_16550A && port->iotype == UPIO_AU)
- up->bugs |= UART_BUG_NOMSR;
-
/* HW bugs may trigger IRQ while IIR == NO_INT */
if (port->type == PORT_TEGRA)
up->bugs |= UART_BUG_NOMSR;
@@ -3293,6 +3282,7 @@ void serial8250_init_port(struct uart_8250_port *up)
struct uart_port *port = &up->port;
spin_lock_init(&port->lock);
+ port->ctrl_id = 0;
port->ops = &serial8250_pops;
port->has_sysrq = IS_ENABLED(CONFIG_SERIAL_8250_CONSOLE);
diff --git a/drivers/tty/serial/8250/8250_pxa.c b/drivers/tty/serial/8250/8250_pxa.c
index 795e55142d4c..28b341f602c6 100644
--- a/drivers/tty/serial/8250/8250_pxa.c
+++ b/drivers/tty/serial/8250/8250_pxa.c
@@ -60,7 +60,7 @@ static const struct of_device_id serial_pxa_dt_ids[] = {
MODULE_DEVICE_TABLE(of, serial_pxa_dt_ids);
/* Uart divisor latch write */
-static void serial_pxa_dl_write(struct uart_8250_port *up, int value)
+static void serial_pxa_dl_write(struct uart_8250_port *up, u32 value)
{
unsigned int dll;
diff --git a/drivers/tty/serial/8250/8250_rt288x.c b/drivers/tty/serial/8250/8250_rt288x.c
new file mode 100644
index 000000000000..6415ca8d3adf
--- /dev/null
+++ b/drivers/tty/serial/8250/8250_rt288x.c
@@ -0,0 +1,136 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * RT288x/Au1xxx driver
+ */
+
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/serial.h>
+#include <linux/serial_8250.h>
+
+#include "8250.h"
+
+#define RT288X_DL 0x28
+
+/* Au1x00/RT288x UART hardware has a weird register layout */
+static const u8 au_io_in_map[7] = {
+ [UART_RX] = 0,
+ [UART_IER] = 2,
+ [UART_IIR] = 3,
+ [UART_LCR] = 5,
+ [UART_MCR] = 6,
+ [UART_LSR] = 7,
+ [UART_MSR] = 8,
+};
+
+static const u8 au_io_out_map[5] = {
+ [UART_TX] = 1,
+ [UART_IER] = 2,
+ [UART_FCR] = 4,
+ [UART_LCR] = 5,
+ [UART_MCR] = 6,
+};
+
+static unsigned int au_serial_in(struct uart_port *p, int offset)
+{
+ if (offset >= ARRAY_SIZE(au_io_in_map))
+ return UINT_MAX;
+ offset = au_io_in_map[offset];
+
+ return __raw_readl(p->membase + (offset << p->regshift));
+}
+
+static void au_serial_out(struct uart_port *p, int offset, int value)
+{
+ if (offset >= ARRAY_SIZE(au_io_out_map))
+ return;
+ offset = au_io_out_map[offset];
+
+ __raw_writel(value, p->membase + (offset << p->regshift));
+}
+
+/* Au1x00 haven't got a standard divisor latch */
+static u32 au_serial_dl_read(struct uart_8250_port *up)
+{
+ return __raw_readl(up->port.membase + RT288X_DL);
+}
+
+static void au_serial_dl_write(struct uart_8250_port *up, u32 value)
+{
+ __raw_writel(value, up->port.membase + RT288X_DL);
+}
+
+int au_platform_setup(struct plat_serial8250_port *p)
+{
+ p->iotype = UPIO_AU;
+
+ p->serial_in = au_serial_in;
+ p->serial_out = au_serial_out;
+ p->dl_read = au_serial_dl_read;
+ p->dl_write = au_serial_dl_write;
+
+ p->mapsize = 0x1000;
+
+ p->bugs |= UART_BUG_NOMSR;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(au_platform_setup);
+
+int rt288x_setup(struct uart_port *p)
+{
+ struct uart_8250_port *up = up_to_u8250p(p);
+
+ p->iotype = UPIO_AU;
+
+ p->serial_in = au_serial_in;
+ p->serial_out = au_serial_out;
+ up->dl_read = au_serial_dl_read;
+ up->dl_write = au_serial_dl_write;
+
+ p->mapsize = 0x100;
+
+ up->bugs |= UART_BUG_NOMSR;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rt288x_setup);
+
+#ifdef CONFIG_SERIAL_8250_CONSOLE
+static void au_putc(struct uart_port *port, unsigned char c)
+{
+ unsigned int status;
+
+ au_serial_out(port, UART_TX, c);
+
+ for (;;) {
+ status = au_serial_in(port, UART_LSR);
+ if (uart_lsr_tx_empty(status))
+ break;
+ cpu_relax();
+ }
+}
+
+static void au_early_serial8250_write(struct console *console,
+ const char *s, unsigned int count)
+{
+ struct earlycon_device *device = console->data;
+ struct uart_port *port = &device->port;
+
+ uart_console_write(port, s, count, au_putc);
+}
+
+static int __init early_au_setup(struct earlycon_device *dev, const char *opt)
+{
+ rt288x_setup(&dev->port);
+ dev->con->write = au_early_serial8250_write;
+
+ return 0;
+}
+OF_EARLYCON_DECLARE(palmchip, "ralink,rt2880-uart", early_au_setup);
+#endif
+
+MODULE_DESCRIPTION("RT288x/Au1xxx UART driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/tty/serial/8250/8250_uniphier.c b/drivers/tty/serial/8250/8250_uniphier.c
index a2978abab0db..a405155264b1 100644
--- a/drivers/tty/serial/8250/8250_uniphier.c
+++ b/drivers/tty/serial/8250/8250_uniphier.c
@@ -145,12 +145,12 @@ static void uniphier_serial_out(struct uart_port *p, int offset, int value)
* The divisor latch register exists at different address.
* Override dl_read/write callbacks.
*/
-static int uniphier_serial_dl_read(struct uart_8250_port *up)
+static u32 uniphier_serial_dl_read(struct uart_8250_port *up)
{
return readl(up->port.membase + UNIPHIER_UART_DLR);
}
-static void uniphier_serial_dl_write(struct uart_8250_port *up, int value)
+static void uniphier_serial_dl_write(struct uart_8250_port *up, u32 value)
{
writel(value, up->port.membase + UNIPHIER_UART_DLR);
}
diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig
index 5313aa31930f..cf33e858b0be 100644
--- a/drivers/tty/serial/8250/Kconfig
+++ b/drivers/tty/serial/8250/Kconfig
@@ -71,14 +71,16 @@ config SERIAL_8250_16550A_VARIANTS
console, you can say N here.
config SERIAL_8250_FINTEK
- bool "Support for Fintek F81216A LPC to 4 UART RS485 API"
+ bool "Support for Fintek variants"
depends on SERIAL_8250
help
- Selecting this option will add support for the RS485 capabilities
- of the Fintek F81216A LPC to 4 UART.
+ Selecting this option will add support for the RS232 and RS485
+ capabilities of the Fintek F81216A LPC to 4 UART as well similar
+ variants.
If this option is not selected the device will be configured as a
- standard 16550A serial port.
+ standard 16550A serial port, however the device may not function
+ correctly without this option enabled.
If unsure, say N.
diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile
index 4fc2fc1f41b6..628b75be312e 100644
--- a/drivers/tty/serial/8250/Makefile
+++ b/drivers/tty/serial/8250/Makefile
@@ -35,6 +35,7 @@ obj-$(CONFIG_SERIAL_8250_DW) += 8250_dw.o
obj-$(CONFIG_SERIAL_8250_EM) += 8250_em.o
obj-$(CONFIG_SERIAL_8250_IOC3) += 8250_ioc3.o
obj-$(CONFIG_SERIAL_8250_OMAP) += 8250_omap.o
+obj-$(CONFIG_SERIAL_8250_RT288X) += 8250_rt288x.o
obj-$(CONFIG_SERIAL_8250_LPC18XX) += 8250_lpc18xx.o
obj-$(CONFIG_SERIAL_8250_MT6577) += 8250_mtk.o
obj-$(CONFIG_SERIAL_8250_UNIPHIER) += 8250_uniphier.o
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index cd9afd9e3018..4f7ab4150ec5 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -3,7 +3,8 @@
# Makefile for the kernel serial device drivers.
#
-obj-$(CONFIG_SERIAL_CORE) += serial_core.o
+obj-$(CONFIG_SERIAL_CORE) += serial_base.o
+serial_base-y := serial_core.o serial_base_bus.o serial_ctrl.o serial_port.o
obj-$(CONFIG_SERIAL_EARLYCON) += earlycon.o
obj-$(CONFIG_SERIAL_EARLYCON_SEMIHOST) += earlycon-semihost.o
@@ -21,7 +22,7 @@ obj-$(CONFIG_SERIAL_SUNSAB) += sunsab.o
obj-$(CONFIG_SERIAL_21285) += 21285.o
# Now bring in any enabled 8250/16450/16550 type drivers.
-obj-$(CONFIG_SERIAL_8250) += 8250/
+obj-y += 8250/
obj-$(CONFIG_SERIAL_AMBA_PL010) += amba-pl010.o
obj-$(CONFIG_SERIAL_AMBA_PL011) += amba-pl011.o
diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c
index d8c2f3455eeb..c5c3f4674459 100644
--- a/drivers/tty/serial/amba-pl011.c
+++ b/drivers/tty/serial/amba-pl011.c
@@ -2166,6 +2166,13 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios,
* ----------^----------^----------^----------^-----
*/
pl011_write_lcr_h(uap, lcr_h);
+
+ /*
+ * Receive was disabled by pl011_disable_uart during shutdown.
+ * Need to reenable receive if you need to use a tty_driver
+ * returns from tty_find_polling_driver() after a port shutdown.
+ */
+ old_cr |= UART011_CR_RXE;
pl011_write(old_cr, uap, REG_CR);
spin_unlock_irqrestore(&port->lock, flags);
diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
index 9cd7479b03c0..6e9192f122aa 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -3006,14 +3006,13 @@ static int atmel_serial_remove(struct platform_device *pdev)
{
struct uart_port *port = platform_get_drvdata(pdev);
struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
- int ret = 0;
tasklet_kill(&atmel_port->tasklet_rx);
tasklet_kill(&atmel_port->tasklet_tx);
device_init_wakeup(&pdev->dev, 0);
- ret = uart_remove_one_port(&atmel_uart, port);
+ uart_remove_one_port(&atmel_uart, port);
kfree(atmel_port->rx_ring.buf);
@@ -3023,7 +3022,7 @@ static int atmel_serial_remove(struct platform_device *pdev)
pdev->dev.of_node = NULL;
- return ret;
+ return 0;
}
static SIMPLE_DEV_PM_OPS(atmel_serial_pm_ops, atmel_serial_suspend,
diff --git a/drivers/tty/serial/clps711x.c b/drivers/tty/serial/clps711x.c
index e190dce58f46..e49bc4019b50 100644
--- a/drivers/tty/serial/clps711x.c
+++ b/drivers/tty/serial/clps711x.c
@@ -514,7 +514,9 @@ static int uart_clps711x_remove(struct platform_device *pdev)
{
struct clps711x_port *s = platform_get_drvdata(pdev);
- return uart_remove_one_port(&clps711x_uart, &s->port);
+ uart_remove_one_port(&clps711x_uart, &s->port);
+
+ return 0;
}
static const struct of_device_id __maybe_unused clps711x_uart_dt_ids[] = {
diff --git a/drivers/tty/serial/cpm_uart/cpm_uart_core.c b/drivers/tty/serial/cpm_uart/cpm_uart_core.c
index 349e7da643f0..66afa9bea6bf 100644
--- a/drivers/tty/serial/cpm_uart/cpm_uart_core.c
+++ b/drivers/tty/serial/cpm_uart/cpm_uart_core.c
@@ -1431,7 +1431,10 @@ static int cpm_uart_probe(struct platform_device *ofdev)
static int cpm_uart_remove(struct platform_device *ofdev)
{
struct uart_cpm_port *pinfo = platform_get_drvdata(ofdev);
- return uart_remove_one_port(&cpm_reg, &pinfo->port);
+
+ uart_remove_one_port(&cpm_reg, &pinfo->port);
+
+ return 0;
}
static const struct of_device_id cpm_uart_match[] = {
diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index 7486a2b8556c..f2a47a8c5b85 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -238,6 +238,7 @@
/* Rx DMA timeout in ms, which is used to calculate Rx ring buffer size */
#define DMA_RX_TIMEOUT (10)
+#define DMA_RX_IDLE_CHARS 8
#define UART_AUTOSUSPEND_TIMEOUT 3000
#define DRIVER_NAME "fsl-lpuart"
@@ -282,6 +283,7 @@ struct lpuart_port {
struct scatterlist rx_sgl, tx_sgl[2];
struct circ_buf rx_ring;
int rx_dma_rng_buf_len;
+ int last_residue;
unsigned int dma_tx_nents;
wait_queue_head_t dma_wait;
bool is_cs7; /* Set to true when character size is 7 */
@@ -331,7 +333,7 @@ static struct lpuart_soc_data imx8qxp_data = {
.devtype = IMX8QXP_LPUART,
.iotype = UPIO_MEM32,
.reg_off = IMX_REG_OFF,
- .rx_watermark = 31,
+ .rx_watermark = 7, /* A lower watermark is ideal for low baud rates. */
};
static struct lpuart_soc_data imxrt1050_data = {
.devtype = IMXRT1050_LPUART,
@@ -1255,6 +1257,8 @@ static void lpuart_copy_rx_to_tty(struct lpuart_port *sport)
sport->port.icount.rx += copied;
}
+ sport->last_residue = state.residue;
+
exit:
dma_sync_sg_for_device(chan->device->dev, &sport->rx_sgl, 1,
DMA_FROM_DEVICE);
@@ -1272,11 +1276,43 @@ static void lpuart_dma_rx_complete(void *arg)
lpuart_copy_rx_to_tty(sport);
}
+/*
+ * Timer function to simulate the hardware EOP (End Of Package) event.
+ * The timer callback is to check for new RX data and copy to TTY buffer.
+ * If no new data are received since last interval, the EOP condition is
+ * met, complete the DMA transfer by copying the data. Otherwise, just
+ * restart timer.
+ */
static void lpuart_timer_func(struct timer_list *t)
{
struct lpuart_port *sport = from_timer(sport, t, lpuart_timer);
+ enum dma_status dmastat;
+ struct dma_chan *chan = sport->dma_rx_chan;
+ struct circ_buf *ring = &sport->rx_ring;
+ struct dma_tx_state state;
+ unsigned long flags;
+ int count;
- lpuart_copy_rx_to_tty(sport);
+ dmastat = dmaengine_tx_status(chan, sport->dma_rx_cookie, &state);
+ if (dmastat == DMA_ERROR) {
+ dev_err(sport->port.dev, "Rx DMA transfer failed!\n");
+ return;
+ }
+
+ ring->head = sport->rx_sgl.length - state.residue;
+ count = CIRC_CNT(ring->head, ring->tail, sport->rx_sgl.length);
+
+ /* Check if new data received before copying */
+ if ((count != 0) && (sport->last_residue == state.residue))
+ lpuart_copy_rx_to_tty(sport);
+ else
+ mod_timer(&sport->lpuart_timer,
+ jiffies + sport->dma_rx_timeout);
+
+ if (spin_trylock_irqsave(&sport->port.lock, flags)) {
+ sport->last_residue = state.residue;
+ spin_unlock_irqrestore(&sport->port.lock, flags);
+ }
}
static inline int lpuart_start_rx_dma(struct lpuart_port *sport)
@@ -1297,9 +1333,20 @@ static inline int lpuart_start_rx_dma(struct lpuart_port *sport)
*/
sport->rx_dma_rng_buf_len = (DMA_RX_TIMEOUT * baud / bits / 1000) * 2;
sport->rx_dma_rng_buf_len = (1 << fls(sport->rx_dma_rng_buf_len));
+ sport->rx_dma_rng_buf_len = max_t(int,
+ sport->rxfifo_size * 2,
+ sport->rx_dma_rng_buf_len);
+ /*
+ * Keep this condition check in case rxfifo_size is unavailable
+ * for some SoCs.
+ */
if (sport->rx_dma_rng_buf_len < 16)
sport->rx_dma_rng_buf_len = 16;
+ sport->last_residue = 0;
+ sport->dma_rx_timeout = max(nsecs_to_jiffies(
+ sport->port.frame_time * DMA_RX_IDLE_CHARS), 1UL);
+
ring->buf = kzalloc(sport->rx_dma_rng_buf_len, GFP_ATOMIC);
if (!ring->buf)
return -ENOMEM;
@@ -1689,12 +1736,13 @@ static void lpuart_rx_dma_startup(struct lpuart_port *sport)
if (!sport->dma_rx_chan)
goto err;
+ /* set default Rx DMA timeout */
+ sport->dma_rx_timeout = msecs_to_jiffies(DMA_RX_TIMEOUT);
+
ret = lpuart_start_rx_dma(sport);
if (ret)
goto err;
- /* set Rx DMA timeout */
- sport->dma_rx_timeout = msecs_to_jiffies(DMA_RX_TIMEOUT);
if (!sport->dma_rx_timeout)
sport->dma_rx_timeout = 1;
diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
index c5e17569c3ad..b2bf3cb449f4 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -2467,7 +2467,9 @@ static int imx_uart_remove(struct platform_device *pdev)
{
struct imx_port *sport = platform_get_drvdata(pdev);
- return uart_remove_one_port(&imx_uart_uart_driver, &sport->port);
+ uart_remove_one_port(&imx_uart_uart_driver, &sport->port);
+
+ return 0;
}
static void imx_uart_restore_context(struct imx_port *sport)
diff --git a/drivers/tty/serial/lantiq.c b/drivers/tty/serial/lantiq.c
index a58e9277dfad..d413f97f7190 100644
--- a/drivers/tty/serial/lantiq.c
+++ b/drivers/tty/serial/lantiq.c
@@ -889,7 +889,9 @@ static int lqasc_remove(struct platform_device *pdev)
{
struct uart_port *port = platform_get_drvdata(pdev);
- return uart_remove_one_port(&lqasc_reg, port);
+ uart_remove_one_port(&lqasc_reg, port);
+
+ return 0;
}
static const struct ltq_soc_data soc_data_lantiq = {
diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c
index 9fee722058f4..997e39449766 100644
--- a/drivers/tty/serial/max310x.c
+++ b/drivers/tty/serial/max310x.c
@@ -1636,7 +1636,7 @@ static struct i2c_driver max310x_i2c_driver = {
.of_match_table = max310x_dt_ids,
.pm = &max310x_pm_ops,
},
- .probe_new = max310x_i2c_probe,
+ .probe = max310x_i2c_probe,
.remove = max310x_i2c_remove,
};
#endif
diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c
index abad091baeea..2e7e7c409cf2 100644
--- a/drivers/tty/serial/sc16is7xx.c
+++ b/drivers/tty/serial/sc16is7xx.c
@@ -1709,7 +1709,7 @@ static struct i2c_driver sc16is7xx_i2c_uart_driver = {
.name = SC16IS7XX_NAME,
.of_match_table = sc16is7xx_dt_ids,
},
- .probe_new = sc16is7xx_i2c_probe,
+ .probe = sc16is7xx_i2c_probe,
.remove = sc16is7xx_i2c_remove,
.id_table = sc16is7xx_i2c_id_table,
};
diff --git a/drivers/tty/serial/serial_base.h b/drivers/tty/serial/serial_base.h
new file mode 100644
index 000000000000..9faac0ff6b89
--- /dev/null
+++ b/drivers/tty/serial/serial_base.h
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Serial core related functions, serial port device drivers do not need this.
+ *
+ * Copyright (C) 2023 Texas Instruments Incorporated - https://www.ti.com/
+ * Author: Tony Lindgren <[email protected]>
+ */
+
+#define to_serial_base_ctrl_device(d) container_of((d), struct serial_ctrl_device, dev)
+#define to_serial_base_port_device(d) container_of((d), struct serial_port_device, dev)
+
+struct uart_driver;
+struct uart_port;
+struct device_driver;
+struct device;
+
+struct serial_ctrl_device {
+ struct device dev;
+};
+
+struct serial_port_device {
+ struct device dev;
+ struct uart_port *port;
+};
+
+int serial_base_ctrl_init(void);
+void serial_base_ctrl_exit(void);
+
+int serial_base_port_init(void);
+void serial_base_port_exit(void);
+
+int serial_base_driver_register(struct device_driver *driver);
+void serial_base_driver_unregister(struct device_driver *driver);
+
+struct serial_ctrl_device *serial_base_ctrl_add(struct uart_port *port,
+ struct device *parent);
+struct serial_port_device *serial_base_port_add(struct uart_port *port,
+ struct serial_ctrl_device *parent);
+void serial_base_ctrl_device_remove(struct serial_ctrl_device *ctrl_dev);
+void serial_base_port_device_remove(struct serial_port_device *port_dev);
+
+int serial_ctrl_register_port(struct uart_driver *drv, struct uart_port *port);
+void serial_ctrl_unregister_port(struct uart_driver *drv, struct uart_port *port);
+
+int serial_core_register_port(struct uart_driver *drv, struct uart_port *port);
+void serial_core_unregister_port(struct uart_driver *drv, struct uart_port *port);
diff --git a/drivers/tty/serial/serial_base_bus.c b/drivers/tty/serial/serial_base_bus.c
new file mode 100644
index 000000000000..9354af7c11af
--- /dev/null
+++ b/drivers/tty/serial/serial_base_bus.c
@@ -0,0 +1,209 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Serial base bus layer for controllers
+ *
+ * Copyright (C) 2023 Texas Instruments Incorporated - https://www.ti.com/
+ * Author: Tony Lindgren <[email protected]>
+ *
+ * The serial core bus manages the serial core controller instances.
+ */
+
+#include <linux/container_of.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/serial_core.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include "serial_base.h"
+
+static bool serial_base_initialized;
+
+static int serial_base_match(struct device *dev, struct device_driver *drv)
+{
+ int len = strlen(drv->name);
+
+ return !strncmp(dev_name(dev), drv->name, len);
+}
+
+static struct bus_type serial_base_bus_type = {
+ .name = "serial-base",
+ .match = serial_base_match,
+};
+
+int serial_base_driver_register(struct device_driver *driver)
+{
+ driver->bus = &serial_base_bus_type;
+
+ return driver_register(driver);
+}
+
+void serial_base_driver_unregister(struct device_driver *driver)
+{
+ driver_unregister(driver);
+}
+
+static int serial_base_device_init(struct uart_port *port,
+ struct device *dev,
+ struct device *parent_dev,
+ const struct device_type *type,
+ void (*release)(struct device *dev),
+ int id)
+{
+ if (!serial_base_initialized) {
+ dev_err(port->dev, "uart_add_one_port() called before arch_initcall()?\n");
+ return -EPROBE_DEFER;
+ }
+
+ device_initialize(dev);
+ dev->type = type;
+ dev->parent = parent_dev;
+ dev->bus = &serial_base_bus_type;
+ dev->release = release;
+
+ return dev_set_name(dev, "%s.%s.%d", type->name, dev_name(port->dev), id);
+}
+
+static const struct device_type serial_ctrl_type = {
+ .name = "ctrl",
+};
+
+static void serial_base_ctrl_release(struct device *dev)
+{
+ struct serial_ctrl_device *ctrl_dev = to_serial_base_ctrl_device(dev);
+
+ kfree(ctrl_dev);
+}
+
+void serial_base_ctrl_device_remove(struct serial_ctrl_device *ctrl_dev)
+{
+ if (!ctrl_dev)
+ return;
+
+ device_del(&ctrl_dev->dev);
+}
+
+struct serial_ctrl_device *serial_base_ctrl_add(struct uart_port *port,
+ struct device *parent)
+{
+ struct serial_ctrl_device *ctrl_dev;
+ int err;
+
+ ctrl_dev = kzalloc(sizeof(*ctrl_dev), GFP_KERNEL);
+ if (!ctrl_dev)
+ return ERR_PTR(-ENOMEM);
+
+ err = serial_base_device_init(port, &ctrl_dev->dev,
+ parent, &serial_ctrl_type,
+ serial_base_ctrl_release,
+ port->ctrl_id);
+ if (err)
+ goto err_free_ctrl_dev;
+
+ err = device_add(&ctrl_dev->dev);
+ if (err)
+ goto err_put_device;
+
+ return ctrl_dev;
+
+err_put_device:
+ put_device(&ctrl_dev->dev);
+err_free_ctrl_dev:
+ kfree(ctrl_dev);
+
+ return ERR_PTR(err);
+}
+
+static const struct device_type serial_port_type = {
+ .name = "port",
+};
+
+static void serial_base_port_release(struct device *dev)
+{
+ struct serial_port_device *port_dev = to_serial_base_port_device(dev);
+
+ kfree(port_dev);
+}
+
+struct serial_port_device *serial_base_port_add(struct uart_port *port,
+ struct serial_ctrl_device *ctrl_dev)
+{
+ struct serial_port_device *port_dev;
+ int err;
+
+ port_dev = kzalloc(sizeof(*port_dev), GFP_KERNEL);
+ if (!port_dev)
+ return ERR_PTR(-ENOMEM);
+
+ err = serial_base_device_init(port, &port_dev->dev,
+ &ctrl_dev->dev, &serial_port_type,
+ serial_base_port_release,
+ port->line);
+ if (err)
+ goto err_free_port_dev;
+
+ port_dev->port = port;
+
+ err = device_add(&port_dev->dev);
+ if (err)
+ goto err_put_device;
+
+ return port_dev;
+
+err_put_device:
+ put_device(&port_dev->dev);
+err_free_port_dev:
+ kfree(port_dev);
+
+ return ERR_PTR(err);
+}
+
+void serial_base_port_device_remove(struct serial_port_device *port_dev)
+{
+ if (!port_dev)
+ return;
+
+ device_del(&port_dev->dev);
+}
+
+static int serial_base_init(void)
+{
+ int ret;
+
+ ret = bus_register(&serial_base_bus_type);
+ if (ret)
+ return ret;
+
+ ret = serial_base_ctrl_init();
+ if (ret)
+ goto err_bus_unregister;
+
+ ret = serial_base_port_init();
+ if (ret)
+ goto err_ctrl_exit;
+
+ serial_base_initialized = true;
+
+ return 0;
+
+err_ctrl_exit:
+ serial_base_ctrl_exit();
+
+err_bus_unregister:
+ bus_unregister(&serial_base_bus_type);
+
+ return ret;
+}
+arch_initcall(serial_base_init);
+
+static void serial_base_exit(void)
+{
+ serial_base_port_exit();
+ serial_base_ctrl_exit();
+ bus_unregister(&serial_base_bus_type);
+}
+module_exit(serial_base_exit);
+
+MODULE_AUTHOR("Tony Lindgren <[email protected]>");
+MODULE_DESCRIPTION("Serial core bus");
+MODULE_LICENSE("GPL");
diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index 54e82f476a2c..831d033611e6 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -17,6 +17,7 @@
#include <linux/gpio/consumer.h>
#include <linux/kernel.h>
#include <linux/of.h>
+#include <linux/pm_runtime.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/device.h>
@@ -31,6 +32,8 @@
#include <linux/irq.h>
#include <linux/uaccess.h>
+#include "serial_base.h"
+
/*
* This is used to lock changes in serial line configuration.
*/
@@ -134,9 +137,30 @@ static void __uart_start(struct tty_struct *tty)
{
struct uart_state *state = tty->driver_data;
struct uart_port *port = state->uart_port;
+ struct serial_port_device *port_dev;
+ int err;
+
+ if (!port || port->flags & UPF_DEAD || uart_tx_stopped(port))
+ return;
+
+ port_dev = port->port_dev;
+
+ /* Increment the runtime PM usage count for the active check below */
+ err = pm_runtime_get(&port_dev->dev);
+ if (err < 0) {
+ pm_runtime_put_noidle(&port_dev->dev);
+ return;
+ }
- if (port && !(port->flags & UPF_DEAD) && !uart_tx_stopped(port))
+ /*
+ * Start TX if enabled, and kick runtime PM. If the device is not
+ * enabled, serial_port_runtime_resume() calls start_tx() again
+ * after enabling the device.
+ */
+ if (pm_runtime_active(&port_dev->dev))
port->ops->start_tx(port);
+ pm_runtime_mark_last_busy(&port_dev->dev);
+ pm_runtime_put_autosuspend(&port_dev->dev);
}
static void uart_start(struct tty_struct *tty)
@@ -2333,8 +2357,11 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *uport)
* able to Re-start_rx later.
*/
if (!console_suspend_enabled && uart_console(uport)) {
- if (uport->ops->start_rx)
+ if (uport->ops->start_rx) {
+ spin_lock_irq(&uport->lock);
uport->ops->stop_rx(uport);
+ spin_unlock_irq(&uport->lock);
+ }
goto unlock;
}
@@ -2427,8 +2454,11 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport)
if (console_suspend_enabled)
uart_change_pm(state, UART_PM_STATE_ON);
uport->ops->set_termios(uport, &termios, NULL);
- if (!console_suspend_enabled && uport->ops->start_rx)
+ if (!console_suspend_enabled && uport->ops->start_rx) {
+ spin_lock_irq(&uport->lock);
uport->ops->start_rx(uport);
+ spin_unlock_irq(&uport->lock);
+ }
if (console_suspend_enabled)
console_start(uport->cons);
}
@@ -3042,7 +3072,7 @@ static const struct attribute_group tty_dev_attr_group = {
};
/**
- * uart_add_one_port - attach a driver-defined port structure
+ * serial_core_add_one_port - attach a driver-defined port structure
* @drv: pointer to the uart low level driver structure for this port
* @uport: uart port structure to use for this port.
*
@@ -3051,8 +3081,9 @@ static const struct attribute_group tty_dev_attr_group = {
* This allows the driver @drv to register its own uart_port structure with the
* core driver. The main purpose is to allow the low level uart drivers to
* expand uart_port, rather than having yet more levels of structures.
+ * Caller must hold port_mutex.
*/
-int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport)
+static int serial_core_add_one_port(struct uart_driver *drv, struct uart_port *uport)
{
struct uart_state *state;
struct tty_port *port;
@@ -3066,7 +3097,6 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport)
state = drv->state + uport->line;
port = &state->port;
- mutex_lock(&port_mutex);
mutex_lock(&port->mutex);
if (state->uart_port) {
ret = -EINVAL;
@@ -3131,21 +3161,14 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport)
uport->line);
}
- /*
- * Ensure UPF_DEAD is not set.
- */
- uport->flags &= ~UPF_DEAD;
-
out:
mutex_unlock(&port->mutex);
- mutex_unlock(&port_mutex);
return ret;
}
-EXPORT_SYMBOL(uart_add_one_port);
/**
- * uart_remove_one_port - detach a driver defined port structure
+ * serial_core_remove_one_port - detach a driver defined port structure
* @drv: pointer to the uart low level driver structure for this port
* @uport: uart port structure for this port
*
@@ -3153,21 +3176,16 @@ EXPORT_SYMBOL(uart_add_one_port);
*
* This unhooks (and hangs up) the specified port structure from the core
* driver. No further calls will be made to the low-level code for this port.
+ * Caller must hold port_mutex.
*/
-int uart_remove_one_port(struct uart_driver *drv, struct uart_port *uport)
+static void serial_core_remove_one_port(struct uart_driver *drv,
+ struct uart_port *uport)
{
struct uart_state *state = drv->state + uport->line;
struct tty_port *port = &state->port;
struct uart_port *uart_port;
struct tty_struct *tty;
- int ret = 0;
-
- mutex_lock(&port_mutex);
- /*
- * Mark the port "dead" - this prevents any opens from
- * succeeding while we shut down the port.
- */
mutex_lock(&port->mutex);
uart_port = uart_port_check(state);
if (uart_port != uport)
@@ -3176,10 +3194,8 @@ int uart_remove_one_port(struct uart_driver *drv, struct uart_port *uport)
if (!uart_port) {
mutex_unlock(&port->mutex);
- ret = -EINVAL;
- goto out;
+ return;
}
- uport->flags |= UPF_DEAD;
mutex_unlock(&port->mutex);
/*
@@ -3211,18 +3227,14 @@ int uart_remove_one_port(struct uart_driver *drv, struct uart_port *uport)
* Indicate that there isn't a port here anymore.
*/
uport->type = PORT_UNKNOWN;
+ uport->port_dev = NULL;
mutex_lock(&port->mutex);
WARN_ON(atomic_dec_return(&state->refcount) < 0);
wait_event(state->remove_wait, !atomic_read(&state->refcount));
state->uart_port = NULL;
mutex_unlock(&port->mutex);
-out:
- mutex_unlock(&port_mutex);
-
- return ret;
}
-EXPORT_SYMBOL(uart_remove_one_port);
/**
* uart_match_port - are the two ports equivalent?
@@ -3257,6 +3269,144 @@ bool uart_match_port(const struct uart_port *port1,
}
EXPORT_SYMBOL(uart_match_port);
+static struct serial_ctrl_device *
+serial_core_get_ctrl_dev(struct serial_port_device *port_dev)
+{
+ struct device *dev = &port_dev->dev;
+
+ return to_serial_base_ctrl_device(dev->parent);
+}
+
+/*
+ * Find a registered serial core controller device if one exists. Returns
+ * the first device matching the ctrl_id. Caller must hold port_mutex.
+ */
+static struct serial_ctrl_device *serial_core_ctrl_find(struct uart_driver *drv,
+ struct device *phys_dev,
+ int ctrl_id)
+{
+ struct uart_state *state;
+ int i;
+
+ lockdep_assert_held(&port_mutex);
+
+ for (i = 0; i < drv->nr; i++) {
+ state = drv->state + i;
+ if (!state->uart_port || !state->uart_port->port_dev)
+ continue;
+
+ if (state->uart_port->dev == phys_dev &&
+ state->uart_port->ctrl_id == ctrl_id)
+ return serial_core_get_ctrl_dev(state->uart_port->port_dev);
+ }
+
+ return NULL;
+}
+
+static struct serial_ctrl_device *serial_core_ctrl_device_add(struct uart_port *port)
+{
+ return serial_base_ctrl_add(port, port->dev);
+}
+
+static int serial_core_port_device_add(struct serial_ctrl_device *ctrl_dev,
+ struct uart_port *port)
+{
+ struct serial_port_device *port_dev;
+
+ port_dev = serial_base_port_add(port, ctrl_dev);
+ if (IS_ERR(port_dev))
+ return PTR_ERR(port_dev);
+
+ port->port_dev = port_dev;
+
+ return 0;
+}
+
+/*
+ * Initialize a serial core port device, and a controller device if needed.
+ */
+int serial_core_register_port(struct uart_driver *drv, struct uart_port *port)
+{
+ struct serial_ctrl_device *ctrl_dev, *new_ctrl_dev = NULL;
+ int ret;
+
+ mutex_lock(&port_mutex);
+
+ /*
+ * Prevent serial_port_runtime_resume() from trying to use the port
+ * until serial_core_add_one_port() has completed
+ */
+ port->flags |= UPF_DEAD;
+
+ /* Inititalize a serial core controller device if needed */
+ ctrl_dev = serial_core_ctrl_find(drv, port->dev, port->ctrl_id);
+ if (!ctrl_dev) {
+ new_ctrl_dev = serial_core_ctrl_device_add(port);
+ if (IS_ERR(new_ctrl_dev)) {
+ ret = PTR_ERR(new_ctrl_dev);
+ goto err_unlock;
+ }
+ ctrl_dev = new_ctrl_dev;
+ }
+
+ /*
+ * Initialize a serial core port device. Tag the port dead to prevent
+ * serial_port_runtime_resume() trying to do anything until port has
+ * been registered. It gets cleared by serial_core_add_one_port().
+ */
+ ret = serial_core_port_device_add(ctrl_dev, port);
+ if (ret)
+ goto err_unregister_ctrl_dev;
+
+ ret = serial_core_add_one_port(drv, port);
+ if (ret)
+ goto err_unregister_port_dev;
+
+ port->flags &= ~UPF_DEAD;
+
+ mutex_unlock(&port_mutex);
+
+ return 0;
+
+err_unregister_port_dev:
+ serial_base_port_device_remove(port->port_dev);
+
+err_unregister_ctrl_dev:
+ serial_base_ctrl_device_remove(new_ctrl_dev);
+
+err_unlock:
+ mutex_unlock(&port_mutex);
+
+ return ret;
+}
+
+/*
+ * Removes a serial core port device, and the related serial core controller
+ * device if the last instance.
+ */
+void serial_core_unregister_port(struct uart_driver *drv, struct uart_port *port)
+{
+ struct device *phys_dev = port->dev;
+ struct serial_port_device *port_dev = port->port_dev;
+ struct serial_ctrl_device *ctrl_dev = serial_core_get_ctrl_dev(port_dev);
+ int ctrl_id = port->ctrl_id;
+
+ mutex_lock(&port_mutex);
+
+ port->flags |= UPF_DEAD;
+
+ serial_core_remove_one_port(drv, port);
+
+ /* Note that struct uart_port *port is no longer valid at this point */
+ serial_base_port_device_remove(port_dev);
+
+ /* Drop the serial core controller device if no ports are using it */
+ if (!serial_core_ctrl_find(drv, phys_dev, ctrl_id))
+ serial_base_ctrl_device_remove(ctrl_dev);
+
+ mutex_unlock(&port_mutex);
+}
+
/**
* uart_handle_dcd_change - handle a change of carrier detect state
* @uport: uart_port structure for the open port
diff --git a/drivers/tty/serial/serial_ctrl.c b/drivers/tty/serial/serial_ctrl.c
new file mode 100644
index 000000000000..6fcf634425dc
--- /dev/null
+++ b/drivers/tty/serial/serial_ctrl.c
@@ -0,0 +1,68 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Serial core controller driver
+ *
+ * Copyright (C) 2023 Texas Instruments Incorporated - https://www.ti.com/
+ * Author: Tony Lindgren <[email protected]>
+ *
+ * This driver manages the serial core controller struct device instances.
+ * The serial core controller devices are children of the physical serial
+ * port device.
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/serial_core.h>
+#include <linux/spinlock.h>
+
+#include "serial_base.h"
+
+static int serial_ctrl_probe(struct device *dev)
+{
+ pm_runtime_enable(dev);
+
+ return 0;
+}
+
+static int serial_ctrl_remove(struct device *dev)
+{
+ pm_runtime_disable(dev);
+
+ return 0;
+}
+
+/*
+ * Serial core controller device init functions. Note that the physical
+ * serial port device driver may not have completed probe at this point.
+ */
+int serial_ctrl_register_port(struct uart_driver *drv, struct uart_port *port)
+{
+ return serial_core_register_port(drv, port);
+}
+
+void serial_ctrl_unregister_port(struct uart_driver *drv, struct uart_port *port)
+{
+ serial_core_unregister_port(drv, port);
+}
+
+static struct device_driver serial_ctrl_driver = {
+ .name = "ctrl",
+ .suppress_bind_attrs = true,
+ .probe = serial_ctrl_probe,
+ .remove = serial_ctrl_remove,
+};
+
+int serial_base_ctrl_init(void)
+{
+ return serial_base_driver_register(&serial_ctrl_driver);
+}
+
+void serial_base_ctrl_exit(void)
+{
+ serial_base_driver_unregister(&serial_ctrl_driver);
+}
+
+MODULE_AUTHOR("Tony Lindgren <[email protected]>");
+MODULE_DESCRIPTION("Serial core controller driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/tty/serial/serial_port.c b/drivers/tty/serial/serial_port.c
new file mode 100644
index 000000000000..862423237007
--- /dev/null
+++ b/drivers/tty/serial/serial_port.c
@@ -0,0 +1,105 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Serial core port device driver
+ *
+ * Copyright (C) 2023 Texas Instruments Incorporated - https://www.ti.com/
+ * Author: Tony Lindgren <[email protected]>
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/serial_core.h>
+#include <linux/spinlock.h>
+
+#include "serial_base.h"
+
+#define SERIAL_PORT_AUTOSUSPEND_DELAY_MS 500
+
+/* Only considers pending TX for now. Caller must take care of locking */
+static int __serial_port_busy(struct uart_port *port)
+{
+ return !uart_tx_stopped(port) &&
+ uart_circ_chars_pending(&port->state->xmit);
+}
+
+static int serial_port_runtime_resume(struct device *dev)
+{
+ struct serial_port_device *port_dev = to_serial_base_port_device(dev);
+ struct uart_port *port;
+ unsigned long flags;
+
+ port = port_dev->port;
+
+ if (port->flags & UPF_DEAD)
+ goto out;
+
+ /* Flush any pending TX for the port */
+ spin_lock_irqsave(&port->lock, flags);
+ if (__serial_port_busy(port))
+ port->ops->start_tx(port);
+ spin_unlock_irqrestore(&port->lock, flags);
+
+out:
+ pm_runtime_mark_last_busy(dev);
+
+ return 0;
+}
+
+static DEFINE_RUNTIME_DEV_PM_OPS(serial_port_pm,
+ NULL, serial_port_runtime_resume, NULL);
+
+static int serial_port_probe(struct device *dev)
+{
+ pm_runtime_enable(dev);
+ pm_runtime_set_autosuspend_delay(dev, SERIAL_PORT_AUTOSUSPEND_DELAY_MS);
+ pm_runtime_use_autosuspend(dev);
+
+ return 0;
+}
+
+static int serial_port_remove(struct device *dev)
+{
+ pm_runtime_dont_use_autosuspend(dev);
+ pm_runtime_disable(dev);
+
+ return 0;
+}
+
+/*
+ * Serial core port device init functions. Note that the physical serial
+ * port device driver may not have completed probe at this point.
+ */
+int uart_add_one_port(struct uart_driver *drv, struct uart_port *port)
+{
+ return serial_ctrl_register_port(drv, port);
+}
+EXPORT_SYMBOL(uart_add_one_port);
+
+void uart_remove_one_port(struct uart_driver *drv, struct uart_port *port)
+{
+ serial_ctrl_unregister_port(drv, port);
+}
+EXPORT_SYMBOL(uart_remove_one_port);
+
+static struct device_driver serial_port_driver = {
+ .name = "port",
+ .suppress_bind_attrs = true,
+ .probe = serial_port_probe,
+ .remove = serial_port_remove,
+ .pm = pm_ptr(&serial_port_pm),
+};
+
+int serial_base_port_init(void)
+{
+ return serial_base_driver_register(&serial_port_driver);
+}
+
+void serial_base_port_exit(void)
+{
+ serial_base_driver_unregister(&serial_port_driver);
+}
+
+MODULE_AUTHOR("Tony Lindgren <[email protected]>");
+MODULE_DESCRIPTION("Serial controller port driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/tty/serial/st-asc.c b/drivers/tty/serial/st-asc.c
index 5215e6910f68..dc2f2051435c 100644
--- a/drivers/tty/serial/st-asc.c
+++ b/drivers/tty/serial/st-asc.c
@@ -796,7 +796,9 @@ static int asc_serial_remove(struct platform_device *pdev)
{
struct uart_port *port = platform_get_drvdata(pdev);
- return uart_remove_one_port(&asc_uart_driver, port);
+ uart_remove_one_port(&asc_uart_driver, port);
+
+ return 0;
}
#ifdef CONFIG_PM_SLEEP
diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c
index 1e38fc9b10c1..e9e11a259621 100644
--- a/drivers/tty/serial/stm32-usart.c
+++ b/drivers/tty/serial/stm32-usart.c
@@ -1755,13 +1755,10 @@ static int stm32_usart_serial_remove(struct platform_device *pdev)
struct uart_port *port = platform_get_drvdata(pdev);
struct stm32_port *stm32_port = to_stm32_port(port);
const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
- int err;
u32 cr3;
pm_runtime_get_sync(&pdev->dev);
- err = uart_remove_one_port(&stm32_usart_driver, port);
- if (err)
- return(err);
+ uart_remove_one_port(&stm32_usart_driver, port);
pm_runtime_disable(&pdev->dev);
pm_runtime_set_suspended(&pdev->dev);
diff --git a/drivers/tty/serial/uartlite.c b/drivers/tty/serial/uartlite.c
index 94584e54ebbe..679574893ebe 100644
--- a/drivers/tty/serial/uartlite.c
+++ b/drivers/tty/serial/uartlite.c
@@ -685,18 +685,15 @@ static int ulite_assign(struct device *dev, int id, phys_addr_t base, int irq,
*
* @dev: pointer to device structure
*/
-static int ulite_release(struct device *dev)
+static void ulite_release(struct device *dev)
{
struct uart_port *port = dev_get_drvdata(dev);
- int rc = 0;
if (port) {
- rc = uart_remove_one_port(&ulite_uart_driver, port);
+ uart_remove_one_port(&ulite_uart_driver, port);
dev_set_drvdata(dev, NULL);
port->mapbase = 0;
}
-
- return rc;
}
/**
@@ -900,14 +897,13 @@ static int ulite_remove(struct platform_device *pdev)
{
struct uart_port *port = dev_get_drvdata(&pdev->dev);
struct uartlite_data *pdata = port->private_data;
- int rc;
clk_disable_unprepare(pdata->clk);
- rc = ulite_release(&pdev->dev);
+ ulite_release(&pdev->dev);
pm_runtime_disable(&pdev->dev);
pm_runtime_set_suspended(&pdev->dev);
pm_runtime_dont_use_autosuspend(&pdev->dev);
- return rc;
+ return 0;
}
/* work with hotplug and coldplug */
diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c
index 8e521c69a959..20a751663ef9 100644
--- a/drivers/tty/serial/xilinx_uartps.c
+++ b/drivers/tty/serial/xilinx_uartps.c
@@ -1670,14 +1670,13 @@ static int cdns_uart_remove(struct platform_device *pdev)
{
struct uart_port *port = platform_get_drvdata(pdev);
struct cdns_uart *cdns_uart_data = port->private_data;
- int rc;
/* Remove the cdns_uart port from the serial core */
#ifdef CONFIG_COMMON_CLK
clk_notifier_unregister(cdns_uart_data->uartclk,
&cdns_uart_data->clk_rate_change_nb);
#endif
- rc = uart_remove_one_port(cdns_uart_data->cdns_uart_driver, port);
+ uart_remove_one_port(cdns_uart_data->cdns_uart_driver, port);
port->mapbase = 0;
clk_disable_unprepare(cdns_uart_data->uartclk);
clk_disable_unprepare(cdns_uart_data->pclk);
@@ -1693,7 +1692,7 @@ static int cdns_uart_remove(struct platform_device *pdev)
if (!--instances)
uart_unregister_driver(cdns_uart_data->cdns_uart_driver);
- return rc;
+ return 0;
}
static struct platform_driver cdns_uart_platform_driver = {
diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h
index 6f78f302d272..eb44420b39ec 100644
--- a/include/linux/serial_8250.h
+++ b/include/linux/serial_8250.h
@@ -7,17 +7,34 @@
#ifndef _LINUX_SERIAL_8250_H
#define _LINUX_SERIAL_8250_H
+#include <linux/errno.h>
#include <linux/serial_core.h>
#include <linux/serial_reg.h>
#include <linux/platform_device.h>
+struct uart_8250_port;
+
/*
* This is the platform device platform_data structure
+ *
+ * @mapsize: Port size for ioremap()
+ * @bugs: Port bugs
+ *
+ * @dl_read: ``u32 ()(struct uart_8250_port *up)``
+ *
+ * UART divisor latch read.
+ *
+ * @dl_write: ``void ()(struct uart_8250_port *up, u32 value)``
+ *
+ * Write @value into UART divisor latch.
+ *
+ * Locking: Caller holds port's lock.
*/
struct plat_serial8250_port {
unsigned long iobase; /* io base address */
void __iomem *membase; /* ioremap cookie or NULL */
resource_size_t mapbase; /* resource base */
+ resource_size_t mapsize;
unsigned int uartclk; /* UART clock rate */
unsigned int irq; /* interrupt number */
unsigned long irqflags; /* request_irq flags */
@@ -28,8 +45,11 @@ struct plat_serial8250_port {
unsigned char has_sysrq; /* supports magic SysRq */
unsigned int type; /* If UPF_FIXED_TYPE */
upf_t flags; /* UPF_* flags */
+ u16 bugs; /* port bugs */
unsigned int (*serial_in)(struct uart_port *, int);
void (*serial_out)(struct uart_port *, int, int);
+ u32 (*dl_read)(struct uart_8250_port *up);
+ void (*dl_write)(struct uart_8250_port *up, u32 value);
void (*set_termios)(struct uart_port *,
struct ktermios *new,
const struct ktermios *old);
@@ -90,14 +110,23 @@ struct uart_8250_em485 {
* their own 8250 ports without registering their own
* platform device. Using these will make your driver
* dependent on the 8250 driver.
+ *
+ * @dl_read: ``u32 ()(struct uart_8250_port *port)``
+ *
+ * UART divisor latch read.
+ *
+ * @dl_write: ``void ()(struct uart_8250_port *port, u32 value)``
+ *
+ * Write @value into UART divisor latch.
+ *
+ * Locking: Caller holds port's lock.
*/
-
struct uart_8250_port {
struct uart_port port;
struct timer_list timer; /* "no irq" timer */
struct list_head list; /* ports on this IRQ */
u32 capabilities; /* port capabilities */
- unsigned short bugs; /* port bugs */
+ u16 bugs; /* port bugs */
bool fifo_bug; /* min RX trigger if enabled */
unsigned int tx_loadsz; /* transmit fifo load size */
unsigned char acr;
@@ -129,8 +158,8 @@ struct uart_8250_port {
const struct uart_8250_ops *ops;
/* 8250 specific callbacks */
- int (*dl_read)(struct uart_8250_port *);
- void (*dl_write)(struct uart_8250_port *, int);
+ u32 (*dl_read)(struct uart_8250_port *up);
+ void (*dl_write)(struct uart_8250_port *up, u32 value);
struct uart_8250_em485 *em485;
void (*rs485_start_tx)(struct uart_8250_port *);
@@ -183,8 +212,11 @@ void serial8250_set_isa_configurator(void (*v)(int port, struct uart_port *up,
u32 *capabilities));
#ifdef CONFIG_SERIAL_8250_RT288X
-unsigned int au_serial_in(struct uart_port *p, int offset);
-void au_serial_out(struct uart_port *p, int offset, int value);
+int rt288x_setup(struct uart_port *p);
+int au_platform_setup(struct plat_serial8250_port *p);
+#else
+static inline int rt288x_setup(struct uart_port *p) { return -ENODEV; }
+static inline int au_platform_setup(struct plat_serial8250_port *p) { return -ENODEV; }
#endif
#endif
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index 66ecec15a1bf..6d58c57acdaa 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -28,6 +28,7 @@
struct uart_port;
struct serial_struct;
+struct serial_port_device;
struct device;
struct gpio_desc;
@@ -458,6 +459,7 @@ struct uart_port {
struct serial_rs485 *rs485);
int (*iso7816_config)(struct uart_port *,
struct serial_iso7816 *iso7816);
+ int ctrl_id; /* optional serial core controller id */
unsigned int irq; /* irq number */
unsigned long irqflags; /* irq flags */
unsigned int uartclk; /* base uart clock */
@@ -563,7 +565,8 @@ struct uart_port {
unsigned int minor;
resource_size_t mapbase; /* for ioremap */
resource_size_t mapsize;
- struct device *dev; /* parent device */
+ struct device *dev; /* serial port physical parent device */
+ struct serial_port_device *port_dev; /* serial core port device */
unsigned long sysrq; /* sysrq timeout */
unsigned int sysrq_ch; /* char for sysrq */
@@ -853,7 +856,7 @@ void uart_console_write(struct uart_port *port, const char *s,
int uart_register_driver(struct uart_driver *uart);
void uart_unregister_driver(struct uart_driver *uart);
int uart_add_one_port(struct uart_driver *reg, struct uart_port *port);
-int uart_remove_one_port(struct uart_driver *reg, struct uart_port *port);
+void uart_remove_one_port(struct uart_driver *reg, struct uart_port *port);
bool uart_match_port(const struct uart_port *port1,
const struct uart_port *port2);