diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2018-06-06 02:55:56 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-06-06 02:55:56 +0300 |
commit | a22e48cf317d22a4326dc19e906e6b5f4f92e94e (patch) | |
tree | e3573ec1f96c50e52efe8717d9a99f249c4a65cf /drivers/tty | |
parent | ec064d3c6b40697fd72f4b1eeabbf293b7947a04 (diff) | |
parent | 4b4ecd9cb853c14913a3726cfcc60ccda1d2924a (diff) | |
download | linux-a22e48cf317d22a4326dc19e906e6b5f4f92e94e.tar.xz |
Merge tag 'tty-4.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty
Pull tty/serial updates from Greg KH:
"Here is the big tty/serial driver update for 4.18-rc1.
There's nothing major here, just lots of serial driver updates. Full
details are in the shortlog, nothing anything specific to call out
here.
All have been in linux-next for a while with no reported issues"
* tag 'tty-4.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty: (55 commits)
vt: Perform safe console erase only once
serial: imx: disable UCR4_OREN on shutdown
serial: imx: drop CTS/RTS handling from shutdown
tty: fix typo in ASYNCB_FOURPORT comment
serial: samsung: check DMA engine capabilities before using DMA mode
tty: Fix data race in tty_insert_flip_string_fixed_flag
tty: serial: msm_geni_serial: Fix TX infinite loop
serial: 8250_dw: Fix runtime PM handling
serial: 8250: omap: Fix idling of clocks for unused uarts
tty: serial: drop ATH79 specific SoC symbols
serial: 8250: Add missing rxtrig_bytes on Altera 16550 UART
serial/aspeed-vuart: fix a couple mod_timer() calls
serial: sh-sci: Use spin_{try}lock_irqsave instead of open coding version
serial: 8250_of: Add IO space support
tty/serial: atmel: use port->name as name in request_irq()
serial: imx: dma_unmap_sg buffers on shutdown
serial: imx: cleanup imx_uart_disable_dma()
tty: serial: qcom_geni_serial: Add early console support
tty: serial: qcom_geni_serial: Return IRQ_NONE for spurious interrupts
tty: serial: qcom_geni_serial: Use iowrite32_rep to write to FIFO
...
Diffstat (limited to 'drivers/tty')
26 files changed, 681 insertions, 293 deletions
diff --git a/drivers/tty/ipwireless/network.c b/drivers/tty/ipwireless/network.c index 695439c03147..cf20616340a1 100644 --- a/drivers/tty/ipwireless/network.c +++ b/drivers/tty/ipwireless/network.c @@ -416,7 +416,7 @@ void ipwireless_network_packet_received(struct ipw_network *network, struct ipw_network *ipwireless_network_create(struct ipw_hardware *hw) { struct ipw_network *network = - kzalloc(sizeof(struct ipw_network), GFP_ATOMIC); + kzalloc(sizeof(struct ipw_network), GFP_KERNEL); if (!network) return NULL; diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index 1dbe27c9946c..86b7e20ffd7f 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -2675,7 +2675,7 @@ static inline void muxnet_put(struct gsm_mux_net *mux_net) kref_put(&mux_net->ref, net_free); } -static int gsm_mux_net_start_xmit(struct sk_buff *skb, +static netdev_tx_t gsm_mux_net_start_xmit(struct sk_buff *skb, struct net_device *net) { struct gsm_mux_net *mux_net = netdev_priv(net); diff --git a/drivers/tty/nozomi.c b/drivers/tty/nozomi.c index b57b35066ebe..fed820e9ab9d 100644 --- a/drivers/tty/nozomi.c +++ b/drivers/tty/nozomi.c @@ -72,19 +72,19 @@ do { \ #define TMP_BUF_MAX 256 -#define DUMP(buf__,len__) \ - do { \ - char tbuf[TMP_BUF_MAX] = {0};\ - if (len__ > 1) {\ - snprintf(tbuf, len__ > TMP_BUF_MAX ? TMP_BUF_MAX : len__, "%s", buf__);\ - if (tbuf[len__-2] == '\r') {\ - tbuf[len__-2] = 'r';\ - } \ - DBG1("SENDING: '%s' (%d+n)", tbuf, len__);\ - } else {\ - DBG1("SENDING: '%s' (%d)", tbuf, len__);\ - } \ -} while (0) +#define DUMP(buf__, len__) \ + do { \ + char tbuf[TMP_BUF_MAX] = {0}; \ + if (len__ > 1) { \ + u32 data_len = min_t(u32, len__, TMP_BUF_MAX); \ + strscpy(tbuf, buf__, data_len); \ + if (tbuf[data_len - 2] == '\r') \ + tbuf[data_len - 2] = 'r'; \ + DBG1("SENDING: '%s' (%d+n)", tbuf, len__); \ + } else { \ + DBG1("SENDING: '%s' (%d)", tbuf, len__); \ + } \ + } while (0) /* Defines */ #define NOZOMI_NAME "nozomi" @@ -102,41 +102,41 @@ do { \ #define RECEIVE_BUF_MAX 4 -#define R_IIR 0x0000 /* Interrupt Identity Register */ -#define R_FCR 0x0000 /* Flow Control Register */ -#define R_IER 0x0004 /* Interrupt Enable Register */ +#define R_IIR 0x0000 /* Interrupt Identity Register */ +#define R_FCR 0x0000 /* Flow Control Register */ +#define R_IER 0x0004 /* Interrupt Enable Register */ #define NOZOMI_CONFIG_MAGIC 0xEFEFFEFE #define TOGGLE_VALID 0x0000 /* Definition of interrupt tokens */ -#define MDM_DL1 0x0001 -#define MDM_UL1 0x0002 -#define MDM_DL2 0x0004 -#define MDM_UL2 0x0008 -#define DIAG_DL1 0x0010 -#define DIAG_DL2 0x0020 -#define DIAG_UL 0x0040 -#define APP1_DL 0x0080 -#define APP1_UL 0x0100 -#define APP2_DL 0x0200 -#define APP2_UL 0x0400 -#define CTRL_DL 0x0800 -#define CTRL_UL 0x1000 -#define RESET 0x8000 - -#define MDM_DL (MDM_DL1 | MDM_DL2) -#define MDM_UL (MDM_UL1 | MDM_UL2) -#define DIAG_DL (DIAG_DL1 | DIAG_DL2) +#define MDM_DL1 0x0001 +#define MDM_UL1 0x0002 +#define MDM_DL2 0x0004 +#define MDM_UL2 0x0008 +#define DIAG_DL1 0x0010 +#define DIAG_DL2 0x0020 +#define DIAG_UL 0x0040 +#define APP1_DL 0x0080 +#define APP1_UL 0x0100 +#define APP2_DL 0x0200 +#define APP2_UL 0x0400 +#define CTRL_DL 0x0800 +#define CTRL_UL 0x1000 +#define RESET 0x8000 + +#define MDM_DL (MDM_DL1 | MDM_DL2) +#define MDM_UL (MDM_UL1 | MDM_UL2) +#define DIAG_DL (DIAG_DL1 | DIAG_DL2) /* modem signal definition */ -#define CTRL_DSR 0x0001 -#define CTRL_DCD 0x0002 -#define CTRL_RI 0x0004 -#define CTRL_CTS 0x0008 +#define CTRL_DSR 0x0001 +#define CTRL_DCD 0x0002 +#define CTRL_RI 0x0004 +#define CTRL_CTS 0x0008 -#define CTRL_DTR 0x0001 -#define CTRL_RTS 0x0002 +#define CTRL_DTR 0x0001 +#define CTRL_RTS 0x0002 #define MAX_PORT 4 #define NOZOMI_MAX_PORTS 5 @@ -155,7 +155,7 @@ enum card_type { /* Initialization states a card can be in */ enum card_state { - NOZOMI_STATE_UKNOWN = 0, + NOZOMI_STATE_UNKNOWN = 0, NOZOMI_STATE_ENABLED = 1, /* pci device enabled */ NOZOMI_STATE_ALLOCATED = 2, /* config setup done */ NOZOMI_STATE_READY = 3, /* flowcontrols received */ @@ -365,7 +365,7 @@ struct buffer { u8 *data; } __attribute__ ((packed)); -/* Global variables */ +/* Global variables */ static const struct pci_device_id nozomi_pci_tbl[] = { {PCI_DEVICE(0x1931, 0x000c)}, /* Nozomi HSDPA */ {}, @@ -1686,12 +1686,12 @@ static int ntty_tiocmget(struct tty_struct *tty) /* Note: these could change under us but it is not clear this matters if so */ - return (ctrl_ul->RTS ? TIOCM_RTS : 0) | - (ctrl_ul->DTR ? TIOCM_DTR : 0) | - (ctrl_dl->DCD ? TIOCM_CAR : 0) | - (ctrl_dl->RI ? TIOCM_RNG : 0) | - (ctrl_dl->DSR ? TIOCM_DSR : 0) | - (ctrl_dl->CTS ? TIOCM_CTS : 0); + return (ctrl_ul->RTS ? TIOCM_RTS : 0) + | (ctrl_ul->DTR ? TIOCM_DTR : 0) + | (ctrl_dl->DCD ? TIOCM_CAR : 0) + | (ctrl_dl->RI ? TIOCM_RNG : 0) + | (ctrl_dl->DSR ? TIOCM_DSR : 0) + | (ctrl_dl->CTS ? TIOCM_CTS : 0); } /* Sets io controls parameters */ @@ -1722,10 +1722,10 @@ static int ntty_cflags_changed(struct port *port, unsigned long flags, const struct async_icount cnow = port->tty_icount; int ret; - ret = ((flags & TIOCM_RNG) && (cnow.rng != cprev->rng)) || - ((flags & TIOCM_DSR) && (cnow.dsr != cprev->dsr)) || - ((flags & TIOCM_CD) && (cnow.dcd != cprev->dcd)) || - ((flags & TIOCM_CTS) && (cnow.cts != cprev->cts)); + ret = ((flags & TIOCM_RNG) && (cnow.rng != cprev->rng)) + || ((flags & TIOCM_DSR) && (cnow.dsr != cprev->dsr)) + || ((flags & TIOCM_CD) && (cnow.dcd != cprev->dcd)) + || ((flags & TIOCM_CTS) && (cnow.cts != cprev->cts)); *cprev = cnow; diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index 6c7151edd715..b0e2c4847a5d 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -110,16 +110,19 @@ static void pty_unthrottle(struct tty_struct *tty) static int pty_write(struct tty_struct *tty, const unsigned char *buf, int c) { struct tty_struct *to = tty->link; + unsigned long flags; if (tty->stopped) return 0; if (c > 0) { + spin_lock_irqsave(&to->port->lock, flags); /* Stuff the data into the input queue of the other end */ c = tty_insert_flip_string(to->port, buf, c); /* And shovel */ if (c) tty_flip_buffer_push(to->port); + spin_unlock_irqrestore(&to->port->lock, flags); } return c; } diff --git a/drivers/tty/serial/8250/8250_aspeed_vuart.c b/drivers/tty/serial/8250/8250_aspeed_vuart.c index 74a408d9db24..435bec40dee6 100644 --- a/drivers/tty/serial/8250/8250_aspeed_vuart.c +++ b/drivers/tty/serial/8250/8250_aspeed_vuart.c @@ -10,6 +10,8 @@ #include <linux/of_address.h> #include <linux/of_irq.h> #include <linux/of_platform.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> #include <linux/clk.h> #include "8250.h" @@ -28,9 +30,18 @@ struct aspeed_vuart { void __iomem *regs; struct clk *clk; int line; + struct timer_list unthrottle_timer; + struct uart_8250_port *port; }; /* + * If we fill the tty flip buffers, we throttle the data ready interrupt + * to prevent dropped characters. This timeout defines how long we wait + * to (conditionally, depending on buffer state) unthrottle. + */ +static const int unthrottle_timeout = HZ/10; + +/* * 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. @@ -179,6 +190,114 @@ static void aspeed_vuart_shutdown(struct uart_port *uart_port) serial8250_do_shutdown(uart_port); } +static void __aspeed_vuart_set_throttle(struct uart_8250_port *up, + bool throttle) +{ + unsigned char irqs = UART_IER_RLSI | UART_IER_RDI; + + up->ier &= ~irqs; + if (!throttle) + up->ier |= irqs; + serial_out(up, UART_IER, up->ier); +} +static void aspeed_vuart_set_throttle(struct uart_port *port, bool throttle) +{ + struct uart_8250_port *up = up_to_u8250p(port); + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + __aspeed_vuart_set_throttle(up, throttle); + spin_unlock_irqrestore(&port->lock, flags); +} + +static void aspeed_vuart_throttle(struct uart_port *port) +{ + aspeed_vuart_set_throttle(port, true); +} + +static void aspeed_vuart_unthrottle(struct uart_port *port) +{ + aspeed_vuart_set_throttle(port, false); +} + +static void aspeed_vuart_unthrottle_exp(struct timer_list *timer) +{ + struct aspeed_vuart *vuart = from_timer(vuart, timer, unthrottle_timer); + struct uart_8250_port *up = vuart->port; + + if (!tty_buffer_space_avail(&up->port.state->port)) { + mod_timer(&vuart->unthrottle_timer, + jiffies + unthrottle_timeout); + return; + } + + aspeed_vuart_unthrottle(&up->port); +} + +/* + * Custom interrupt handler to manage finer-grained flow control. Although we + * have throttle/unthrottle callbacks, we've seen that the VUART device can + * deliver characters faster than the ldisc has a chance to check buffer space + * against the throttle threshold. This results in dropped characters before + * the throttle. + * + * We do this by checking for flip buffer space before RX. If we have no space, + * throttle now and schedule an unthrottle for later, once the ldisc has had + * a chance to drain the buffers. + */ +static int aspeed_vuart_handle_irq(struct uart_port *port) +{ + struct uart_8250_port *up = up_to_u8250p(port); + unsigned int iir, lsr; + unsigned long flags; + int space, count; + + iir = serial_port_in(port, UART_IIR); + + if (iir & UART_IIR_NO_INT) + return 0; + + spin_lock_irqsave(&port->lock, flags); + + lsr = serial_port_in(port, UART_LSR); + + if (lsr & (UART_LSR_DR | UART_LSR_BI)) { + space = tty_buffer_space_avail(&port->state->port); + + if (!space) { + /* throttle and schedule an unthrottle later */ + struct aspeed_vuart *vuart = port->private_data; + __aspeed_vuart_set_throttle(up, true); + + if (!timer_pending(&vuart->unthrottle_timer)) { + vuart->port = up; + mod_timer(&vuart->unthrottle_timer, + jiffies + unthrottle_timeout); + } + + } else { + count = min(space, 256); + + do { + serial8250_read_char(up, lsr); + lsr = serial_in(up, UART_LSR); + if (--count == 0) + break; + } while (lsr & (UART_LSR_DR | UART_LSR_BI)); + + tty_flip_buffer_push(&port->state->port); + } + } + + serial8250_modem_status(up); + if (lsr & UART_LSR_THRE) + serial8250_tx_chars(up); + + spin_unlock_irqrestore(&port->lock, flags); + + return 1; +} + static int aspeed_vuart_probe(struct platform_device *pdev) { struct uart_8250_port port; @@ -195,6 +314,7 @@ static int aspeed_vuart_probe(struct platform_device *pdev) return -ENOMEM; vuart->dev = &pdev->dev; + timer_setup(&vuart->unthrottle_timer, aspeed_vuart_unthrottle_exp, 0); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); vuart->regs = devm_ioremap_resource(&pdev->dev, res); @@ -208,6 +328,9 @@ static int aspeed_vuart_probe(struct platform_device *pdev) port.port.mapsize = resource_size(res); port.port.startup = aspeed_vuart_startup; port.port.shutdown = aspeed_vuart_shutdown; + port.port.throttle = aspeed_vuart_throttle; + port.port.unthrottle = aspeed_vuart_unthrottle; + port.port.status = UPSTAT_SYNC_FIFO; port.port.dev = &pdev->dev; rc = sysfs_create_group(&vuart->dev->kobj, &aspeed_vuart_attr_group); @@ -253,6 +376,7 @@ static int aspeed_vuart_probe(struct platform_device *pdev) port.port.irq = irq_of_parse_and_map(np, 0); port.port.irqflags = IRQF_SHARED; + port.port.handle_irq = aspeed_vuart_handle_irq; port.port.iotype = UPIO_MEM; port.port.type = PORT_16550A; port.port.uartclk = clk; @@ -292,6 +416,7 @@ static int aspeed_vuart_remove(struct platform_device *pdev) { struct aspeed_vuart *vuart = platform_get_drvdata(pdev); + del_timer_sync(&vuart->unthrottle_timer); aspeed_vuart_set_enabled(vuart, false); serial8250_unregister_port(vuart->line); sysfs_remove_group(&vuart->dev->kobj, &aspeed_vuart_attr_group); diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index 6fcdb90f616a..aff04f1de3a5 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -121,25 +121,44 @@ static void dw8250_check_lcr(struct uart_port *p, int value) } /* Returns once the transmitter is empty or we run out of retries */ -static void dw8250_tx_wait_empty(struct uart_port *p, int tries) +static void dw8250_tx_wait_empty(struct uart_port *p) { + unsigned int tries = 20000; + unsigned int delay_threshold = tries - 1000; unsigned int lsr; while (tries--) { lsr = readb (p->membase + (UART_LSR << p->regshift)); if (lsr & UART_LSR_TEMT) break; - udelay (10); + + /* The device is first given a chance to empty without delay, + * to avoid slowdowns at high bitrates. If after 1000 tries + * the buffer has still not emptied, allow more time for low- + * speed links. */ + if (tries < delay_threshold) + udelay (1); } } -static void dw8250_serial_out(struct uart_port *p, int offset, int value) +static void dw8250_serial_out38x(struct uart_port *p, int offset, int value) { struct dw8250_data *d = p->private_data; /* Allow the TX to drain before we reconfigure */ if (offset == UART_LCR) - dw8250_tx_wait_empty(p, 1000); + dw8250_tx_wait_empty(p); + + writeb(value, p->membase + (offset << p->regshift)); + + if (offset == UART_LCR && !d->uart_16550_compatible) + dw8250_check_lcr(p, value); +} + + +static void dw8250_serial_out(struct uart_port *p, int offset, int value) +{ + struct dw8250_data *d = p->private_data; writeb(value, p->membase + (offset << p->regshift)); @@ -357,6 +376,9 @@ static void dw8250_quirks(struct uart_port *p, struct dw8250_data *data) p->serial_in = dw8250_serial_in32be; p->serial_out = dw8250_serial_out32be; } + if (of_device_is_compatible(np, "marvell,armada-38x-uart")) + p->serial_out = dw8250_serial_out38x; + } else if (acpi_dev_present("APMC0D08", NULL, -1)) { p->iotype = UPIO_MEM32; p->regshift = 2; @@ -554,6 +576,10 @@ static int dw8250_probe(struct platform_device *pdev) if (!data->skip_autocfg) dw8250_setup_port(p); +#ifdef CONFIG_PM + uart.capabilities |= UART_CAP_RPM; +#endif + /* If we have a valid fifosize, try hooking up DMA */ if (p->fifosize) { data->dma.rxconf.src_maxburst = p->fifosize / 4; @@ -666,6 +692,7 @@ static const struct dev_pm_ops dw8250_pm_ops = { static const struct of_device_id dw8250_of_match[] = { { .compatible = "snps,dw-apb-uart" }, { .compatible = "cavium,octeon-3860-uart" }, + { .compatible = "marvell,armada-38x-uart" }, { /* Sentinel */ } }; MODULE_DEVICE_TABLE(of, dw8250_of_match); diff --git a/drivers/tty/serial/8250/8250_early.c b/drivers/tty/serial/8250/8250_early.c index ae6a256524d8..5cd8c36c8fcc 100644 --- a/drivers/tty/serial/8250/8250_early.c +++ b/drivers/tty/serial/8250/8250_early.c @@ -122,7 +122,7 @@ static void __init init_port(struct earlycon_device *device) serial8250_early_out(port, UART_FCR, 0); /* no fifo */ serial8250_early_out(port, UART_MCR, 0x3); /* DTR + RTS */ - if (port->uartclk && device->baud) { + if (port->uartclk) { divisor = DIV_ROUND_CLOSEST(port->uartclk, 16 * device->baud); c = serial8250_early_in(port, UART_LCR); serial8250_early_out(port, UART_LCR, c | UART_LCR_DLAB); diff --git a/drivers/tty/serial/8250/8250_of.c b/drivers/tty/serial/8250/8250_of.c index 9835b1c1cbe1..bfb37f0be22f 100644 --- a/drivers/tty/serial/8250/8250_of.c +++ b/drivers/tty/serial/8250/8250_of.c @@ -92,13 +92,43 @@ static int of_platform_serial_setup(struct platform_device *ofdev, goto err_unprepare; } + port->flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_FIXED_PORT | + UPF_FIXED_TYPE; spin_lock_init(&port->lock); - port->mapbase = resource.start; - port->mapsize = resource_size(&resource); - /* Check for shifted address mapping */ - if (of_property_read_u32(np, "reg-offset", &prop) == 0) - port->mapbase += prop; + if (resource_type(&resource) == IORESOURCE_IO) { + port->iotype = UPIO_PORT; + port->iobase = resource.start; + } else { + port->mapbase = resource.start; + port->mapsize = resource_size(&resource); + + /* Check for shifted address mapping */ + if (of_property_read_u32(np, "reg-offset", &prop) == 0) + port->mapbase += prop; + + port->iotype = UPIO_MEM; + if (of_property_read_u32(np, "reg-io-width", &prop) == 0) { + switch (prop) { + case 1: + port->iotype = UPIO_MEM; + break; + case 2: + port->iotype = UPIO_MEM16; + break; + case 4: + port->iotype = of_device_is_big_endian(np) ? + UPIO_MEM32BE : UPIO_MEM32; + break; + default: + dev_warn(&ofdev->dev, "unsupported reg-io-width (%d)\n", + prop); + ret = -EINVAL; + goto err_dispose; + } + } + port->flags |= UPF_IOREMAP; + } /* Check for registers offset within the devices address range */ if (of_property_read_u32(np, "reg-shift", &prop) == 0) @@ -114,26 +144,6 @@ static int of_platform_serial_setup(struct platform_device *ofdev, port->line = ret; port->irq = irq_of_parse_and_map(np, 0); - port->iotype = UPIO_MEM; - if (of_property_read_u32(np, "reg-io-width", &prop) == 0) { - switch (prop) { - case 1: - port->iotype = UPIO_MEM; - break; - case 2: - port->iotype = UPIO_MEM16; - break; - case 4: - port->iotype = of_device_is_big_endian(np) ? - UPIO_MEM32BE : UPIO_MEM32; - break; - default: - dev_warn(&ofdev->dev, "unsupported reg-io-width (%d)\n", - prop); - ret = -EINVAL; - goto err_dispose; - } - } info->rst = devm_reset_control_get_optional_shared(&ofdev->dev, NULL); if (IS_ERR(info->rst)) { @@ -147,8 +157,7 @@ static int of_platform_serial_setup(struct platform_device *ofdev, port->type = type; port->uartclk = clk; - port->flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_IOREMAP - | UPF_FIXED_PORT | UPF_FIXED_TYPE; + port->irqflags |= IRQF_SHARED; if (of_property_read_bool(np, "no-loopback-test")) port->flags |= UPF_SKIP_TEST; diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c index 624b501fd253..1b337fee07ed 100644 --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -1110,13 +1110,14 @@ static int omap8250_no_handle_irq(struct uart_port *port) return 0; } +static const u8 omap4_habit = UART_ERRATA_CLOCK_DISABLE; static const u8 am3352_habit = OMAP_DMA_TX_KICK | UART_ERRATA_CLOCK_DISABLE; static const u8 dra742_habit = UART_ERRATA_CLOCK_DISABLE; static const struct of_device_id omap8250_dt_ids[] = { { .compatible = "ti,omap2-uart" }, { .compatible = "ti,omap3-uart" }, - { .compatible = "ti,omap4-uart" }, + { .compatible = "ti,omap4-uart", .data = &omap4_habit, }, { .compatible = "ti,am3352-uart", .data = &am3352_habit, }, { .compatible = "ti,am4372-uart", .data = &am3352_habit, }, { .compatible = "ti,dra742-uart", .data = &dra742_habit, }, @@ -1310,8 +1311,17 @@ static void omap8250_complete(struct device *dev) static int omap8250_suspend(struct device *dev) { struct omap8250_priv *priv = dev_get_drvdata(dev); + struct uart_8250_port *up = serial8250_get_port(priv->line); serial8250_suspend_port(priv->line); + + pm_runtime_get_sync(dev); + if (!device_may_wakeup(dev)) + priv->wer = 0; + serial_out(up, UART_OMAP_WER, priv->wer); + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + flush_work(&priv->qos_work); return 0; } @@ -1353,6 +1363,19 @@ static int omap8250_soft_reset(struct device *dev) int sysc; int syss; + /* + * At least on omap4, unused uarts may not idle after reset without + * a basic scr dma configuration even with no dma in use. The + * module clkctrl status bits will be 1 instead of 3 blocking idle + * for the whole clockdomain. The softreset below will clear scr, + * and we restore it on resume so this is safe to do on all SoCs + * 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, + OMAP_UART_SCR_DMAMODE_1 | OMAP_UART_SCR_DMAMODE_CTL); + sysc = serial_in(up, UART_OMAP_SYSC); /* softreset the UART */ @@ -1403,6 +1426,8 @@ static int omap8250_runtime_suspend(struct device *dev) /* 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) diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index 95833cbc4338..cf541aab2bd0 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -243,6 +243,7 @@ static const struct serial8250_config uart_config[] = { .fifo_size = 32, .tx_loadsz = 32, .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, + .rxtrig_bytes = {1, 8, 16, 30}, .flags = UART_CAP_FIFO | UART_CAP_AFE, }, [PORT_ALTR_16550_F64] = { @@ -250,6 +251,7 @@ static const struct serial8250_config uart_config[] = { .fifo_size = 64, .tx_loadsz = 64, .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, + .rxtrig_bytes = {1, 16, 32, 62}, .flags = UART_CAP_FIFO | UART_CAP_AFE, }, [PORT_ALTR_16550_F128] = { @@ -257,6 +259,7 @@ static const struct serial8250_config uart_config[] = { .fifo_size = 128, .tx_loadsz = 128, .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, + .rxtrig_bytes = {1, 32, 64, 126}, .flags = UART_CAP_FIFO | UART_CAP_AFE, }, /* @@ -1680,7 +1683,7 @@ static void serial8250_enable_ms(struct uart_port *port) serial8250_rpm_put(up); } -static void serial8250_read_char(struct uart_8250_port *up, unsigned char lsr) +void serial8250_read_char(struct uart_8250_port *up, unsigned char lsr) { struct uart_port *port = &up->port; unsigned char ch; @@ -1740,6 +1743,7 @@ static void serial8250_read_char(struct uart_8250_port *up, unsigned char lsr) uart_insert_char(port, lsr, UART_LSR_OE, ch, flag); } +EXPORT_SYMBOL_GPL(serial8250_read_char); /* * serial8250_rx_chars: processes according to the passed in LSR diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 0f058df0b070..df8bd0c7b97d 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -115,7 +115,6 @@ config SERIAL_SB1250_DUART_CONSOLE config SERIAL_ATMEL bool "AT91 on-chip serial port support" - depends on HAS_DMA depends on ARCH_AT91 || COMPILE_TEST select SERIAL_CORE select SERIAL_MCTRL_GPIO if GPIOLIB @@ -500,7 +499,6 @@ config SERIAL_SA1100_CONSOLE config SERIAL_IMX tristate "IMX serial port support" - depends on HAS_DMA depends on ARCH_MXC || COMPILE_TEST select SERIAL_CORE select RATIONAL @@ -676,6 +674,8 @@ config SERIAL_SH_SCI config SERIAL_SH_SCI_NR_UARTS int "Maximum number of SCI(F) serial ports" if EXPERT + range 1 64 if 64BIT + range 1 32 if !64BIT depends on SERIAL_SH_SCI default "3" if H8300 default "10" if SUPERH @@ -1262,7 +1262,6 @@ config SERIAL_PCH_UART_CONSOLE config SERIAL_MXS_AUART tristate "MXS AUART support" - depends on HAS_DMA depends on ARCH_MXS || MACH_ASM9260 || COMPILE_TEST select SERIAL_CORE select SERIAL_MCTRL_GPIO if GPIOLIB @@ -1295,7 +1294,7 @@ config SERIAL_XILINX_PS_UART_CONSOLE config SERIAL_AR933X tristate "AR933X serial port support" - depends on HAVE_CLK && SOC_AR933X + depends on HAVE_CLK && ATH79 select SERIAL_CORE help If you have an Atheros AR933X SOC based board and want to use the @@ -1473,7 +1472,6 @@ config SERIAL_SPRD_CONSOLE config SERIAL_STM32 tristate "STMicroelectronics STM32 serial port support" select SERIAL_CORE - depends on HAS_DMA depends on ARCH_STM32 || COMPILE_TEST help This driver is for the on-chip Serial Controller on diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 4b40a5b449ee..ebd33c0232e6 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -1727,10 +1727,26 @@ static int pl011_allocate_irq(struct uart_amba_port *uap) */ static void pl011_enable_interrupts(struct uart_amba_port *uap) { + unsigned int i; + spin_lock_irq(&uap->port.lock); /* Clear out any spuriously appearing RX interrupts */ pl011_write(UART011_RTIS | UART011_RXIS, uap, REG_ICR); + + /* + * RXIS is asserted only when the RX FIFO transitions from below + * to above the trigger threshold. If the RX FIFO is already + * full to the threshold this can't happen and RXIS will now be + * stuck off. Drain the RX FIFO explicitly to fix this: + */ + for (i = 0; i < uap->fifosize * 2; ++i) { + if (pl011_read(uap, REG_FR) & UART01x_FR_RXFE) + break; + + pl011_read(uap, REG_DR); + } + uap->im = UART011_RTIM; if (!pl011_dma_rx_running(uap)) uap->im |= UART011_RXIM; diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index e287fe8f10fc..55b3eff148b1 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -1757,7 +1757,6 @@ static int atmel_startup(struct uart_port *port) { struct platform_device *pdev = to_platform_device(port->dev); struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); - struct tty_struct *tty = port->state->port.tty; int retval; /* @@ -1772,8 +1771,8 @@ static int atmel_startup(struct uart_port *port) * Allocate the IRQ */ retval = request_irq(port->irq, atmel_interrupt, - IRQF_SHARED | IRQF_COND_SUSPEND, - tty ? tty->name : "atmel_serial", port); + IRQF_SHARED | IRQF_COND_SUSPEND, + dev_name(&pdev->dev), port); if (retval) { dev_err(port->dev, "atmel_startup - Can't get irq\n"); return retval; diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c index 22683393a0f2..c14873b67803 100644 --- a/drivers/tty/serial/earlycon.c +++ b/drivers/tty/serial/earlycon.c @@ -246,7 +246,6 @@ int __init of_setup_earlycon(const struct earlycon_id *match, return -ENXIO; } port->mapbase = addr; - port->uartclk = BASE_BAUD * 16; val = of_get_flat_dt_prop(node, "reg-offset", NULL); if (val) @@ -281,6 +280,10 @@ int __init of_setup_earlycon(const struct earlycon_id *match, if (val) early_console_dev.baud = be32_to_cpu(*val); + val = of_get_flat_dt_prop(node, "clock-frequency", NULL); + if (val) + port->uartclk = be32_to_cpu(*val); + if (options) { early_console_dev.baud = simple_strtoul(options, NULL, 0); strlcpy(early_console_dev.options, options, diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index c2fc6bef7a6f..4e853570ea80 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -1291,18 +1291,13 @@ static void imx_uart_enable_dma(struct imx_port *sport) static void imx_uart_disable_dma(struct imx_port *sport) { - u32 ucr1, ucr2; + u32 ucr1; /* clear UCR1 */ ucr1 = imx_uart_readl(sport, UCR1); ucr1 &= ~(UCR1_RXDMAEN | UCR1_TXDMAEN | UCR1_ATDMAEN); imx_uart_writel(sport, ucr1, UCR1); - /* clear UCR2 */ - ucr2 = imx_uart_readl(sport, UCR2); - ucr2 &= ~(UCR2_CTSC | UCR2_CTS | UCR2_ATEN); - imx_uart_writel(sport, ucr2, UCR2); - imx_uart_setup_ufcr(sport, TXTL_DEFAULT, RXTL_DEFAULT); sport->dma_is_enabled = 0; @@ -1427,13 +1422,21 @@ static void imx_uart_shutdown(struct uart_port *port) { struct imx_port *sport = (struct imx_port *)port; unsigned long flags; - u32 ucr1, ucr2; + u32 ucr1, ucr2, ucr4; if (sport->dma_is_enabled) { - sport->dma_is_rxing = 0; - sport->dma_is_txing = 0; dmaengine_terminate_sync(sport->dma_chan_tx); + if (sport->dma_is_txing) { + dma_unmap_sg(sport->port.dev, &sport->tx_sgl[0], + sport->dma_tx_nents, DMA_TO_DEVICE); + sport->dma_is_txing = 0; + } dmaengine_terminate_sync(sport->dma_chan_rx); + if (sport->dma_is_rxing) { + dma_unmap_sg(sport->port.dev, &sport->rx_sgl, + 1, DMA_FROM_DEVICE); + sport->dma_is_rxing = 0; + } spin_lock_irqsave(&sport->port.lock, flags); imx_uart_stop_tx(port); @@ -1449,6 +1452,10 @@ static void imx_uart_shutdown(struct uart_port *port) ucr2 = imx_uart_readl(sport, UCR2); ucr2 &= ~(UCR2_TXEN | UCR2_ATEN); imx_uart_writel(sport, ucr2, UCR2); + + ucr4 = imx_uart_readl(sport, UCR4); + ucr4 &= ~UCR4_OREN; + imx_uart_writel(sport, ucr4, UCR4); spin_unlock_irqrestore(&sport->port.lock, flags); /* @@ -2425,8 +2432,7 @@ static void imx_uart_enable_wakeup(struct imx_port *sport, bool on) static int imx_uart_suspend_noirq(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct imx_port *sport = platform_get_drvdata(pdev); + struct imx_port *sport = dev_get_drvdata(dev); imx_uart_save_context(sport); @@ -2437,8 +2443,7 @@ static int imx_uart_suspend_noirq(struct device *dev) static int imx_uart_resume_noirq(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct imx_port *sport = platform_get_drvdata(pdev); + struct imx_port *sport = dev_get_drvdata(dev); int ret; ret = clk_enable(sport->clk_ipg); @@ -2452,8 +2457,7 @@ static int imx_uart_resume_noirq(struct device *dev) static int imx_uart_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct imx_port *sport = platform_get_drvdata(pdev); + struct imx_port *sport = dev_get_drvdata(dev); int ret; uart_suspend_port(&imx_uart_uart_driver, &sport->port); @@ -2471,8 +2475,7 @@ static int imx_uart_suspend(struct device *dev) static int imx_uart_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct imx_port *sport = platform_get_drvdata(pdev); + struct imx_port *sport = dev_get_drvdata(dev); /* disable wakeup from i.MX UART */ imx_uart_enable_wakeup(sport, false); @@ -2487,8 +2490,7 @@ static int imx_uart_resume(struct device *dev) static int imx_uart_freeze(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct imx_port *sport = platform_get_drvdata(pdev); + struct imx_port *sport = dev_get_drvdata(dev); uart_suspend_port(&imx_uart_uart_driver, &sport->port); @@ -2497,8 +2499,7 @@ static int imx_uart_freeze(struct device *dev) static int imx_uart_thaw(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct imx_port *sport = platform_get_drvdata(pdev); + struct imx_port *sport = dev_get_drvdata(dev); uart_resume_port(&imx_uart_uart_driver, &sport->port); diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index ee96cf0d0057..736b74fd6623 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -1812,11 +1812,34 @@ static const struct of_device_id msm_match_table[] = { }; MODULE_DEVICE_TABLE(of, msm_match_table); +static int __maybe_unused msm_serial_suspend(struct device *dev) +{ + struct msm_port *port = dev_get_drvdata(dev); + + uart_suspend_port(&msm_uart_driver, &port->uart); + + return 0; +} + +static int __maybe_unused msm_serial_resume(struct device *dev) +{ + struct msm_port *port = dev_get_drvdata(dev); + + uart_resume_port(&msm_uart_driver, &port->uart); + + return 0; +} + +static const struct dev_pm_ops msm_serial_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(msm_serial_suspend, msm_serial_resume) +}; + static struct platform_driver msm_platform_driver = { .remove = msm_serial_remove, .probe = msm_serial_probe, .driver = { .name = "msm_serial", + .pm = &msm_serial_dev_pm_ops, .of_match_table = msm_match_table, }, }; diff --git a/drivers/tty/serial/mvebu-uart.c b/drivers/tty/serial/mvebu-uart.c index f503fab1e268..d04b5eeea3c6 100644 --- a/drivers/tty/serial/mvebu-uart.c +++ b/drivers/tty/serial/mvebu-uart.c @@ -71,6 +71,8 @@ #define UART_BRDV 0x10 #define BRDV_BAUD_MASK 0x3FF +#define UART_OSAMP 0x14 + #define MVEBU_NR_UARTS 2 #define MVEBU_UART_TYPE "mvebu-uart" @@ -108,6 +110,17 @@ struct mvebu_uart_driver_data { struct uart_flags flags; }; +/* Saved registers during suspend */ +struct mvebu_uart_pm_regs { + unsigned int rbr; + unsigned int tsh; + unsigned int ctrl; + unsigned int intr; + unsigned int stat; + unsigned int brdv; + unsigned int osamp; +}; + /* MVEBU UART driver structure */ struct mvebu_uart { struct uart_port *port; @@ -115,6 +128,9 @@ struct mvebu_uart { int irq[UART_IRQ_COUNT]; unsigned char __iomem *nb; struct mvebu_uart_driver_data *data; +#if defined(CONFIG_PM) + struct mvebu_uart_pm_regs pm_regs; +#endif /* CONFIG_PM */ }; static struct mvebu_uart *to_mvuart(struct uart_port *port) @@ -718,6 +734,51 @@ static struct uart_driver mvebu_uart_driver = { #endif }; +#if defined(CONFIG_PM) +static int mvebu_uart_suspend(struct device *dev) +{ + struct mvebu_uart *mvuart = dev_get_drvdata(dev); + struct uart_port *port = mvuart->port; + + uart_suspend_port(&mvebu_uart_driver, port); + + mvuart->pm_regs.rbr = readl(port->membase + UART_RBR(port)); + mvuart->pm_regs.tsh = readl(port->membase + UART_TSH(port)); + mvuart->pm_regs.ctrl = readl(port->membase + UART_CTRL(port)); + mvuart->pm_regs.intr = readl(port->membase + UART_INTR(port)); + mvuart->pm_regs.stat = readl(port->membase + UART_STAT); + mvuart->pm_regs.brdv = readl(port->membase + UART_BRDV); + mvuart->pm_regs.osamp = readl(port->membase + UART_OSAMP); + + device_set_wakeup_enable(dev, true); + + return 0; +} + +static int mvebu_uart_resume(struct device *dev) +{ + struct mvebu_uart *mvuart = dev_get_drvdata(dev); + struct uart_port *port = mvuart->port; + + writel(mvuart->pm_regs.rbr, port->membase + UART_RBR(port)); + writel(mvuart->pm_regs.tsh, port->membase + UART_TSH(port)); + writel(mvuart->pm_regs.ctrl, port->membase + UART_CTRL(port)); + writel(mvuart->pm_regs.intr, port->membase + UART_INTR(port)); + writel(mvuart->pm_regs.stat, port->membase + UART_STAT); + writel(mvuart->pm_regs.brdv, port->membase + UART_BRDV); + writel(mvuart->pm_regs.osamp, port->membase + UART_OSAMP); + + uart_resume_port(&mvebu_uart_driver, port); + + return 0; +} + +static const struct dev_pm_ops mvebu_uart_pm_ops = { + .suspend = mvebu_uart_suspend, + .resume = mvebu_uart_resume, +}; +#endif /* CONFIG_PM */ + static const struct of_device_id mvebu_uart_of_match[]; /* Counter to keep track of each UART port id when not using CONFIG_OF */ @@ -891,6 +952,9 @@ static struct platform_driver mvebu_uart_platform_driver = { .name = "mvebu-uart", .of_match_table = of_match_ptr(mvebu_uart_of_match), .suppress_bind_attrs = true, +#if defined(CONFIG_PM) + .pm = &mvebu_uart_pm_ops, +#endif /* CONFIG_PM */ }, }; diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c index a1b3eb04cb32..c62e17c85f57 100644 --- a/drivers/tty/serial/qcom_geni_serial.c +++ b/drivers/tty/serial/qcom_geni_serial.c @@ -98,14 +98,13 @@ struct qcom_geni_serial_port { enum geni_se_xfer_mode xfer_mode; bool setup; int (*handle_rx)(struct uart_port *uport, u32 bytes, bool drop); - unsigned int xmit_size; unsigned int baud; unsigned int tx_bytes_pw; unsigned int rx_bytes_pw; bool brk; }; -static const struct uart_ops qcom_geni_serial_pops; +static const struct uart_ops qcom_geni_console_pops; static struct uart_driver qcom_geni_console_driver; static int handle_rx_console(struct uart_port *uport, u32 bytes, bool drop); static unsigned int qcom_geni_serial_tx_empty(struct uart_port *port); @@ -118,7 +117,14 @@ static const unsigned long root_freq[] = {7372800, 14745600, 19200000, 29491200, #define to_dev_port(ptr, member) \ container_of(ptr, struct qcom_geni_serial_port, member) -static struct qcom_geni_serial_port qcom_geni_console_port; +static struct qcom_geni_serial_port qcom_geni_console_port = { + .uport = { + .iotype = UPIO_MEM, + .ops = &qcom_geni_console_pops, + .flags = UPF_BOOT_AUTOCONF, + .line = 0, + }, +}; static int qcom_geni_serial_request_port(struct uart_port *uport) { @@ -189,8 +195,19 @@ static bool qcom_geni_serial_poll_bit(struct uart_port *uport, timeout_us = ((fifo_bits * USEC_PER_SEC) / baud) + 500; } - return !readl_poll_timeout_atomic(uport->membase + offset, reg, - (bool)(reg & field) == set, 10, timeout_us); + /* + * Use custom implementation instead of readl_poll_atomic since ktimer + * is not ready at the time of early console. + */ + timeout_us = DIV_ROUND_UP(timeout_us, 10) * 10; + while (timeout_us) { + reg = readl_relaxed(uport->membase + offset); + if ((bool)(reg & field) == set) + return true; + udelay(10); + timeout_us -= 10; + } + return false; } static void qcom_geni_serial_setup_tx(struct uart_port *uport, u32 xmit_size) @@ -286,6 +303,10 @@ __qcom_geni_serial_console_write(struct uart_port *uport, const char *s, u32 bytes_to_send = count; for (i = 0; i < count; i++) { + /* + * uart_console_write() adds a carriage return for each newline. + * Account for additional bytes to be written. + */ if (s[i] == '\n') bytes_to_send++; } @@ -305,7 +326,7 @@ __qcom_geni_serial_console_write(struct uart_port *uport, const char *s, if (!qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS, M_TX_FIFO_WATERMARK_EN, true)) break; - chars_to_write = min_t(size_t, (size_t)(count - i), avail / 2); + chars_to_write = min_t(size_t, count - i, avail / 2); uart_console_write(uport, s + i, chars_to_write, qcom_geni_serial_wr_char); writel_relaxed(M_TX_FIFO_WATERMARK_EN, uport->membase + @@ -406,20 +427,18 @@ static void qcom_geni_serial_start_tx(struct uart_port *uport) u32 status; if (port->xfer_mode == GENI_SE_FIFO) { - status = readl_relaxed(uport->membase + SE_GENI_STATUS); + /* + * readl ensures reading & writing of IRQ_EN register + * is not re-ordered before checking the status of the + * Serial Engine. + */ + status = readl(uport->membase + SE_GENI_STATUS); if (status & M_GENI_CMD_ACTIVE) return; if (!qcom_geni_serial_tx_empty(uport)) return; - /* - * Ensure writing to IRQ_EN & watermark registers are not - * re-ordered before checking the status of the Serial - * Engine and TX FIFO - */ - mb(); - irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN); irq_en |= M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN; @@ -442,7 +461,6 @@ static void qcom_geni_serial_stop_tx(struct uart_port *uport) writel_relaxed(0, uport->membase + SE_GENI_TX_WATERMARK_REG); } - port->xmit_size = 0; writel_relaxed(irq_en, uport->membase + SE_GENI_M_IRQ_EN); status = readl_relaxed(uport->membase + SE_GENI_STATUS); /* Possible stop tx is called multiple times. */ @@ -572,21 +590,14 @@ static void qcom_geni_serial_handle_tx(struct uart_port *uport) chunk = uart_circ_chars_pending(xmit); status = readl_relaxed(uport->membase + SE_GENI_TX_FIFO_STATUS); /* Both FIFO and framework buffer are drained */ - if (chunk == port->xmit_size && !status) { - port->xmit_size = 0; - uart_circ_clear(xmit); + if (!chunk && !status) { qcom_geni_serial_stop_tx(uport); goto out_write_wakeup; } - chunk -= port->xmit_size; avail = (port->tx_fifo_depth - port->tx_wm) * port->tx_bytes_pw; - tail = (xmit->tail + port->xmit_size) & (UART_XMIT_SIZE - 1); - if (chunk > (UART_XMIT_SIZE - tail)) - chunk = UART_XMIT_SIZE - tail; - if (chunk > avail) - chunk = avail; - + tail = xmit->tail; + chunk = min3((size_t)chunk, (size_t)(UART_XMIT_SIZE - tail), avail); if (!chunk) goto out_write_wakeup; @@ -595,24 +606,27 @@ static void qcom_geni_serial_handle_tx(struct uart_port *uport) remaining = chunk; for (i = 0; i < chunk; ) { unsigned int tx_bytes; - unsigned int buf = 0; + u8 buf[sizeof(u32)]; int c; - tx_bytes = min_t(size_t, remaining, (size_t)port->tx_bytes_pw); + memset(buf, 0, ARRAY_SIZE(buf)); + tx_bytes = min_t(size_t, remaining, port->tx_bytes_pw); for (c = 0; c < tx_bytes ; c++) - buf |= (xmit->buf[tail + c] << (c * BITS_PER_BYTE)); + buf[c] = xmit->buf[tail + c]; - writel_relaxed(buf, uport->membase + SE_GENI_TX_FIFOn); + iowrite32_rep(uport->membase + SE_GENI_TX_FIFOn, buf, 1); i += tx_bytes; - tail = (tail + tx_bytes) & (UART_XMIT_SIZE - 1); + tail += tx_bytes; uport->icount.tx += tx_bytes; remaining -= tx_bytes; } + + xmit->tail = tail & (UART_XMIT_SIZE - 1); qcom_geni_serial_poll_tx_done(uport); - port->xmit_size += chunk; out_write_wakeup: - uart_write_wakeup(uport); + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(uport); } static irqreturn_t qcom_geni_serial_isr(int isr, void *dev) @@ -627,7 +641,7 @@ static irqreturn_t qcom_geni_serial_isr(int isr, void *dev) struct qcom_geni_serial_port *port = to_dev_port(uport, uport); if (uport->suspended) - return IRQ_HANDLED; + return IRQ_NONE; spin_lock_irqsave(&uport->lock, flags); m_irq_status = readl_relaxed(uport->membase + SE_GENI_M_IRQ_STATUS); @@ -667,20 +681,16 @@ out_unlock: return IRQ_HANDLED; } -static int get_tx_fifo_size(struct qcom_geni_serial_port *port) +static void get_tx_fifo_size(struct qcom_geni_serial_port *port) { struct uart_port *uport; - if (!port) - return -ENODEV; - uport = &port->uport; port->tx_fifo_depth = geni_se_get_tx_fifo_depth(&port->se); port->tx_fifo_width = geni_se_get_tx_fifo_width(&port->se); port->rx_fifo_depth = geni_se_get_rx_fifo_depth(&port->se); uport->fifosize = (port->tx_fifo_depth * port->tx_fifo_width) / BITS_PER_BYTE; - return 0; } static void set_rfr_wm(struct qcom_geni_serial_port *port) @@ -702,7 +712,6 @@ static void qcom_geni_serial_shutdown(struct uart_port *uport) /* Stop the console before stopping the current tx */ console_stop(uport->cons); - disable_irq(uport->irq); free_irq(uport->irq, uport); spin_lock_irqsave(&uport->lock, flags); qcom_geni_serial_stop_tx(uport); @@ -892,7 +901,7 @@ out_restart_rx: static unsigned int qcom_geni_serial_tx_empty(struct uart_port *uport) { - return !readl_relaxed(uport->membase + SE_GENI_TX_FIFO_STATUS); + return !readl(uport->membase + SE_GENI_TX_FIFO_STATUS); } #ifdef CONFIG_SERIAL_QCOM_GENI_CONSOLE @@ -910,7 +919,7 @@ static int __init qcom_geni_console_setup(struct console *co, char *options) port = get_port_from_line(co->index); if (IS_ERR(port)) { - pr_err("Invalid line %d(%d)\n", co->index, (int)PTR_ERR(port)); + pr_err("Invalid line %d\n", co->index); return PTR_ERR(port); } @@ -942,6 +951,65 @@ static int __init qcom_geni_console_setup(struct console *co, char *options) return uart_set_options(uport, co, baud, parity, bits, flow); } +static void qcom_geni_serial_earlycon_write(struct console *con, + const char *s, unsigned int n) +{ + struct earlycon_device *dev = con->data; + + __qcom_geni_serial_console_write(&dev->port, s, n); +} + +static int __init qcom_geni_serial_earlycon_setup(struct earlycon_device *dev, + const char *opt) +{ + struct uart_port *uport = &dev->port; + u32 tx_trans_cfg; + u32 tx_parity_cfg = 0; /* Disable Tx Parity */ + u32 rx_trans_cfg = 0; + u32 rx_parity_cfg = 0; /* Disable Rx Parity */ + u32 stop_bit_len = 0; /* Default stop bit length - 1 bit */ + u32 bits_per_char; + struct geni_se se; + + if (!uport->membase) + return -EINVAL; + + memset(&se, 0, sizeof(se)); + se.base = uport->membase; + if (geni_se_read_proto(&se) != GENI_SE_UART) + return -ENXIO; + /* + * Ignore Flow control. + * n = 8. + */ + tx_trans_cfg = UART_CTS_MASK; + bits_per_char = BITS_PER_BYTE; + + /* + * Make an unconditional cancel on the main sequencer to reset + * it else we could end up in data loss scenarios. + */ + qcom_geni_serial_poll_tx_done(uport); + qcom_geni_serial_abort_rx(uport); + geni_se_config_packing(&se, BITS_PER_BYTE, 1, false, true, false); + geni_se_init(&se, DEF_FIFO_DEPTH_WORDS / 2, DEF_FIFO_DEPTH_WORDS - 2); + geni_se_select_mode(&se, GENI_SE_FIFO); + + writel_relaxed(tx_trans_cfg, uport->membase + SE_UART_TX_TRANS_CFG); + writel_relaxed(tx_parity_cfg, uport->membase + SE_UART_TX_PARITY_CFG); + writel_relaxed(rx_trans_cfg, uport->membase + SE_UART_RX_TRANS_CFG); + writel_relaxed(rx_parity_cfg, uport->membase + SE_UART_RX_PARITY_CFG); + writel_relaxed(bits_per_char, uport->membase + SE_UART_TX_WORD_LEN); + writel_relaxed(bits_per_char, uport->membase + SE_UART_RX_WORD_LEN); + writel_relaxed(stop_bit_len, uport->membase + SE_UART_TX_STOP_BIT_LEN); + + dev->con->write = qcom_geni_serial_earlycon_write; + dev->con->setup = NULL; + return 0; +} +OF_EARLYCON_DECLARE(qcom_geni, "qcom,geni-debug-uart", + qcom_geni_serial_earlycon_setup); + static int __init console_register(struct uart_driver *drv) { return uart_register_driver(drv); @@ -1026,16 +1094,13 @@ static int qcom_geni_serial_probe(struct platform_device *pdev) if (pdev->dev.of_node) line = of_alias_get_id(pdev->dev.of_node, "serial"); - else - line = pdev->id; if (line < 0 || line >= GENI_UART_CONS_PORTS) return -ENXIO; port = get_port_from_line(line); if (IS_ERR(port)) { - ret = PTR_ERR(port); - dev_err(&pdev->dev, "Invalid line %d(%d)\n", line, ret); - return ret; + dev_err(&pdev->dev, "Invalid line %d\n", line); + return PTR_ERR(port); } uport = &port->uport; @@ -1072,7 +1137,6 @@ static int qcom_geni_serial_probe(struct platform_device *pdev) uport->private_data = &qcom_geni_console_driver; platform_set_drvdata(pdev, port); port->handle_rx = handle_rx_console; - port->setup = false; return uart_add_one_port(&qcom_geni_console_driver, uport); } @@ -1087,8 +1151,7 @@ static int qcom_geni_serial_remove(struct platform_device *pdev) static int __maybe_unused qcom_geni_serial_sys_suspend_noirq(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct qcom_geni_serial_port *port = platform_get_drvdata(pdev); + struct qcom_geni_serial_port *port = dev_get_drvdata(dev); struct uart_port *uport = &port->uport; uart_suspend_port(uport->private_data, uport); @@ -1097,12 +1160,19 @@ static int __maybe_unused qcom_geni_serial_sys_suspend_noirq(struct device *dev) static int __maybe_unused qcom_geni_serial_sys_resume_noirq(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct qcom_geni_serial_port *port = platform_get_drvdata(pdev); + struct qcom_geni_serial_port *port = dev_get_drvdata(dev); struct uart_port *uport = &port->uport; if (console_suspend_enabled && uport->suspended) { uart_resume_port(uport->private_data, uport); + /* + * uart_suspend_port() invokes port shutdown which in turn + * frees the irq. uart_resume_port invokes port startup which + * performs request_irq. The request_irq auto-enables the IRQ. + * In addition, resume_noirq implicitly enables the IRQ and + * leads to an unbalanced IRQ enable warning. Disable the IRQ + * before returning so that the warning is suppressed. + */ disable_irq(uport->irq); } return 0; @@ -1133,11 +1203,6 @@ static int __init qcom_geni_serial_init(void) { int ret; - qcom_geni_console_port.uport.iotype = UPIO_MEM; - qcom_geni_console_port.uport.ops = &qcom_geni_console_pops; - qcom_geni_console_port.uport.flags = UPF_BOOT_AUTOCONF; - qcom_geni_console_port.uport.line = 0; - ret = console_register(&qcom_geni_console_driver); if (ret) return ret; diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index 3f2f8c118ce0..2f8fa184aafa 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -856,35 +856,54 @@ static void s3c24xx_serial_break_ctl(struct uart_port *port, int break_state) static int s3c24xx_serial_request_dma(struct s3c24xx_uart_port *p) { struct s3c24xx_uart_dma *dma = p->dma; + struct dma_slave_caps dma_caps; + const char *reason = NULL; int ret; /* Default slave configuration parameters */ dma->rx_conf.direction = DMA_DEV_TO_MEM; dma->rx_conf.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; dma->rx_conf.src_addr = p->port.mapbase + S3C2410_URXH; - dma->rx_conf.src_maxburst = 16; + dma->rx_conf.src_maxburst = 1; dma->tx_conf.direction = DMA_MEM_TO_DEV; dma->tx_conf.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; dma->tx_conf.dst_addr = p->port.mapbase + S3C2410_UTXH; - if (dma_get_cache_alignment() >= 16) - dma->tx_conf.dst_maxburst = 16; - else - dma->tx_conf.dst_maxburst = 1; + dma->tx_conf.dst_maxburst = 1; dma->rx_chan = dma_request_chan(p->port.dev, "rx"); - if (IS_ERR(dma->rx_chan)) - return PTR_ERR(dma->rx_chan); + if (IS_ERR(dma->rx_chan)) { + reason = "DMA RX channel request failed"; + ret = PTR_ERR(dma->rx_chan); + goto err_warn; + } + + ret = dma_get_slave_caps(dma->rx_chan, &dma_caps); + if (ret < 0 || + dma_caps.residue_granularity < DMA_RESIDUE_GRANULARITY_BURST) { + reason = "insufficient DMA RX engine capabilities"; + ret = -EOPNOTSUPP; + goto err_release_rx; + } dmaengine_slave_config(dma->rx_chan, &dma->rx_conf); dma->tx_chan = dma_request_chan(p->port.dev, "tx"); if (IS_ERR(dma->tx_chan)) { + reason = "DMA TX channel request failed"; ret = PTR_ERR(dma->tx_chan); goto err_release_rx; } + ret = dma_get_slave_caps(dma->tx_chan, &dma_caps); + if (ret < 0 || + dma_caps.residue_granularity < DMA_RESIDUE_GRANULARITY_BURST) { + reason = "insufficient DMA TX engine capabilities"; + ret = -EOPNOTSUPP; + goto err_release_tx; + } + dmaengine_slave_config(dma->tx_chan, &dma->tx_conf); /* RX buffer */ @@ -899,6 +918,7 @@ static int s3c24xx_serial_request_dma(struct s3c24xx_uart_port *p) dma->rx_addr = dma_map_single(p->port.dev, dma->rx_buf, dma->rx_size, DMA_FROM_DEVICE); if (dma_mapping_error(p->port.dev, dma->rx_addr)) { + reason = "DMA mapping error for RX buffer"; ret = -EIO; goto err_free_rx; } @@ -907,6 +927,7 @@ static int s3c24xx_serial_request_dma(struct s3c24xx_uart_port *p) dma->tx_addr = dma_map_single(p->port.dev, p->port.state->xmit.buf, UART_XMIT_SIZE, DMA_TO_DEVICE); if (dma_mapping_error(p->port.dev, dma->tx_addr)) { + reason = "DMA mapping error for TX buffer"; ret = -EIO; goto err_unmap_rx; } @@ -922,6 +943,9 @@ err_release_tx: dma_release_channel(dma->tx_chan); err_release_rx: dma_release_channel(dma->rx_chan); +err_warn: + if (reason) + dev_warn(p->port.dev, "%s, DMA will not be used\n", reason); return ret; } @@ -1040,8 +1064,6 @@ static int s3c64xx_serial_startup(struct uart_port *port) if (ourport->dma) { ret = s3c24xx_serial_request_dma(ourport); if (ret < 0) { - dev_warn(port->dev, - "DMA request failed, DMA will not be used\n"); devm_kfree(port->dev, ourport->dma); ourport->dma = NULL; } diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index 65792a3539d0..243c96025053 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -1168,7 +1168,10 @@ static int sc16is7xx_probe(struct device *dev, else return PTR_ERR(s->clk); } else { - clk_prepare_enable(s->clk); + ret = clk_prepare_enable(s->clk); + if (ret) + return ret; + freq = clk_get_rate(s->clk); } diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 6ff9405954a6..890b8832aff2 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -674,8 +674,8 @@ static void uart_send_xchar(struct tty_struct *tty, char ch) static void uart_throttle(struct tty_struct *tty) { struct uart_state *state = tty->driver_data; + upstat_t mask = UPSTAT_SYNC_FIFO; struct uart_port *port; - upstat_t mask = 0; port = uart_port_ref(state); if (!port) @@ -703,8 +703,8 @@ static void uart_throttle(struct tty_struct *tty) static void uart_unthrottle(struct tty_struct *tty) { struct uart_state *state = tty->driver_data; + upstat_t mask = UPSTAT_SYNC_FIFO; struct uart_port *port; - upstat_t mask = 0; port = uart_port_ref(state); if (!port) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index fdbbff547106..b46b146524ce 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -160,6 +160,7 @@ struct sci_port { #define SCI_NPORTS CONFIG_SERIAL_SH_SCI_NR_UARTS static struct sci_port sci_ports[SCI_NPORTS]; +static unsigned long sci_ports_in_use; static struct uart_driver sci_uart_driver; static inline struct sci_port * @@ -2390,6 +2391,27 @@ done: uart_update_timeout(port, termios->c_cflag, baud); + /* byte size and parity */ + switch (termios->c_cflag & CSIZE) { + case CS5: + bits = 7; + break; + case CS6: + bits = 8; + break; + case CS7: + bits = 9; + break; + default: + bits = 10; + break; + } + + if (termios->c_cflag & CSTOPB) + bits++; + if (termios->c_cflag & PARENB) + bits++; + if (best_clk >= 0) { if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) switch (srr + 1) { @@ -2406,8 +2428,27 @@ done: serial_port_out(port, SCSCR, scr_val | s->hscif_tot); serial_port_out(port, SCSMR, smr_val); serial_port_out(port, SCBRR, brr); - if (sci_getreg(port, HSSRR)->size) - serial_port_out(port, HSSRR, srr | HSCIF_SRE); + if (sci_getreg(port, HSSRR)->size) { + unsigned int hssrr = srr | HSCIF_SRE; + /* Calculate deviation from intended rate at the + * center of the last stop bit in sampling clocks. + */ + int last_stop = bits * 2 - 1; + int deviation = min_err * srr * last_stop / 2 / baud; + + if (abs(deviation) >= 2) { + /* At least two sampling clocks off at the + * last stop bit; we can increase the error + * margin by shifting the sampling point. + */ + int shift = min(-8, max(7, deviation / 2)); + + hssrr |= (shift << HSCIF_SRHP_SHIFT) & + HSCIF_SRHP_MASK; + hssrr |= HSCIF_SRDE; + } + serial_port_out(port, HSSRR, hssrr); + } /* Wait one bit interval */ udelay((1000000 + (baud - 1)) / baud); @@ -2474,27 +2515,6 @@ done: * value obtained by this formula is too small. Therefore, if the value * is smaller than 20ms, use 20ms as the timeout value for DMA. */ - /* byte size and parity */ - switch (termios->c_cflag & CSIZE) { - case CS5: - bits = 7; - break; - case CS6: - bits = 8; - break; - case CS7: - bits = 9; - break; - default: - bits = 10; - break; - } - - if (termios->c_cflag & CSTOPB) - bits++; - if (termios->c_cflag & PARENB) - bits++; - s->rx_frame = (10000 * bits) / (baud / 100); #ifdef CONFIG_SERIAL_SH_SCI_DMA s->rx_timeout = s->buf_len_rx * 2 * s->rx_frame; @@ -2890,16 +2910,15 @@ static void serial_console_write(struct console *co, const char *s, unsigned long flags; int locked = 1; - local_irq_save(flags); #if defined(SUPPORT_SYSRQ) if (port->sysrq) locked = 0; else #endif if (oops_in_progress) - locked = spin_trylock(&port->lock); + locked = spin_trylock_irqsave(&port->lock, flags); else - spin_lock(&port->lock); + spin_lock_irqsave(&port->lock, flags); /* first save SCSCR then disable interrupts, keep clock source */ ctrl = serial_port_in(port, SCSCR); @@ -2919,8 +2938,7 @@ static void serial_console_write(struct console *co, const char *s, serial_port_out(port, SCSCR, ctrl); if (locked) - spin_unlock(&port->lock); - local_irq_restore(flags); + spin_unlock_irqrestore(&port->lock, flags); } static int serial_console_setup(struct console *co, char *options) @@ -3026,6 +3044,7 @@ static int sci_remove(struct platform_device *dev) { struct sci_port *port = platform_get_drvdata(dev); + sci_ports_in_use &= ~BIT(port->port.line); uart_remove_one_port(&sci_uart_driver, &port->port); sci_cleanup_single(port); @@ -3107,6 +3126,8 @@ static struct plat_sci_port *sci_parse_dt(struct platform_device *pdev, /* Get the line number from the aliases node. */ id = of_alias_get_id(np, "serial"); + if (id < 0 && ~sci_ports_in_use) + id = ffz(sci_ports_in_use); if (id < 0) { dev_err(&pdev->dev, "failed to get alias id (%d)\n", id); return NULL; @@ -3141,6 +3162,9 @@ static int sci_probe_single(struct platform_device *dev, dev_notice(&dev->dev, "Consider bumping CONFIG_SERIAL_SH_SCI_NR_UARTS!\n"); return -EINVAL; } + BUILD_BUG_ON(SCI_NPORTS > sizeof(sci_ports_in_use) * 8); + if (sci_ports_in_use & BIT(index)) + return -EBUSY; mutex_lock(&sci_uart_registration_lock); if (!sci_uart_driver.state) { @@ -3239,6 +3263,7 @@ static int sci_probe(struct platform_device *dev) sh_bios_gdb_detach(); #endif + sci_ports_in_use |= BIT(dev_id); return 0; } diff --git a/drivers/tty/serial/sh-sci.h b/drivers/tty/serial/sh-sci.h index a5f792fd48d9..0b9e804e61a9 100644 --- a/drivers/tty/serial/sh-sci.h +++ b/drivers/tty/serial/sh-sci.h @@ -130,6 +130,10 @@ enum { /* HSSRR HSCIF */ #define HSCIF_SRE BIT(15) /* Sampling Rate Register Enable */ +#define HSCIF_SRDE BIT(14) /* Sampling Point Register Enable */ + +#define HSCIF_SRHP_SHIFT 8 +#define HSCIF_SRHP_MASK 0x0f00 /* SCPCR (Serial Port Control Register), SCIFA/SCIFB only */ #define SCPCR_RTSC BIT(4) /* Serial Port RTS# Pin / Output Pin */ diff --git a/drivers/tty/serial/st-asc.c b/drivers/tty/serial/st-asc.c index 5f9f01fac6dd..7971997cdead 100644 --- a/drivers/tty/serial/st-asc.c +++ b/drivers/tty/serial/st-asc.c @@ -842,16 +842,14 @@ static int asc_serial_remove(struct platform_device *pdev) #ifdef CONFIG_PM_SLEEP static int asc_serial_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct uart_port *port = platform_get_drvdata(pdev); + struct uart_port *port = dev_get_drvdata(dev); return uart_suspend_port(&asc_uart_driver, port); } static int asc_serial_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct uart_port *port = platform_get_drvdata(pdev); + struct uart_port *port = dev_get_drvdata(dev); return uart_resume_port(&asc_uart_driver, port); } diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index bd72dd843338..8a3e34234e98 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -1097,45 +1097,6 @@ static const struct uart_ops cdns_uart_ops = { #endif }; -static struct uart_port cdns_uart_port[CDNS_UART_NR_PORTS]; - -/** - * cdns_uart_get_port - Configure the port from platform device resource info - * @id: Port id - * - * Return: a pointer to a uart_port or NULL for failure - */ -static struct uart_port *cdns_uart_get_port(int id) -{ - struct uart_port *port; - - /* Try the given port id if failed use default method */ - if (id < CDNS_UART_NR_PORTS && cdns_uart_port[id].mapbase != 0) { - /* Find the next unused port */ - for (id = 0; id < CDNS_UART_NR_PORTS; id++) - if (cdns_uart_port[id].mapbase == 0) - break; - } - - if (id >= CDNS_UART_NR_PORTS) - return NULL; - - port = &cdns_uart_port[id]; - - /* At this point, we've got an empty uart_port struct, initialize it */ - spin_lock_init(&port->lock); - port->membase = NULL; - port->irq = 0; - port->type = PORT_UNKNOWN; - port->iotype = UPIO_MEM32; - port->flags = UPF_BOOT_AUTOCONF; - port->ops = &cdns_uart_ops; - port->fifosize = CDNS_UART_FIFO_SIZE; - port->line = id; - port->dev = NULL; - return port; -} - #ifdef CONFIG_SERIAL_XILINX_PS_UART_CONSOLE /** * cdns_uart_console_wait_tx - Wait for the TX to be full @@ -1206,6 +1167,10 @@ OF_EARLYCON_DECLARE(cdns, "cdns,uart-r1p8", cdns_early_console_setup); OF_EARLYCON_DECLARE(cdns, "cdns,uart-r1p12", cdns_early_console_setup); OF_EARLYCON_DECLARE(cdns, "xlnx,zynqmp-uart", cdns_early_console_setup); + +/* Static pointer to console port */ +static struct uart_port *console_port; + /** * cdns_uart_console_write - perform write operation * @co: Console handle @@ -1215,7 +1180,7 @@ OF_EARLYCON_DECLARE(cdns, "xlnx,zynqmp-uart", cdns_early_console_setup); static void cdns_uart_console_write(struct console *co, const char *s, unsigned int count) { - struct uart_port *port = &cdns_uart_port[co->index]; + struct uart_port *port = console_port; unsigned long flags; unsigned int imr, ctrl; int locked = 1; @@ -1261,15 +1226,13 @@ static void cdns_uart_console_write(struct console *co, const char *s, */ static int __init cdns_uart_console_setup(struct console *co, char *options) { - struct uart_port *port = &cdns_uart_port[co->index]; + struct uart_port *port = console_port; + int baud = 9600; int bits = 8; int parity = 'n'; int flow = 'n'; - if (co->index < 0 || co->index >= CDNS_UART_NR_PORTS) - return -EINVAL; - if (!port->membase) { pr_debug("console on " CDNS_UART_TTY_NAME "%i not present\n", co->index); @@ -1293,20 +1256,6 @@ static struct console cdns_uart_console = { .index = -1, /* Specified on the cmdline (e.g. console=ttyPS ) */ .data = &cdns_uart_uart_driver, }; - -/** - * cdns_uart_console_init - Initialization call - * - * Return: 0 on success, negative errno otherwise - */ -static int __init cdns_uart_console_init(void) -{ - register_console(&cdns_uart_console); - return 0; -} - -console_initcall(cdns_uart_console_init); - #endif /* CONFIG_SERIAL_XILINX_PS_UART_CONSOLE */ static struct uart_driver cdns_uart_uart_driver = { @@ -1430,8 +1379,7 @@ static int cdns_uart_resume(struct device *device) #endif /* ! CONFIG_PM_SLEEP */ static int __maybe_unused cdns_runtime_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct uart_port *port = platform_get_drvdata(pdev); + struct uart_port *port = dev_get_drvdata(dev); struct cdns_uart *cdns_uart = port->private_data; clk_disable(cdns_uart->uartclk); @@ -1441,8 +1389,7 @@ static int __maybe_unused cdns_runtime_suspend(struct device *dev) static int __maybe_unused cdns_runtime_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct uart_port *port = platform_get_drvdata(pdev); + struct uart_port *port = dev_get_drvdata(dev); struct cdns_uart *cdns_uart = port->private_data; clk_enable(cdns_uart->pclk); @@ -1487,6 +1434,9 @@ static int cdns_uart_probe(struct platform_device *pdev) GFP_KERNEL); if (!cdns_uart_data) return -ENOMEM; + port = devm_kzalloc(&pdev->dev, sizeof(*port), GFP_KERNEL); + if (!port) + return -ENOMEM; match = of_match_node(cdns_uart_of_match, pdev->dev.of_node); if (match && match->data) { @@ -1552,15 +1502,24 @@ static int cdns_uart_probe(struct platform_device *pdev) if (id < 0) id = 0; - /* Initialize the port structure */ - port = cdns_uart_get_port(id); - - if (!port) { + if (id >= CDNS_UART_NR_PORTS) { dev_err(&pdev->dev, "Cannot get uart_port structure\n"); rc = -ENODEV; goto err_out_notif_unreg; } + /* At this point, we've got an empty uart_port struct, initialize it */ + spin_lock_init(&port->lock); + port->membase = NULL; + port->irq = 0; + port->type = PORT_UNKNOWN; + port->iotype = UPIO_MEM32; + port->flags = UPF_BOOT_AUTOCONF; + port->ops = &cdns_uart_ops; + port->fifosize = CDNS_UART_FIFO_SIZE; + port->line = id; + port->dev = NULL; + /* * Register the port. * This function also registers this device with the tty layer @@ -1579,6 +1538,17 @@ static int cdns_uart_probe(struct platform_device *pdev) pm_runtime_set_active(&pdev->dev); pm_runtime_enable(&pdev->dev); +#ifdef CONFIG_SERIAL_XILINX_PS_UART_CONSOLE + /* + * If console hasn't been found yet try to assign this port + * because it is required to be assigned for console setup function. + * If register_console() don't assign value, then console_port pointer + * is cleanup. + */ + if (cdns_uart_uart_driver.cons->index == -1) + console_port = port; +#endif + rc = uart_add_one_port(&cdns_uart_uart_driver, port); if (rc) { dev_err(&pdev->dev, @@ -1586,6 +1556,12 @@ static int cdns_uart_probe(struct platform_device *pdev) goto err_out_pm_disable; } +#ifdef CONFIG_SERIAL_XILINX_PS_UART_CONSOLE + /* This is not port which is used for console that's why clean it up */ + if (cdns_uart_uart_driver.cons->index == -1) + console_port = NULL; +#endif + return 0; err_out_pm_disable: diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index f97251f39c26..1eb1a376a041 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -1178,15 +1178,8 @@ static void csi_J(struct vc_data *vc, int vpar) count = ((vc->vc_pos - vc->vc_origin) >> 1) + 1; start = (unsigned short *)vc->vc_origin; break; - case 3: /* erase scroll-back buffer (and whole display) */ - scr_memsetw(vc->vc_screenbuf, vc->vc_video_erase_char, - vc->vc_screenbuf_size); - flush_scrollback(vc); - set_origin(vc); - if (con_is_visible(vc)) - update_screen(vc); - /* fall through */ case 2: /* erase whole display */ + case 3: /* (and scrollback buffer later) */ count = vc->vc_cols * vc->vc_rows; start = (unsigned short *)vc->vc_origin; break; @@ -1194,7 +1187,12 @@ static void csi_J(struct vc_data *vc, int vpar) return; } scr_memsetw(start, vc->vc_video_erase_char, 2 * count); - if (con_should_update(vc)) + if (vpar == 3) { + set_origin(vc); + flush_scrollback(vc); + if (con_is_visible(vc)) + update_screen(vc); + } else if (con_should_update(vc)) do_update_region(vc, (unsigned long) start, count); vc->vc_need_wrap = 0; } |