From 0e125a5facf857567f8bb6dbb1ceefac14b2fa64 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Fri, 8 Jul 2016 17:00:39 +0800 Subject: tty: amba-pl011: define flag register bits for ZTE device For some reason we do not really understand, ZTE hardware designers choose to define PL011 Flag Register bit positions differently from standard ones as below. Bit Standard ZTE ----------------------------------- CTS 0 1 DSR 1 3 BUSY 3 8 RI 8 0 Let's define these bits into vendor data and get ZTE PL011 supported properly. Signed-off-by: Shawn Guo Acked-by: Russell King Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/amba-pl011.c | 45 ++++++++++++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 10 deletions(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 8a9e213387a7..3914ad0c2c26 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -93,6 +93,10 @@ static u16 pl011_std_offsets[REG_ARRAY_SIZE] = { struct vendor_data { const u16 *reg_offset; unsigned int ifls; + unsigned int fr_busy; + unsigned int fr_dsr; + unsigned int fr_cts; + unsigned int fr_ri; bool access_32b; bool oversampling; bool dma_threshold; @@ -111,6 +115,10 @@ static unsigned int get_fifosize_arm(struct amba_device *dev) static struct vendor_data vendor_arm = { .reg_offset = pl011_std_offsets, .ifls = UART011_IFLS_RX4_8|UART011_IFLS_TX4_8, + .fr_busy = UART01x_FR_BUSY, + .fr_dsr = UART01x_FR_DSR, + .fr_cts = UART01x_FR_CTS, + .fr_ri = UART011_FR_RI, .oversampling = false, .dma_threshold = false, .cts_event_workaround = false, @@ -121,6 +129,10 @@ static struct vendor_data vendor_arm = { static struct vendor_data vendor_sbsa = { .reg_offset = pl011_std_offsets, + .fr_busy = UART01x_FR_BUSY, + .fr_dsr = UART01x_FR_DSR, + .fr_cts = UART01x_FR_CTS, + .fr_ri = UART011_FR_RI, .access_32b = true, .oversampling = false, .dma_threshold = false, @@ -164,6 +176,10 @@ static unsigned int get_fifosize_st(struct amba_device *dev) static struct vendor_data vendor_st = { .reg_offset = pl011_st_offsets, .ifls = UART011_IFLS_RX_HALF|UART011_IFLS_TX_HALF, + .fr_busy = UART01x_FR_BUSY, + .fr_dsr = UART01x_FR_DSR, + .fr_cts = UART01x_FR_CTS, + .fr_ri = UART011_FR_RI, .oversampling = true, .dma_threshold = true, .cts_event_workaround = true, @@ -192,6 +208,10 @@ static struct vendor_data vendor_zte __maybe_unused = { .reg_offset = pl011_zte_offsets, .access_32b = true, .ifls = UART011_IFLS_RX4_8|UART011_IFLS_TX4_8, + .fr_busy = ZX_UART01x_FR_BUSY, + .fr_dsr = ZX_UART01x_FR_DSR, + .fr_cts = ZX_UART01x_FR_CTS, + .fr_ri = ZX_UART011_FR_RI, .get_fifosize = get_fifosize_arm, }; @@ -1167,7 +1187,7 @@ static void pl011_dma_shutdown(struct uart_amba_port *uap) return; /* Disable RX and TX DMA */ - while (pl011_read(uap, REG_FR) & UART01x_FR_BUSY) + while (pl011_read(uap, REG_FR) & uap->vendor->fr_busy) cpu_relax(); spin_lock_irq(&uap->port.lock); @@ -1416,11 +1436,12 @@ static void pl011_modem_status(struct uart_amba_port *uap) if (delta & UART01x_FR_DCD) uart_handle_dcd_change(&uap->port, status & UART01x_FR_DCD); - if (delta & UART01x_FR_DSR) + if (delta & uap->vendor->fr_dsr) uap->port.icount.dsr++; - if (delta & UART01x_FR_CTS) - uart_handle_cts_change(&uap->port, status & UART01x_FR_CTS); + if (delta & uap->vendor->fr_cts) + uart_handle_cts_change(&uap->port, + status & uap->vendor->fr_cts); wake_up_interruptible(&uap->port.state->port.delta_msr_wait); } @@ -1493,7 +1514,8 @@ static unsigned int pl011_tx_empty(struct uart_port *port) struct uart_amba_port *uap = container_of(port, struct uart_amba_port, port); unsigned int status = pl011_read(uap, REG_FR); - return status & (UART01x_FR_BUSY|UART01x_FR_TXFF) ? 0 : TIOCSER_TEMT; + return status & (uap->vendor->fr_busy | UART01x_FR_TXFF) ? + 0 : TIOCSER_TEMT; } static unsigned int pl011_get_mctrl(struct uart_port *port) @@ -1508,9 +1530,9 @@ static unsigned int pl011_get_mctrl(struct uart_port *port) result |= tiocmbit TIOCMBIT(UART01x_FR_DCD, TIOCM_CAR); - TIOCMBIT(UART01x_FR_DSR, TIOCM_DSR); - TIOCMBIT(UART01x_FR_CTS, TIOCM_CTS); - TIOCMBIT(UART011_FR_RI, TIOCM_RNG); + TIOCMBIT(uap->vendor->fr_dsr, TIOCM_DSR); + TIOCMBIT(uap->vendor->fr_cts, TIOCM_CTS); + TIOCMBIT(uap->vendor->fr_ri, TIOCM_RNG); #undef TIOCMBIT return result; } @@ -2191,7 +2213,7 @@ pl011_console_write(struct console *co, const char *s, unsigned int count) * Finally, wait for transmitter to become empty * and restore the TCR */ - while (pl011_read(uap, REG_FR) & UART01x_FR_BUSY) + while (pl011_read(uap, REG_FR) & uap->vendor->fr_busy) cpu_relax(); if (!uap->vendor->always_enabled) pl011_write(old_cr, uap, REG_CR); @@ -2303,13 +2325,16 @@ static struct console amba_console = { static void pl011_putc(struct uart_port *port, int c) { + struct uart_amba_port *uap = + container_of(port, struct uart_amba_port, port); + while (readl(port->membase + UART01x_FR) & UART01x_FR_TXFF) cpu_relax(); if (port->iotype == UPIO_MEM32) writel(c, port->membase + UART01x_DR); else writeb(c, port->membase + UART01x_DR); - while (readl(port->membase + UART01x_FR) & UART01x_FR_BUSY) + while (readl(port->membase + UART01x_FR) & uap->vendor->fr_busy) cpu_relax(); } -- cgit v1.2.3 From 9c267ddb41a6108761c82bf7444ea5e6eacc8222 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Fri, 8 Jul 2016 17:00:40 +0800 Subject: tty: amba-pl011: add .get_fifosize for ZTE device ZTE PL011 device has a fixed FIFO size 16. Let's add a .get_fifosize hook for it. Signed-off-by: Shawn Guo Acked-by: Russell King Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/amba-pl011.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 3914ad0c2c26..055fe27e364f 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -204,6 +204,11 @@ static const u16 pl011_zte_offsets[REG_ARRAY_SIZE] = { [REG_DMACR] = ZX_UART011_DMACR, }; +static unsigned int get_fifosize_zte(struct amba_device *dev) +{ + return 16; +} + static struct vendor_data vendor_zte __maybe_unused = { .reg_offset = pl011_zte_offsets, .access_32b = true, @@ -212,7 +217,7 @@ static struct vendor_data vendor_zte __maybe_unused = { .fr_dsr = ZX_UART01x_FR_DSR, .fr_cts = ZX_UART01x_FR_CTS, .fr_ri = ZX_UART011_FR_RI, - .get_fifosize = get_fifosize_arm, + .get_fifosize = get_fifosize_zte, }; /* Deals with DMA transactions */ -- cgit v1.2.3 From 2426fbc77faef57c80a573303af6543531e2efa3 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Fri, 8 Jul 2016 17:00:41 +0800 Subject: tty: amba-pl011: probe ZTE device from AMBA bus with a pseudo-ID There is no Peripheral Identification Registers on ZTE PL011 device, so although the driver amba-pl011 is ready to work for ZTE device, the device cannot be probed by the driver at all. With arm,primecell-periphid DT bindings (bindings/arm/primecell.txt) in place, it should be the cleanest the way to use a pseudo-ID to probe the device from AMBA bus. We create an unofficial vendor number AMBA_VENDOR_LINUX, which will practically never become an official vendor ID, and takes Configuration, Revision number, and Part number as input to compose a pseudo-ID for ZTE device. Also, since we start using vendor_zte to probe ZTE device, the __maybe_unused for vendor_zte is removed. Signed-off-by: Russell King Signed-off-by: Shawn Guo Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/amba-pl011.c | 7 ++++++- include/linux/amba/bus.h | 6 ++++++ 2 files changed, 12 insertions(+), 1 deletion(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 055fe27e364f..0b78b04e895e 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -209,7 +209,7 @@ static unsigned int get_fifosize_zte(struct amba_device *dev) return 16; } -static struct vendor_data vendor_zte __maybe_unused = { +static struct vendor_data vendor_zte = { .reg_offset = pl011_zte_offsets, .access_32b = true, .ifls = UART011_IFLS_RX4_8|UART011_IFLS_TX4_8, @@ -2652,6 +2652,11 @@ static struct amba_id pl011_ids[] = { .mask = 0x00ffffff, .data = &vendor_st, }, + { + .id = AMBA_LINUX_ID(0x00, 0x1, 0xffe), + .mask = 0x00ffffff, + .data = &vendor_zte, + }, { 0, 0 }, }; diff --git a/include/linux/amba/bus.h b/include/linux/amba/bus.h index 3d8dcdd1aeae..d143c13bed26 100644 --- a/include/linux/amba/bus.h +++ b/include/linux/amba/bus.h @@ -53,8 +53,14 @@ enum amba_vendor { AMBA_VENDOR_ST = 0x80, AMBA_VENDOR_QCOM = 0x51, AMBA_VENDOR_LSI = 0xb6, + AMBA_VENDOR_LINUX = 0xfe, /* This value is not official */ }; +/* This is used to generate pseudo-ID for AMBA device */ +#define AMBA_LINUX_ID(conf, rev, part) \ + (((conf) & 0xff) << 24 | ((rev) & 0xf) << 20 | \ + AMBA_VENDOR_LINUX << 12 | ((part) & 0xfff)) + extern struct bus_type amba_bustype; #define to_amba_device(d) container_of(d, struct amba_device, dev) -- cgit v1.2.3 From 5bf5635ac1705b8d58fdef5ff0666ef0e72b4629 Mon Sep 17 00:00:00 2001 From: Ludovic Desroches Date: Thu, 25 Aug 2016 15:47:56 +0200 Subject: tty/serial: atmel: add fractional baud rate support The USART device provides a fractional baud rate generator to get a more accurate baud rate. It can be used only when the USART is configured in 'normal mode' and this feature is not available on AT91RM9200 SoC. Signed-off-by: Ludovic Desroches Acked-by: Nicolas Ferre Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/atmel_serial.c | 41 +++++++++++++++++++++++++++++++-------- include/linux/atmel_serial.h | 1 + 2 files changed, 34 insertions(+), 8 deletions(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index 2eaa18ddef61..17592390e85c 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -166,6 +166,7 @@ struct atmel_uart_port { u32 rts_low; bool ms_irq_enabled; u32 rtor; /* address of receiver timeout register if it exists */ + bool has_frac_baudrate; bool has_hw_timer; struct timer_list uart_timer; @@ -1745,6 +1746,11 @@ static void atmel_get_ip_name(struct uart_port *port) dbgu_uart = 0x44424755; /* DBGU */ new_uart = 0x55415254; /* UART */ + /* + * Only USART devices from at91sam9260 SOC implement fractional + * baudrate. + */ + atmel_port->has_frac_baudrate = false; atmel_port->has_hw_timer = false; if (name == new_uart) { @@ -1753,6 +1759,7 @@ static void atmel_get_ip_name(struct uart_port *port) atmel_port->rtor = ATMEL_UA_RTOR; } else if (name == usart) { dev_dbg(port->dev, "Usart\n"); + atmel_port->has_frac_baudrate = true; atmel_port->has_hw_timer = true; atmel_port->rtor = ATMEL_US_RTOR; } else if (name == dbgu_uart) { @@ -1764,6 +1771,7 @@ static void atmel_get_ip_name(struct uart_port *port) case 0x302: case 0x10213: dev_dbg(port->dev, "This version is usart\n"); + atmel_port->has_frac_baudrate = true; atmel_port->has_hw_timer = true; atmel_port->rtor = ATMEL_US_RTOR; break; @@ -2025,8 +2033,9 @@ static void atmel_serial_pm(struct uart_port *port, unsigned int state, static void atmel_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); unsigned long flags; - unsigned int old_mode, mode, imr, quot, baud; + unsigned int old_mode, mode, imr, quot, baud, div, cd, fp = 0; /* save the current mode register */ mode = old_mode = atmel_uart_readl(port, ATMEL_US_MR); @@ -2036,12 +2045,6 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios, ATMEL_US_PAR | ATMEL_US_USMODE); baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16); - quot = uart_get_divisor(port, baud); - - if (quot > 65535) { /* BRGR is 16-bit, so switch to slower clock */ - quot /= 8; - mode |= ATMEL_US_USCLKS_MCK_DIV8; - } /* byte size */ switch (termios->c_cflag & CSIZE) { @@ -2160,7 +2163,29 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios, atmel_uart_writel(port, ATMEL_US_CR, rts_state); } - /* set the baud rate */ + /* + * Set the baud rate: + * Fractional baudrate allows to setup output frequency more + * accurately. This feature is enabled only when using normal mode. + * baudrate = selected clock / (8 * (2 - OVER) * (CD + FP / 8)) + * Currently, OVER is always set to 0 so we get + * baudrate = selected clock (16 * (CD + FP / 8)) + */ + if (atmel_port->has_frac_baudrate && + (mode & ATMEL_US_USMODE) == ATMEL_US_USMODE_NORMAL) { + div = DIV_ROUND_CLOSEST(port->uartclk, baud); + cd = div / 16; + fp = DIV_ROUND_CLOSEST(div % 16, 2); + } else { + cd = uart_get_divisor(port, baud); + } + + if (cd > 65535) { /* BRGR is 16-bit, so switch to slower clock */ + cd /= 8; + mode |= ATMEL_US_USCLKS_MCK_DIV8; + } + quot = cd | fp << ATMEL_US_FP_OFFSET; + atmel_uart_writel(port, ATMEL_US_BRGR, quot); atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_RSTSTA | ATMEL_US_RSTRX); atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN | ATMEL_US_RXEN); diff --git a/include/linux/atmel_serial.h b/include/linux/atmel_serial.h index 5a4d664af87a..f8e452aa48d7 100644 --- a/include/linux/atmel_serial.h +++ b/include/linux/atmel_serial.h @@ -118,6 +118,7 @@ #define ATMEL_US_BRGR 0x20 /* Baud Rate Generator Register */ #define ATMEL_US_CD GENMASK(15, 0) /* Clock Divider */ +#define ATMEL_US_FP_OFFSET 16 /* Fractional Part */ #define ATMEL_US_RTOR 0x24 /* Receiver Time-out Register for USART */ #define ATMEL_UA_RTOR 0x28 /* Receiver Time-out Register for UART */ -- cgit v1.2.3 From ebaa81c7287edd40dd4899e5d93a20e7a2b938e7 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 27 Jun 2016 13:59:08 +0200 Subject: serial: samsung: Register cpufreq notifier only on S3C24xx The Samsung serial driver registered for CPU frequency transitions to recalculate its clock when ARM clock frequency changes. This is needed only on S3C24xx platform so limit the ifdef to respective cpufreq driver. On S3C24xx the ratio ratio between frequencies of UART's parent clock (pclk) and ARM's parent clock (fclk) remains fixed. Therefore when ARM clock frequency goes down, the serial is also affected. Suggested-by: Marek Szyprowski Signed-off-by: Krzysztof Kozlowski Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/samsung.c | 2 +- drivers/tty/serial/samsung.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index ae2095a66708..f44615fa474d 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -1577,7 +1577,7 @@ static void s3c24xx_serial_resetport(struct uart_port *port, } -#ifdef CONFIG_CPU_FREQ +#ifdef CONFIG_ARM_S3C24XX_CPUFREQ static int s3c24xx_serial_cpufreq_transition(struct notifier_block *nb, unsigned long val, void *data) diff --git a/drivers/tty/serial/samsung.h b/drivers/tty/serial/samsung.h index 2ae4fcee1814..a04acef1cb20 100644 --- a/drivers/tty/serial/samsung.h +++ b/drivers/tty/serial/samsung.h @@ -102,7 +102,7 @@ struct s3c24xx_uart_port { struct s3c24xx_uart_dma *dma; -#ifdef CONFIG_CPU_FREQ +#ifdef CONFIG_ARM_S3C24XX_CPUFREQ struct notifier_block freq_transition; #endif }; -- cgit v1.2.3 From b3b57646186400d4f54652ab7bbf55f5764d9467 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Mon, 22 Aug 2016 17:39:09 -0500 Subject: tty: serial_core: convert uart_open to use tty_port_open tty_port_open handles much of the common parts of tty opening. Convert uart_open to use it and move the serial_core specific parts into tty_port.activate function. This will be needed to use tty_port functions directly from in kernel clients. The tricky part is uart_port_startup can return positive values to allow setserial to configure the port. We now return the positive value to tty_port_open so that the tty is not marked as initialized and then set the return value in uart_open to 0. Cc: Peter Hurley Signed-off-by: Rob Herring Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_core.c | 70 +++++++++++++--------------------------- 1 file changed, 23 insertions(+), 47 deletions(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 9fc15335c8c5..0468725a1dd3 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -235,18 +235,9 @@ static int uart_startup(struct tty_struct *tty, struct uart_state *state, if (tty_port_initialized(port)) return 0; - /* - * Set the TTY IO error marker - we will only clear this - * once we have successfully opened the port. - */ - set_bit(TTY_IO_ERROR, &tty->flags); - retval = uart_port_startup(tty, state, init_hw); - if (!retval) { - tty_port_set_initialized(port, 1); - clear_bit(TTY_IO_ERROR, &tty->flags); - } else if (retval > 0) - retval = 0; + if (retval) + set_bit(TTY_IO_ERROR, &tty->flags); return retval; } @@ -972,8 +963,11 @@ static int uart_set_info(struct tty_struct *tty, struct tty_port *port, } uart_change_speed(tty, state, NULL); } - } else + } else { retval = uart_startup(tty, state, 1); + if (retval > 0) + retval = 0; + } exit: return retval; } @@ -1139,6 +1133,8 @@ static int uart_do_autoconfig(struct tty_struct *tty,struct uart_state *state) uport->ops->config_port(uport, flags); ret = uart_startup(tty, state, 1); + if (ret > 0) + ret = 0; } out: mutex_unlock(&port->mutex); @@ -1711,52 +1707,31 @@ static int uart_open(struct tty_struct *tty, struct file *filp) struct uart_driver *drv = tty->driver->driver_state; int retval, line = tty->index; struct uart_state *state = drv->state + line; - struct tty_port *port = &state->port; - struct uart_port *uport; - pr_debug("uart_open(%d) called\n", line); + tty->driver_data = state; - spin_lock_irq(&port->lock); - ++port->count; - spin_unlock_irq(&port->lock); + retval = tty_port_open(&state->port, tty, filp); + if (retval > 0) + retval = 0; - /* - * We take the semaphore here to guarantee that we won't be re-entered - * while allocating the state structure, or while we request any IRQs - * that the driver may need. This also has the nice side-effect that - * it delays the action of uart_hangup, so we can guarantee that - * state->port.tty will always contain something reasonable. - */ - if (mutex_lock_interruptible(&port->mutex)) { - retval = -ERESTARTSYS; - goto end; - } + return retval; +} + +static int uart_port_activate(struct tty_port *port, struct tty_struct *tty) +{ + struct uart_state *state = container_of(port, struct uart_state, port); + struct uart_port *uport; uport = uart_port_check(state); - if (!uport || uport->flags & UPF_DEAD) { - retval = -ENXIO; - goto err_unlock; - } + if (!uport || uport->flags & UPF_DEAD) + return -ENXIO; - tty->driver_data = state; - uport->state = state; port->low_latency = (uport->flags & UPF_LOW_LATENCY) ? 1 : 0; - tty_port_tty_set(port, tty); /* * Start up the serial port. */ - retval = uart_startup(tty, state, 0); - - /* - * If we succeeded, wait until the port is ready. - */ -err_unlock: - mutex_unlock(&port->mutex); - if (retval == 0) - retval = tty_port_block_til_ready(port, tty, filp); -end: - return retval; + return uart_startup(tty, state, 0); } static const char *uart_type(struct uart_port *port) @@ -2470,6 +2445,7 @@ static const struct tty_operations uart_ops = { static const struct tty_port_operations uart_port_ops = { .carrier_raised = uart_carrier_raised, .dtr_rts = uart_dtr_rts, + .activate = uart_port_activate, }; /** -- cgit v1.2.3 From 761ed4a94582ab291aa24dcbea4e01e8936488c8 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Mon, 22 Aug 2016 17:39:10 -0500 Subject: tty: serial_core: convert uart_close to use tty_port_close tty_port_close handles much of the common parts of tty close. Convert uart_close to use it and move the serial_core specific parts into tty_port.shutdown function. This will be needed to use tty_port functions directly from in kernel clients. This change causes ops->stop_rx() to be called after uart_wait_until_sent() is called which I think should be fine. Otherwise, the sequence of the close should be the same. Cc: Peter Hurley Signed-off-by: Rob Herring Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_core.c | 56 ++++++++++++++-------------------------- 1 file changed, 19 insertions(+), 37 deletions(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 0468725a1dd3..e183d2eff16d 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -1461,7 +1461,6 @@ static void uart_close(struct tty_struct *tty, struct file *filp) { struct uart_state *state = tty->driver_data; struct tty_port *port; - struct uart_port *uport; if (!state) { struct uart_driver *drv = tty->driver->driver_state; @@ -1477,56 +1476,36 @@ static void uart_close(struct tty_struct *tty, struct file *filp) port = &state->port; pr_debug("uart_close(%d) called\n", tty->index); - if (tty_port_close_start(port, tty, filp) == 0) - return; + tty_port_close(tty->port, tty, filp); +} - mutex_lock(&port->mutex); - uport = uart_port_check(state); +static void uart_tty_port_shutdown(struct tty_port *port) +{ + struct uart_state *state = container_of(port, struct uart_state, port); + struct uart_port *uport = uart_port_check(state); + spin_lock_irq(&uport->lock); /* * At this point, we stop accepting input. To do this, we * disable the receive line status interrupts. */ - if (tty_port_initialized(port) && - !WARN(!uport, "detached port still initialized!\n")) { - spin_lock_irq(&uport->lock); - uport->ops->stop_rx(uport); - spin_unlock_irq(&uport->lock); - /* - * Before we drop DTR, make sure the UART transmitter - * has completely drained; this is especially - * important if there is a transmit FIFO! - */ - uart_wait_until_sent(tty, uport->timeout); - } + WARN(!uport, "detached port still initialized!\n"); - uart_shutdown(tty, state); - tty_port_tty_set(port, NULL); + uport->ops->stop_rx(uport); - spin_lock_irq(&port->lock); + spin_unlock_irq(&uport->lock); - if (port->blocked_open) { - spin_unlock_irq(&port->lock); - if (port->close_delay) - msleep_interruptible(jiffies_to_msecs(port->close_delay)); - spin_lock_irq(&port->lock); - } else if (uport && !uart_console(uport)) { - spin_unlock_irq(&port->lock); - uart_change_pm(state, UART_PM_STATE_OFF); - spin_lock_irq(&port->lock); - } - spin_unlock_irq(&port->lock); - tty_port_set_active(port, 0); + uart_port_shutdown(port); /* - * Wake up anyone trying to open this port. + * It's possible for shutdown to be called after suspend if we get + * a DCD drop (hangup) at just the right time. Clear suspended bit so + * we don't try to resume a port that has been shutdown. */ - wake_up_interruptible(&port->open_wait); + tty_port_set_suspended(port, 0); - mutex_unlock(&port->mutex); + uart_change_pm(state, UART_PM_STATE_OFF); - tty_ldisc_flush(tty); - tty->closing = 0; } static void uart_wait_until_sent(struct tty_struct *tty, int timeout) @@ -2446,6 +2425,7 @@ static const struct tty_port_operations uart_port_ops = { .carrier_raised = uart_carrier_raised, .dtr_rts = uart_dtr_rts, .activate = uart_port_activate, + .shutdown = uart_tty_port_shutdown, }; /** @@ -2762,6 +2742,8 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport) uport->cons = drv->cons; uport->minor = drv->tty_driver->minor_start + uport->line; + port->console = uart_console(uport); + /* * If this port is a console, then the spinlock is already * initialised. -- cgit v1.2.3 From 0b1221a36ca53dca5992caf6b37f67c98bb4e431 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Wed, 24 Aug 2016 07:06:58 +0200 Subject: serial: vt8500_serial: Fix a parameter of find_first_zero_bit. The 2nd parameter of 'find_first_zero_bit' is the number of bits to search. In this case, we are passing 'sizeof(vt8500_ports_in_use)'. 'vt8500_ports_in_use' is an 'unsigned long'. So the sizeof is likely to return 4 on a 32 bits kernel. A few lines below, we check if it is below VT8500_MAX_PORTS, which is 6. It is likely that the number of bits in a long was expected here. In order to fix it: - use DECLARE_BITMAP when declaring the vt8500_ports_in_use - use VT8500_MAX_PORTS as a maximum value when checking/setting bits in this bitmap - modify code now that 'vt8500_ports_in_use' has become a pointer because of the use of DECLARE_BITMAP It has been spotted by the following coccinelle script: @@ expression ret, x; @@ * ret = \(find_first_bit \| find_first_zero_bit\) (x, sizeof(...)); Signed-off-by: Christophe JAILLET Reviewed-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/vt8500_serial.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/vt8500_serial.c b/drivers/tty/serial/vt8500_serial.c index 23cfc5e16b45..6b85adce0ac9 100644 --- a/drivers/tty/serial/vt8500_serial.c +++ b/drivers/tty/serial/vt8500_serial.c @@ -118,7 +118,7 @@ struct vt8500_port { * have been allocated as we can't use pdev->id in * devicetree */ -static unsigned long vt8500_ports_in_use; +static DECLARE_BITMAP(vt8500_ports_in_use, VT8500_MAX_PORTS); static inline void vt8500_write(struct uart_port *port, unsigned int val, unsigned int off) @@ -663,15 +663,15 @@ static int vt8500_serial_probe(struct platform_device *pdev) if (port < 0) { /* calculate the port id */ - port = find_first_zero_bit(&vt8500_ports_in_use, - sizeof(vt8500_ports_in_use)); + port = find_first_zero_bit(vt8500_ports_in_use, + VT8500_MAX_PORTS); } if (port >= VT8500_MAX_PORTS) return -ENODEV; /* reserve the port id */ - if (test_and_set_bit(port, &vt8500_ports_in_use)) { + if (test_and_set_bit(port, vt8500_ports_in_use)) { /* port already in use - shouldn't really happen */ return -EBUSY; } -- cgit v1.2.3 From e16b46f190a22587898b331f9d58583b0b166c9a Mon Sep 17 00:00:00 2001 From: Kefeng Wang Date: Wed, 24 Aug 2016 16:33:33 +0800 Subject: serial: 8250_dw: Check the data->pclk when get apb_pclk It should check the data->pclk, not data->clk when get apb_pclk. Fixes: c8ed99d4f6a8("serial: 8250_dw: Add support for deferred probing") Signed-off-by: Kefeng Wang Tested-by: Andy Shevchenko Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_dw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index e19969614203..b022f5a01e63 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -462,7 +462,7 @@ static int dw8250_probe(struct platform_device *pdev) } data->pclk = devm_clk_get(&pdev->dev, "apb_pclk"); - if (IS_ERR(data->clk) && PTR_ERR(data->clk) == -EPROBE_DEFER) { + if (IS_ERR(data->pclk) && PTR_ERR(data->pclk) == -EPROBE_DEFER) { err = -EPROBE_DEFER; goto err_clk; } -- cgit v1.2.3 From b70b636186de5329a5a8e9e9a254b8d690d8575b Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Tue, 19 Jul 2016 13:13:03 +0530 Subject: tty: serial: fsl_lpuart: consider TX FIFO too in tx_empty Currently the tx_empty callback only considers the Transmit Complete Flag (TC). The reference manual is not quite clear if the TC flag covers the TX FIFO too. Debug prints on real hardware have shown that from time to time the TC flag is asserted (indicating Transmitter idle) while there are still data in the TX FIFO. Hence, in this case the serial core will call the shutdown callback even though there are data remaining in the TX FIFO buffers. Avoid early shutdowns by considering the TX FIFO empty flag too. Also avoid theoretical race conditions between DMA and the driver by checking whether the TX DMA is in progress too. Signed-off-by: Stefan Agner Signed-off-by: Bhuvanchandra DV Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/fsl_lpuart.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 7f95f782a485..35592ff565fb 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -810,8 +810,18 @@ static irqreturn_t lpuart32_int(int irq, void *dev_id) /* return TIOCSER_TEMT when transmitter is not busy */ static unsigned int lpuart_tx_empty(struct uart_port *port) { - return (readb(port->membase + UARTSR1) & UARTSR1_TC) ? - TIOCSER_TEMT : 0; + struct lpuart_port *sport = container_of(port, + struct lpuart_port, port); + unsigned char sr1 = readb(port->membase + UARTSR1); + unsigned char sfifo = readb(port->membase + UARTSFIFO); + + if (sport->dma_tx_in_progress) + return 0; + + if (sr1 & UARTSR1_TC && sfifo & UARTSFIFO_TXEMPT) + return TIOCSER_TEMT; + + return 0; } static unsigned int lpuart32_tx_empty(struct uart_port *port) -- cgit v1.2.3 From d6b0d2f243fef6cb87f5338bf06c2330175f106f Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Tue, 19 Jul 2016 13:13:04 +0530 Subject: tty: serial: fsl_lpuart: support suspend/resume Add suspend/resume support. Signed-off-by: Stefan Agner Signed-off-by: Bhuvanchandra DV Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/fsl_lpuart.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 35592ff565fb..e46cffddcf7c 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -483,9 +483,8 @@ static void lpuart_dma_rx_complete(void *arg) spin_unlock_irqrestore(&sport->port.lock, flags); } -static void lpuart_timer_func(unsigned long data) +static void lpuart_dma_rx_terminate(struct lpuart_port *sport) { - struct lpuart_port *sport = (struct lpuart_port *)data; struct tty_port *port = &sport->port.state->port; struct dma_tx_state state; unsigned long flags; @@ -510,6 +509,11 @@ static void lpuart_timer_func(unsigned long data) spin_unlock_irqrestore(&sport->port.lock, flags); } +static void lpuart_timer_func(unsigned long data) +{ + lpuart_dma_rx_terminate((struct lpuart_port *)data); +} + static inline void lpuart_prepare_rx(struct lpuart_port *sport) { unsigned long flags; @@ -1931,7 +1935,12 @@ static int lpuart_suspend(struct device *dev) writeb(temp, sport->port.membase + UARTCR2); } + if (sport->dma_rx_in_progress) + lpuart_dma_rx_terminate(sport); + uart_suspend_port(&lpuart_reg, &sport->port); + if (sport->port.suspended && !sport->port.irq_wake) + clk_disable_unprepare(sport->clk); return 0; } @@ -1941,6 +1950,9 @@ static int lpuart_resume(struct device *dev) struct lpuart_port *sport = dev_get_drvdata(dev); unsigned long temp; + if (sport->port.suspended && !sport->port.irq_wake) + clk_prepare_enable(sport->clk); + if (sport->lpuart32) { lpuart32_setup_watermark(sport); temp = lpuart32_read(sport->port.membase + UARTCTRL); -- cgit v1.2.3 From d68827c62a105eec547945daedf4d1d3e283717d Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Tue, 19 Jul 2016 13:13:05 +0530 Subject: tty: serial: fsl_lpuart: fix clearing of receive flag Commit 8e4934c6d6c6 ("tty: serial: fsl_lpuart: clear receive flag on FIFO flush") implemented clearing of the receive flag by reading the status register only. It turned out that even though we flush the FIFO afterwards, a explicit read of the data register is still required. This leads to a FIFO underrun. To avoid this, follow the advice in the overrun "Operation section": Unconditionally clear RXUF after using RXFLUSH. Signed-off-by: Stefan Agner Signed-off-by: Bhuvanchandra DV Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/fsl_lpuart.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index e46cffddcf7c..71646323e71b 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -935,13 +935,16 @@ static void lpuart_setup_watermark(struct lpuart_port *sport) writeb(val | UARTPFIFO_TXFE | UARTPFIFO_RXFE, sport->port.membase + UARTPFIFO); - /* explicitly clear RDRF */ - readb(sport->port.membase + UARTSR1); - /* flush Tx and Rx FIFO */ writeb(UARTCFIFO_TXFLUSH | UARTCFIFO_RXFLUSH, sport->port.membase + UARTCFIFO); + /* explicitly clear RDRF */ + if (readb(sport->port.membase + UARTSR1) & UARTSR1_RDRF) { + readb(sport->port.membase + UARTDR); + writeb(UARTSFIFO_RXUF, sport->port.membase + UARTSFIFO); + } + writeb(0, sport->port.membase + UARTTWFIFO); writeb(1, sport->port.membase + UARTRWFIFO); -- cgit v1.2.3 From aa9e7d78039e34ac0f56b370e21ac719482823ee Mon Sep 17 00:00:00 2001 From: Bhuvanchandra DV Date: Tue, 19 Jul 2016 13:13:06 +0530 Subject: tty: serial: fsl_lpuart: Fix broken 8m/s1 support By default the driver always configure the mode as 8s1 even when 8m1 mode is selected. Fix this by adding support to control the space/mark bit. Signed-off-by: Bhuvanchandra DV Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/fsl_lpuart.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 71646323e71b..5608b82b7f2d 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -1220,13 +1220,14 @@ lpuart_set_termios(struct uart_port *port, struct ktermios *termios, { struct lpuart_port *sport = container_of(port, struct lpuart_port, port); unsigned long flags; - unsigned char cr1, old_cr1, old_cr2, cr4, bdh, modem; + unsigned char cr1, old_cr1, old_cr2, cr3, cr4, bdh, modem; unsigned int baud; unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8; unsigned int sbr, brfa; cr1 = old_cr1 = readb(sport->port.membase + UARTCR1); old_cr2 = readb(sport->port.membase + UARTCR2); + cr3 = readb(sport->port.membase + UARTCR3); cr4 = readb(sport->port.membase + UARTCR4); bdh = readb(sport->port.membase + UARTBDH); modem = readb(sport->port.membase + UARTMODEM); @@ -1274,7 +1275,10 @@ lpuart_set_termios(struct uart_port *port, struct ktermios *termios, if ((termios->c_cflag & PARENB)) { if (termios->c_cflag & CMSPAR) { cr1 &= ~UARTCR1_PE; - cr1 |= UARTCR1_M; + if (termios->c_cflag & PARODD) + cr3 |= UARTCR3_T8; + else + cr3 &= ~UARTCR3_T8; } else { cr1 |= UARTCR1_PE; if ((termios->c_cflag & CSIZE) == CS8) @@ -1342,6 +1346,7 @@ lpuart_set_termios(struct uart_port *port, struct ktermios *termios, writeb(cr4 | brfa, sport->port.membase + UARTCR4); writeb(bdh, sport->port.membase + UARTBDH); writeb(sbr & 0xFF, sport->port.membase + UARTBDL); + writeb(cr3, sport->port.membase + UARTCR3); writeb(cr1, sport->port.membase + UARTCR1); writeb(modem, sport->port.membase + UARTMODEM); -- cgit v1.2.3 From 5887ad43ee02a00f17a6132b7fb256dc6865474c Mon Sep 17 00:00:00 2001 From: Bhuvanchandra DV Date: Tue, 19 Jul 2016 13:13:07 +0530 Subject: tty: serial: fsl_lpuart: Use cyclic DMA for Rx The initial approach of DMA implementatin for RX is inefficient due to switching from PIO to DMA, this leads to overruns especially on instances with the smaller FIFO. To address these issues this patch uses a cyclic DMA for receiver path. Some part of the code is borrowed from atmel serial driver. Signed-off-by: Bhuvanchandra DV Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/fsl_lpuart.c | 483 +++++++++++++++++++++------------------- 1 file changed, 258 insertions(+), 225 deletions(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 5608b82b7f2d..79965706d97f 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -224,7 +224,8 @@ #define UARTWATER_TXWATER_OFF 0 #define UARTWATER_RXWATER_OFF 16 -#define FSL_UART_RX_DMA_BUFFER_SIZE 64 +/* Rx DMA timeout in ms, which is used to calculate Rx ring buffer size */ +#define DMA_RX_TIMEOUT (10) #define DRIVER_NAME "fsl-lpuart" #define DEV_NAME "ttyLP" @@ -244,17 +245,17 @@ struct lpuart_port { struct dma_async_tx_descriptor *dma_tx_desc; struct dma_async_tx_descriptor *dma_rx_desc; dma_addr_t dma_tx_buf_bus; - dma_addr_t dma_rx_buf_bus; dma_cookie_t dma_tx_cookie; dma_cookie_t dma_rx_cookie; unsigned char *dma_tx_buf_virt; - unsigned char *dma_rx_buf_virt; unsigned int dma_tx_bytes; unsigned int dma_rx_bytes; int dma_tx_in_progress; - int dma_rx_in_progress; unsigned int dma_rx_timeout; struct timer_list lpuart_timer; + struct scatterlist rx_sgl; + struct circ_buf rx_ring; + int rx_dma_rng_buf_len; }; static const struct of_device_id lpuart_dt_ids[] = { @@ -270,7 +271,6 @@ MODULE_DEVICE_TABLE(of, lpuart_dt_ids); /* Forward declare this for the dma callbacks*/ static void lpuart_dma_tx_complete(void *arg); -static void lpuart_dma_rx_complete(void *arg); static u32 lpuart32_read(void __iomem *addr) { @@ -316,32 +316,6 @@ static void lpuart32_stop_rx(struct uart_port *port) lpuart32_write(temp & ~UARTCTRL_RE, port->membase + UARTCTRL); } -static void lpuart_copy_rx_to_tty(struct lpuart_port *sport, - struct tty_port *tty, int count) -{ - int copied; - - sport->port.icount.rx += count; - - if (!tty) { - dev_err(sport->port.dev, "No tty port\n"); - return; - } - - dma_sync_single_for_cpu(sport->port.dev, sport->dma_rx_buf_bus, - FSL_UART_RX_DMA_BUFFER_SIZE, DMA_FROM_DEVICE); - copied = tty_insert_flip_string(tty, - ((unsigned char *)(sport->dma_rx_buf_virt)), count); - - if (copied != count) { - WARN_ON(1); - dev_err(sport->port.dev, "RxData copy to tty layer failed\n"); - } - - dma_sync_single_for_device(sport->port.dev, sport->dma_rx_buf_bus, - FSL_UART_RX_DMA_BUFFER_SIZE, DMA_TO_DEVICE); -} - static void lpuart_pio_tx(struct lpuart_port *sport) { struct circ_buf *xmit = &sport->port.state->xmit; @@ -433,28 +407,6 @@ static void lpuart_dma_tx_complete(void *arg) spin_unlock_irqrestore(&sport->port.lock, flags); } -static int lpuart_dma_rx(struct lpuart_port *sport) -{ - dma_sync_single_for_device(sport->port.dev, sport->dma_rx_buf_bus, - FSL_UART_RX_DMA_BUFFER_SIZE, DMA_TO_DEVICE); - sport->dma_rx_desc = dmaengine_prep_slave_single(sport->dma_rx_chan, - sport->dma_rx_buf_bus, FSL_UART_RX_DMA_BUFFER_SIZE, - DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT); - - if (!sport->dma_rx_desc) { - dev_err(sport->port.dev, "Not able to get desc for rx\n"); - return -EIO; - } - - sport->dma_rx_desc->callback = lpuart_dma_rx_complete; - sport->dma_rx_desc->callback_param = sport; - sport->dma_rx_in_progress = 1; - sport->dma_rx_cookie = dmaengine_submit(sport->dma_rx_desc); - dma_async_issue_pending(sport->dma_rx_chan); - - return 0; -} - static void lpuart_flush_buffer(struct uart_port *port) { struct lpuart_port *sport = container_of(port, struct lpuart_port, port); @@ -464,73 +416,6 @@ static void lpuart_flush_buffer(struct uart_port *port) } } -static void lpuart_dma_rx_complete(void *arg) -{ - struct lpuart_port *sport = arg; - struct tty_port *port = &sport->port.state->port; - unsigned long flags; - - async_tx_ack(sport->dma_rx_desc); - mod_timer(&sport->lpuart_timer, jiffies + sport->dma_rx_timeout); - - spin_lock_irqsave(&sport->port.lock, flags); - - sport->dma_rx_in_progress = 0; - lpuart_copy_rx_to_tty(sport, port, FSL_UART_RX_DMA_BUFFER_SIZE); - tty_flip_buffer_push(port); - lpuart_dma_rx(sport); - - spin_unlock_irqrestore(&sport->port.lock, flags); -} - -static void lpuart_dma_rx_terminate(struct lpuart_port *sport) -{ - struct tty_port *port = &sport->port.state->port; - struct dma_tx_state state; - unsigned long flags; - unsigned char temp; - int count; - - del_timer(&sport->lpuart_timer); - dmaengine_pause(sport->dma_rx_chan); - dmaengine_tx_status(sport->dma_rx_chan, sport->dma_rx_cookie, &state); - dmaengine_terminate_all(sport->dma_rx_chan); - count = FSL_UART_RX_DMA_BUFFER_SIZE - state.residue; - async_tx_ack(sport->dma_rx_desc); - - spin_lock_irqsave(&sport->port.lock, flags); - - sport->dma_rx_in_progress = 0; - lpuart_copy_rx_to_tty(sport, port, count); - tty_flip_buffer_push(port); - temp = readb(sport->port.membase + UARTCR5); - writeb(temp & ~UARTCR5_RDMAS, sport->port.membase + UARTCR5); - - spin_unlock_irqrestore(&sport->port.lock, flags); -} - -static void lpuart_timer_func(unsigned long data) -{ - lpuart_dma_rx_terminate((struct lpuart_port *)data); -} - -static inline void lpuart_prepare_rx(struct lpuart_port *sport) -{ - unsigned long flags; - unsigned char temp; - - spin_lock_irqsave(&sport->port.lock, flags); - - sport->lpuart_timer.expires = jiffies + sport->dma_rx_timeout; - add_timer(&sport->lpuart_timer); - - lpuart_dma_rx(sport); - temp = readb(sport->port.membase + UARTCR5); - writeb(temp | UARTCR5_RDMAS, sport->port.membase + UARTCR5); - - spin_unlock_irqrestore(&sport->port.lock, flags); -} - static inline void lpuart_transmit_buffer(struct lpuart_port *sport) { struct circ_buf *xmit = &sport->port.state->xmit; @@ -770,18 +655,14 @@ out: static irqreturn_t lpuart_int(int irq, void *dev_id) { struct lpuart_port *sport = dev_id; - unsigned char sts, crdma; + unsigned char sts; sts = readb(sport->port.membase + UARTSR1); - crdma = readb(sport->port.membase + UARTCR5); - if (sts & UARTSR1_RDRF && !(crdma & UARTCR5_RDMAS)) { - if (sport->lpuart_dma_rx_use) - lpuart_prepare_rx(sport); - else - lpuart_rxint(irq, dev_id); - } - if (sts & UARTSR1_TDRE && !(crdma & UARTCR5_TDMAS)) { + if (sts & UARTSR1_RDRF) + lpuart_rxint(irq, dev_id); + + if (sts & UARTSR1_TDRE) { if (sport->lpuart_dma_tx_use) lpuart_pio_tx(sport); else @@ -834,6 +715,209 @@ static unsigned int lpuart32_tx_empty(struct uart_port *port) TIOCSER_TEMT : 0; } +static void lpuart_copy_rx_to_tty(struct lpuart_port *sport) +{ + struct tty_port *port = &sport->port.state->port; + struct dma_tx_state state; + enum dma_status dmastat; + struct circ_buf *ring = &sport->rx_ring; + unsigned long flags; + int count = 0; + unsigned char sr; + + sr = readb(sport->port.membase + UARTSR1); + + if (sr & (UARTSR1_PE | UARTSR1_FE)) { + /* Read DR to clear the error flags */ + readb(sport->port.membase + UARTDR); + + if (sr & UARTSR1_PE) + sport->port.icount.parity++; + else if (sr & UARTSR1_FE) + sport->port.icount.frame++; + } + + async_tx_ack(sport->dma_rx_desc); + + spin_lock_irqsave(&sport->port.lock, flags); + + dmastat = dmaengine_tx_status(sport->dma_rx_chan, + sport->dma_rx_cookie, + &state); + + if (dmastat == DMA_ERROR) { + dev_err(sport->port.dev, "Rx DMA transfer failed!\n"); + spin_unlock_irqrestore(&sport->port.lock, flags); + return; + } + + /* CPU claims ownership of RX DMA buffer */ + dma_sync_sg_for_cpu(sport->port.dev, &sport->rx_sgl, 1, DMA_FROM_DEVICE); + + /* + * ring->head points to the end of data already written by the DMA. + * ring->tail points to the beginning of data to be read by the + * framework. + * The current transfer size should not be larger than the dma buffer + * length. + */ + ring->head = sport->rx_sgl.length - state.residue; + BUG_ON(ring->head > sport->rx_sgl.length); + /* + * At this point ring->head may point to the first byte right after the + * last byte of the dma buffer: + * 0 <= ring->head <= sport->rx_sgl.length + * + * However ring->tail must always points inside the dma buffer: + * 0 <= ring->tail <= sport->rx_sgl.length - 1 + * + * Since we use a ring buffer, we have to handle the case + * where head is lower than tail. In such a case, we first read from + * tail to the end of the buffer then reset tail. + */ + if (ring->head < ring->tail) { + count = sport->rx_sgl.length - ring->tail; + + tty_insert_flip_string(port, ring->buf + ring->tail, count); + ring->tail = 0; + sport->port.icount.rx += count; + } + + /* Finally we read data from tail to head */ + if (ring->tail < ring->head) { + count = ring->head - ring->tail; + tty_insert_flip_string(port, ring->buf + ring->tail, count); + /* Wrap ring->head if needed */ + if (ring->head >= sport->rx_sgl.length) + ring->head = 0; + ring->tail = ring->head; + sport->port.icount.rx += count; + } + + dma_sync_sg_for_device(sport->port.dev, &sport->rx_sgl, 1, + DMA_FROM_DEVICE); + + spin_unlock_irqrestore(&sport->port.lock, flags); + + tty_flip_buffer_push(port); + mod_timer(&sport->lpuart_timer, jiffies + sport->dma_rx_timeout); +} + +static void lpuart_dma_rx_complete(void *arg) +{ + struct lpuart_port *sport = arg; + + lpuart_copy_rx_to_tty(sport); +} + +static void lpuart_timer_func(unsigned long data) +{ + struct lpuart_port *sport = (struct lpuart_port *)data; + + lpuart_copy_rx_to_tty(sport); +} + +static inline int lpuart_start_rx_dma(struct lpuart_port *sport) +{ + struct dma_slave_config dma_rx_sconfig = {}; + struct circ_buf *ring = &sport->rx_ring; + int ret, nent; + int bits, baud; + struct tty_struct *tty = tty_port_tty_get(&sport->port.state->port); + struct ktermios *termios = &tty->termios; + + baud = tty_get_baud_rate(tty); + + bits = (termios->c_cflag & CSIZE) == CS7 ? 9 : 10; + if (termios->c_cflag & PARENB) + bits++; + + /* + * Calculate length of one DMA buffer size to keep latency below + * 10ms at any baud rate. + */ + 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) - 1)); + if (sport->rx_dma_rng_buf_len < 16) + sport->rx_dma_rng_buf_len = 16; + + ring->buf = kmalloc(sport->rx_dma_rng_buf_len, GFP_KERNEL); + if (!ring->buf) { + dev_err(sport->port.dev, "Ring buf alloc failed\n"); + return -ENOMEM; + } + + sg_init_one(&sport->rx_sgl, ring->buf, sport->rx_dma_rng_buf_len); + sg_set_buf(&sport->rx_sgl, ring->buf, sport->rx_dma_rng_buf_len); + nent = dma_map_sg(sport->port.dev, &sport->rx_sgl, 1, DMA_FROM_DEVICE); + + if (!nent) { + dev_err(sport->port.dev, "DMA Rx mapping error\n"); + return -EINVAL; + } + + dma_rx_sconfig.src_addr = sport->port.mapbase + UARTDR; + dma_rx_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + dma_rx_sconfig.src_maxburst = 1; + dma_rx_sconfig.direction = DMA_DEV_TO_MEM; + ret = dmaengine_slave_config(sport->dma_rx_chan, &dma_rx_sconfig); + + if (ret < 0) { + dev_err(sport->port.dev, + "DMA Rx slave config failed, err = %d\n", ret); + return ret; + } + + sport->dma_rx_desc = dmaengine_prep_dma_cyclic(sport->dma_rx_chan, + sg_dma_address(&sport->rx_sgl), + sport->rx_sgl.length, + sport->rx_sgl.length / 2, + DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT); + if (!sport->dma_rx_desc) { + dev_err(sport->port.dev, "Cannot prepare cyclic DMA\n"); + return -EFAULT; + } + + sport->dma_rx_desc->callback = lpuart_dma_rx_complete; + sport->dma_rx_desc->callback_param = sport; + sport->dma_rx_cookie = dmaengine_submit(sport->dma_rx_desc); + dma_async_issue_pending(sport->dma_rx_chan); + + writeb(readb(sport->port.membase + UARTCR5) | UARTCR5_RDMAS, + sport->port.membase + UARTCR5); + + return 0; +} + +static void lpuart_dma_tx_free(struct uart_port *port) +{ + struct lpuart_port *sport = container_of(port, + struct lpuart_port, port); + + dma_unmap_single(sport->port.dev, sport->dma_tx_buf_bus, + UART_XMIT_SIZE, DMA_TO_DEVICE); + + sport->dma_tx_buf_bus = 0; + sport->dma_tx_buf_virt = NULL; +} + +static void lpuart_dma_rx_free(struct uart_port *port) +{ + struct lpuart_port *sport = container_of(port, + struct lpuart_port, port); + + if (sport->dma_rx_chan) + dmaengine_terminate_all(sport->dma_rx_chan); + + dma_unmap_sg(sport->port.dev, &sport->rx_sgl, 1, DMA_FROM_DEVICE); + kfree(sport->rx_ring.buf); + sport->rx_ring.tail = 0; + sport->rx_ring.head = 0; + sport->dma_rx_desc = NULL; + sport->dma_rx_cookie = -EINVAL; +} + static unsigned int lpuart_get_mctrl(struct uart_port *port) { unsigned int temp = 0; @@ -1015,72 +1099,12 @@ static int lpuart_dma_tx_request(struct uart_port *port) return 0; } -static int lpuart_dma_rx_request(struct uart_port *port) +static void rx_dma_timer_init(struct lpuart_port *sport) { - struct lpuart_port *sport = container_of(port, - struct lpuart_port, port); - struct dma_slave_config dma_rx_sconfig; - dma_addr_t dma_bus; - unsigned char *dma_buf; - int ret; - - dma_buf = devm_kzalloc(sport->port.dev, - FSL_UART_RX_DMA_BUFFER_SIZE, GFP_KERNEL); - - if (!dma_buf) { - dev_err(sport->port.dev, "Dma rx alloc failed\n"); - return -ENOMEM; - } - - dma_bus = dma_map_single(sport->dma_rx_chan->device->dev, dma_buf, - FSL_UART_RX_DMA_BUFFER_SIZE, DMA_FROM_DEVICE); - - if (dma_mapping_error(sport->dma_rx_chan->device->dev, dma_bus)) { - dev_err(sport->port.dev, "dma_map_single rx failed\n"); - return -ENOMEM; - } - - dma_rx_sconfig.src_addr = sport->port.mapbase + UARTDR; - dma_rx_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; - dma_rx_sconfig.src_maxburst = 1; - dma_rx_sconfig.direction = DMA_DEV_TO_MEM; - ret = dmaengine_slave_config(sport->dma_rx_chan, &dma_rx_sconfig); - - if (ret < 0) { - dev_err(sport->port.dev, - "Dma slave config failed, err = %d\n", ret); - return ret; - } - - sport->dma_rx_buf_virt = dma_buf; - sport->dma_rx_buf_bus = dma_bus; - sport->dma_rx_in_progress = 0; - - return 0; -} - -static void lpuart_dma_tx_free(struct uart_port *port) -{ - struct lpuart_port *sport = container_of(port, - struct lpuart_port, port); - - dma_unmap_single(sport->port.dev, sport->dma_tx_buf_bus, - UART_XMIT_SIZE, DMA_TO_DEVICE); - - sport->dma_tx_buf_bus = 0; - sport->dma_tx_buf_virt = NULL; -} - -static void lpuart_dma_rx_free(struct uart_port *port) -{ - struct lpuart_port *sport = container_of(port, - struct lpuart_port, port); - - dma_unmap_single(sport->port.dev, sport->dma_rx_buf_bus, - FSL_UART_RX_DMA_BUFFER_SIZE, DMA_FROM_DEVICE); - - sport->dma_rx_buf_bus = 0; - sport->dma_rx_buf_virt = NULL; + setup_timer(&sport->lpuart_timer, lpuart_timer_func, + (unsigned long)sport); + sport->lpuart_timer.expires = jiffies + sport->dma_rx_timeout; + add_timer(&sport->lpuart_timer); } static int lpuart_startup(struct uart_port *port) @@ -1101,22 +1125,6 @@ static int lpuart_startup(struct uart_port *port) sport->rxfifo_size = 0x1 << (((temp >> UARTPFIFO_RXSIZE_OFF) & UARTPFIFO_FIFOSIZE_MASK) + 1); - if (sport->dma_rx_chan && !lpuart_dma_rx_request(port)) { - sport->lpuart_dma_rx_use = true; - setup_timer(&sport->lpuart_timer, lpuart_timer_func, - (unsigned long)sport); - } else - sport->lpuart_dma_rx_use = false; - - - if (sport->dma_tx_chan && !lpuart_dma_tx_request(port)) { - sport->lpuart_dma_tx_use = true; - temp = readb(port->membase + UARTCR5); - temp &= ~UARTCR5_RDMAS; - writeb(temp | UARTCR5_TDMAS, port->membase + UARTCR5); - } else - sport->lpuart_dma_tx_use = false; - ret = devm_request_irq(port->dev, port->irq, lpuart_int, 0, DRIVER_NAME, sport); if (ret) @@ -1130,7 +1138,28 @@ static int lpuart_startup(struct uart_port *port) temp |= (UARTCR2_RIE | UARTCR2_TIE | UARTCR2_RE | UARTCR2_TE); writeb(temp, sport->port.membase + UARTCR2); + if (sport->dma_rx_chan && !lpuart_start_rx_dma(sport)) { + /* set Rx DMA timeout */ + sport->dma_rx_timeout = msecs_to_jiffies(DMA_RX_TIMEOUT); + if (!sport->dma_rx_timeout) + sport->dma_rx_timeout = 1; + + sport->lpuart_dma_rx_use = true; + rx_dma_timer_init(sport); + } else { + sport->lpuart_dma_rx_use = false; + } + + if (sport->dma_tx_chan && !lpuart_dma_tx_request(port)) { + sport->lpuart_dma_tx_use = true; + temp = readb(port->membase + UARTCR5); + writeb(temp | UARTCR5_TDMAS, port->membase + UARTCR5); + } else { + sport->lpuart_dma_tx_use = false; + } + spin_unlock_irqrestore(&sport->port.lock, flags); + return 0; } @@ -1187,8 +1216,8 @@ static void lpuart_shutdown(struct uart_port *port) devm_free_irq(port->dev, port->irq, sport); if (sport->lpuart_dma_rx_use) { - lpuart_dma_rx_free(&sport->port); del_timer_sync(&sport->lpuart_timer); + lpuart_dma_rx_free(&sport->port); } if (sport->lpuart_dma_tx_use) @@ -1318,17 +1347,6 @@ lpuart_set_termios(struct uart_port *port, struct ktermios *termios, /* update the per-port timeout */ uart_update_timeout(port, termios->c_cflag, baud); - if (sport->lpuart_dma_rx_use) { - /* Calculate delay for 1.5 DMA buffers */ - sport->dma_rx_timeout = (sport->port.timeout - HZ / 50) * - FSL_UART_RX_DMA_BUFFER_SIZE * 3 / - sport->rxfifo_size / 2; - dev_dbg(port->dev, "DMA Rx t-out %ums, tty t-out %u jiffies\n", - sport->dma_rx_timeout * 1000 / HZ, sport->port.timeout); - if (sport->dma_rx_timeout < msecs_to_jiffies(20)) - sport->dma_rx_timeout = msecs_to_jiffies(20); - } - /* wait transmit engin complete */ while (!(readb(sport->port.membase + UARTSR1) & UARTSR1_TC)) barrier(); @@ -1353,6 +1371,24 @@ lpuart_set_termios(struct uart_port *port, struct ktermios *termios, /* restore control register */ writeb(old_cr2, sport->port.membase + UARTCR2); + /* + * If new baud rate is set, we will also need to update the Ring buffer + * length according to the selected baud rate and restart Rx DMA path. + */ + if (old) { + if (sport->lpuart_dma_rx_use) { + del_timer_sync(&sport->lpuart_timer); + lpuart_dma_rx_free(&sport->port); + } + + if (sport->dma_rx_chan && !lpuart_start_rx_dma(sport)) { + sport->lpuart_dma_rx_use = true; + rx_dma_timer_init(sport); + } else { + sport->lpuart_dma_rx_use = false; + } + } + spin_unlock_irqrestore(&sport->port.lock, flags); } @@ -1943,9 +1979,6 @@ static int lpuart_suspend(struct device *dev) writeb(temp, sport->port.membase + UARTCR2); } - if (sport->dma_rx_in_progress) - lpuart_dma_rx_terminate(sport); - uart_suspend_port(&lpuart_reg, &sport->port); if (sport->port.suspended && !sport->port.irq_wake) clk_disable_unprepare(sport->clk); -- cgit v1.2.3 From 6250cc30c4c4e25393ba247f71bdc04b6af3191b Mon Sep 17 00:00:00 2001 From: Bhuvanchandra DV Date: Tue, 19 Jul 2016 13:13:08 +0530 Subject: tty: serial: fsl_lpuart: Use scatter/gather DMA for Tx Drop PIO to DMA switching and use scatter/gather DMA for Tx path to improve performance. Some part of the code is borrowed from imx serial driver. Signed-off-by: Bhuvanchandra DV Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/fsl_lpuart.c | 257 ++++++++++++++++++---------------------- 1 file changed, 113 insertions(+), 144 deletions(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 79965706d97f..93e7589893d2 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -244,18 +244,18 @@ struct lpuart_port { struct dma_chan *dma_rx_chan; struct dma_async_tx_descriptor *dma_tx_desc; struct dma_async_tx_descriptor *dma_rx_desc; - dma_addr_t dma_tx_buf_bus; dma_cookie_t dma_tx_cookie; dma_cookie_t dma_rx_cookie; - unsigned char *dma_tx_buf_virt; unsigned int dma_tx_bytes; unsigned int dma_rx_bytes; - int dma_tx_in_progress; + bool dma_tx_in_progress; unsigned int dma_rx_timeout; struct timer_list lpuart_timer; - struct scatterlist rx_sgl; + struct scatterlist rx_sgl, tx_sgl[2]; struct circ_buf rx_ring; int rx_dma_rng_buf_len; + unsigned int dma_tx_nents; + wait_queue_head_t dma_wait; }; static const struct of_device_id lpuart_dt_ids[] = { @@ -316,103 +316,118 @@ static void lpuart32_stop_rx(struct uart_port *port) lpuart32_write(temp & ~UARTCTRL_RE, port->membase + UARTCTRL); } -static void lpuart_pio_tx(struct lpuart_port *sport) +static void lpuart_dma_tx(struct lpuart_port *sport) { struct circ_buf *xmit = &sport->port.state->xmit; - unsigned long flags; - - spin_lock_irqsave(&sport->port.lock, flags); + struct scatterlist *sgl = sport->tx_sgl; + struct device *dev = sport->port.dev; + int ret; - while (!uart_circ_empty(xmit) && - readb(sport->port.membase + UARTTCFIFO) < sport->txfifo_size) { - writeb(xmit->buf[xmit->tail], sport->port.membase + UARTDR); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - sport->port.icount.tx++; - } + if (sport->dma_tx_in_progress) + return; - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(&sport->port); + sport->dma_tx_bytes = uart_circ_chars_pending(xmit); - if (uart_circ_empty(xmit)) - writeb(readb(sport->port.membase + UARTCR5) | UARTCR5_TDMAS, - sport->port.membase + UARTCR5); + if (xmit->tail < xmit->head) { + sport->dma_tx_nents = 1; + sg_init_one(sgl, xmit->buf + xmit->tail, sport->dma_tx_bytes); + } else { + sport->dma_tx_nents = 2; + sg_init_table(sgl, 2); + sg_set_buf(sgl, xmit->buf + xmit->tail, + UART_XMIT_SIZE - xmit->tail); + sg_set_buf(sgl + 1, xmit->buf, xmit->head); + } - spin_unlock_irqrestore(&sport->port.lock, flags); -} + ret = dma_map_sg(dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE); + if (!ret) { + dev_err(dev, "DMA mapping error for TX.\n"); + return; + } -static int lpuart_dma_tx(struct lpuart_port *sport, unsigned long count) -{ - struct circ_buf *xmit = &sport->port.state->xmit; - dma_addr_t tx_bus_addr; - - dma_sync_single_for_device(sport->port.dev, sport->dma_tx_buf_bus, - UART_XMIT_SIZE, DMA_TO_DEVICE); - sport->dma_tx_bytes = count & ~(sport->txfifo_size - 1); - tx_bus_addr = sport->dma_tx_buf_bus + xmit->tail; - sport->dma_tx_desc = dmaengine_prep_slave_single(sport->dma_tx_chan, - tx_bus_addr, sport->dma_tx_bytes, + sport->dma_tx_desc = dmaengine_prep_slave_sg(sport->dma_tx_chan, sgl, + sport->dma_tx_nents, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT); - if (!sport->dma_tx_desc) { - dev_err(sport->port.dev, "Not able to get desc for tx\n"); - return -EIO; + dma_unmap_sg(dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE); + dev_err(dev, "Cannot prepare TX slave DMA!\n"); + return; } sport->dma_tx_desc->callback = lpuart_dma_tx_complete; sport->dma_tx_desc->callback_param = sport; - sport->dma_tx_in_progress = 1; + sport->dma_tx_in_progress = true; sport->dma_tx_cookie = dmaengine_submit(sport->dma_tx_desc); dma_async_issue_pending(sport->dma_tx_chan); - return 0; -} - -static void lpuart_prepare_tx(struct lpuart_port *sport) -{ - struct circ_buf *xmit = &sport->port.state->xmit; - unsigned long count = CIRC_CNT_TO_END(xmit->head, - xmit->tail, UART_XMIT_SIZE); - - if (!count) - return; - - if (count < sport->txfifo_size) - writeb(readb(sport->port.membase + UARTCR5) & ~UARTCR5_TDMAS, - sport->port.membase + UARTCR5); - else { - writeb(readb(sport->port.membase + UARTCR5) | UARTCR5_TDMAS, - sport->port.membase + UARTCR5); - lpuart_dma_tx(sport, count); - } } static void lpuart_dma_tx_complete(void *arg) { struct lpuart_port *sport = arg; + struct scatterlist *sgl = &sport->tx_sgl[0]; struct circ_buf *xmit = &sport->port.state->xmit; unsigned long flags; - async_tx_ack(sport->dma_tx_desc); - spin_lock_irqsave(&sport->port.lock, flags); + dma_unmap_sg(sport->port.dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE); + xmit->tail = (xmit->tail + sport->dma_tx_bytes) & (UART_XMIT_SIZE - 1); - sport->dma_tx_in_progress = 0; + + sport->port.icount.tx += sport->dma_tx_bytes; + sport->dma_tx_in_progress = false; + spin_unlock_irqrestore(&sport->port.lock, flags); if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(&sport->port); - lpuart_prepare_tx(sport); + if (waitqueue_active(&sport->dma_wait)) { + wake_up(&sport->dma_wait); + return; + } + + spin_lock_irqsave(&sport->port.lock, flags); + + if (!uart_circ_empty(xmit) && !uart_tx_stopped(&sport->port)) + lpuart_dma_tx(sport); spin_unlock_irqrestore(&sport->port.lock, flags); } +static int lpuart_dma_tx_request(struct uart_port *port) +{ + struct lpuart_port *sport = container_of(port, + struct lpuart_port, port); + struct dma_slave_config dma_tx_sconfig = {}; + int ret; + + dma_tx_sconfig.dst_addr = sport->port.mapbase + UARTDR; + dma_tx_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + dma_tx_sconfig.dst_maxburst = 1; + dma_tx_sconfig.direction = DMA_MEM_TO_DEV; + ret = dmaengine_slave_config(sport->dma_tx_chan, &dma_tx_sconfig); + + if (ret) { + dev_err(sport->port.dev, + "DMA slave config failed, err = %d\n", ret); + return ret; + } + + return 0; +} + static void lpuart_flush_buffer(struct uart_port *port) { struct lpuart_port *sport = container_of(port, struct lpuart_port, port); + if (sport->lpuart_dma_tx_use) { + if (sport->dma_tx_in_progress) { + dma_unmap_sg(sport->port.dev, &sport->tx_sgl[0], + sport->dma_tx_nents, DMA_TO_DEVICE); + sport->dma_tx_in_progress = false; + } dmaengine_terminate_all(sport->dma_tx_chan); - sport->dma_tx_in_progress = 0; } } @@ -469,8 +484,8 @@ static void lpuart_start_tx(struct uart_port *port) writeb(temp | UARTCR2_TIE, port->membase + UARTCR2); if (sport->lpuart_dma_tx_use) { - if (!uart_circ_empty(xmit) && !sport->dma_tx_in_progress) - lpuart_prepare_tx(sport); + if (!uart_circ_empty(xmit) && !uart_tx_stopped(port)) + lpuart_dma_tx(sport); } else { if (readb(port->membase + UARTSR1) & UARTSR1_TDRE) lpuart_transmit_buffer(sport); @@ -489,6 +504,29 @@ static void lpuart32_start_tx(struct uart_port *port) lpuart32_transmit_buffer(sport); } +/* return TIOCSER_TEMT when transmitter is not busy */ +static unsigned int lpuart_tx_empty(struct uart_port *port) +{ + struct lpuart_port *sport = container_of(port, + struct lpuart_port, port); + unsigned char sr1 = readb(port->membase + UARTSR1); + unsigned char sfifo = readb(port->membase + UARTSFIFO); + + if (sport->dma_tx_in_progress) + return 0; + + if (sr1 & UARTSR1_TC && sfifo & UARTSFIFO_TXEMPT) + return TIOCSER_TEMT; + + return 0; +} + +static unsigned int lpuart32_tx_empty(struct uart_port *port) +{ + return (lpuart32_read(port->membase + UARTSTAT) & UARTSTAT_TC) ? + TIOCSER_TEMT : 0; +} + static irqreturn_t lpuart_txint(int irq, void *dev_id) { struct lpuart_port *sport = dev_id; @@ -662,12 +700,8 @@ static irqreturn_t lpuart_int(int irq, void *dev_id) if (sts & UARTSR1_RDRF) lpuart_rxint(irq, dev_id); - if (sts & UARTSR1_TDRE) { - if (sport->lpuart_dma_tx_use) - lpuart_pio_tx(sport); - else - lpuart_txint(irq, dev_id); - } + if (sts & UARTSR1_TDRE) + lpuart_txint(irq, dev_id); return IRQ_HANDLED; } @@ -692,29 +726,6 @@ static irqreturn_t lpuart32_int(int irq, void *dev_id) return IRQ_HANDLED; } -/* return TIOCSER_TEMT when transmitter is not busy */ -static unsigned int lpuart_tx_empty(struct uart_port *port) -{ - struct lpuart_port *sport = container_of(port, - struct lpuart_port, port); - unsigned char sr1 = readb(port->membase + UARTSR1); - unsigned char sfifo = readb(port->membase + UARTSFIFO); - - if (sport->dma_tx_in_progress) - return 0; - - if (sr1 & UARTSR1_TC && sfifo & UARTSFIFO_TXEMPT) - return TIOCSER_TEMT; - - return 0; -} - -static unsigned int lpuart32_tx_empty(struct uart_port *port) -{ - return (lpuart32_read(port->membase + UARTSTAT) & UARTSTAT_TC) ? - TIOCSER_TEMT : 0; -} - static void lpuart_copy_rx_to_tty(struct lpuart_port *sport) { struct tty_port *port = &sport->port.state->port; @@ -890,18 +901,6 @@ static inline int lpuart_start_rx_dma(struct lpuart_port *sport) return 0; } -static void lpuart_dma_tx_free(struct uart_port *port) -{ - struct lpuart_port *sport = container_of(port, - struct lpuart_port, port); - - dma_unmap_single(sport->port.dev, sport->dma_tx_buf_bus, - UART_XMIT_SIZE, DMA_TO_DEVICE); - - sport->dma_tx_buf_bus = 0; - sport->dma_tx_buf_virt = NULL; -} - static void lpuart_dma_rx_free(struct uart_port *port) { struct lpuart_port *sport = container_of(port, @@ -1061,44 +1060,6 @@ static void lpuart32_setup_watermark(struct lpuart_port *sport) lpuart32_write(ctrl_saved, sport->port.membase + UARTCTRL); } -static int lpuart_dma_tx_request(struct uart_port *port) -{ - struct lpuart_port *sport = container_of(port, - struct lpuart_port, port); - struct dma_slave_config dma_tx_sconfig; - dma_addr_t dma_bus; - unsigned char *dma_buf; - int ret; - - dma_bus = dma_map_single(sport->dma_tx_chan->device->dev, - sport->port.state->xmit.buf, - UART_XMIT_SIZE, DMA_TO_DEVICE); - - if (dma_mapping_error(sport->dma_tx_chan->device->dev, dma_bus)) { - dev_err(sport->port.dev, "dma_map_single tx failed\n"); - return -ENOMEM; - } - - dma_buf = sport->port.state->xmit.buf; - dma_tx_sconfig.dst_addr = sport->port.mapbase + UARTDR; - dma_tx_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; - dma_tx_sconfig.dst_maxburst = sport->txfifo_size; - dma_tx_sconfig.direction = DMA_MEM_TO_DEV; - ret = dmaengine_slave_config(sport->dma_tx_chan, &dma_tx_sconfig); - - if (ret < 0) { - dev_err(sport->port.dev, - "Dma slave config failed, err = %d\n", ret); - return ret; - } - - sport->dma_tx_buf_virt = dma_buf; - sport->dma_tx_buf_bus = dma_bus; - sport->dma_tx_in_progress = 0; - - return 0; -} - static void rx_dma_timer_init(struct lpuart_port *sport) { setup_timer(&sport->lpuart_timer, lpuart_timer_func, @@ -1151,6 +1112,7 @@ static int lpuart_startup(struct uart_port *port) } if (sport->dma_tx_chan && !lpuart_dma_tx_request(port)) { + init_waitqueue_head(&sport->dma_wait); sport->lpuart_dma_tx_use = true; temp = readb(port->membase + UARTCR5); writeb(temp | UARTCR5_TDMAS, port->membase + UARTCR5); @@ -1220,8 +1182,15 @@ static void lpuart_shutdown(struct uart_port *port) lpuart_dma_rx_free(&sport->port); } - if (sport->lpuart_dma_tx_use) - lpuart_dma_tx_free(&sport->port); + if (sport->lpuart_dma_tx_use) { + if (wait_event_interruptible(sport->dma_wait, + !sport->dma_tx_in_progress) != false) { + sport->dma_tx_in_progress = false; + dmaengine_terminate_all(sport->dma_tx_chan); + } + + lpuart_stop_tx(port); + } } static void lpuart32_shutdown(struct uart_port *port) -- cgit v1.2.3 From c05efd692f1f2c2ba637356613bab6e2dfba2507 Mon Sep 17 00:00:00 2001 From: Bhuvanchandra DV Date: Tue, 19 Jul 2016 13:13:09 +0530 Subject: tty: serial: fsl_lpuart: Update suspend/resume for DMA mode When DMA mode is enabled one need to make sure the DMA channels are idle before entering suspend mode especially when UART ports which are set as wakeup source and console port with no_console_suspend is set. This patch takes care of gracefully releasing DMA channels for the above two cases and start the DMA at resume. Signed-off-by: Bhuvanchandra DV Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/fsl_lpuart.c | 44 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 93e7589893d2..6ab4b25583d7 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -1949,6 +1949,30 @@ static int lpuart_suspend(struct device *dev) } uart_suspend_port(&lpuart_reg, &sport->port); + + if (sport->lpuart_dma_rx_use) { + /* + * EDMA driver during suspend will forcefully release any + * non-idle DMA channels. If port wakeup is enabled or if port + * is console port or 'no_console_suspend' is set the Rx DMA + * cannot resume as as expected, hence gracefully release the + * Rx DMA path before suspend and start Rx DMA path on resume. + */ + if (sport->port.irq_wake) { + del_timer_sync(&sport->lpuart_timer); + lpuart_dma_rx_free(&sport->port); + } + + /* Disable Rx DMA to use UART port as wakeup source */ + writeb(readb(sport->port.membase + UARTCR5) & ~UARTCR5_RDMAS, + sport->port.membase + UARTCR5); + } + + if (sport->lpuart_dma_tx_use) { + sport->dma_tx_in_progress = false; + dmaengine_terminate_all(sport->dma_tx_chan); + } + if (sport->port.suspended && !sport->port.irq_wake) clk_disable_unprepare(sport->clk); @@ -1976,6 +2000,26 @@ static int lpuart_resume(struct device *dev) writeb(temp, sport->port.membase + UARTCR2); } + if (sport->lpuart_dma_rx_use) { + if (sport->port.irq_wake) { + if (!lpuart_start_rx_dma(sport)) { + sport->lpuart_dma_rx_use = true; + rx_dma_timer_init(sport); + } else { + sport->lpuart_dma_rx_use = false; + } + } + } + + if (sport->dma_tx_chan && !lpuart_dma_tx_request(&sport->port)) { + init_waitqueue_head(&sport->dma_wait); + sport->lpuart_dma_tx_use = true; + writeb(readb(sport->port.membase + UARTCR5) | + UARTCR5_TDMAS, sport->port.membase + UARTCR5); + } else { + sport->lpuart_dma_tx_use = false; + } + uart_resume_port(&lpuart_reg, &sport->port); return 0; -- cgit v1.2.3 From 03895cf41d1890a219679490428c8bf10b17e2b9 Mon Sep 17 00:00:00 2001 From: Bhuvanchandra DV Date: Tue, 19 Jul 2016 13:13:10 +0530 Subject: tty: serial: fsl_lpuart: Add support for RS-485 Enable Vybrid's build-in support for RS-485 auto RTS for controlling line direction of RS-485 transceiver driver. Enable RS485 feature by either using ioctrl 'TIOCSRS485' or enable it in the device tree by setting 'linux,rs485-enabled-at-boot-time' property. Signed-off-by: Bhuvanchandra DV Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/fsl_lpuart.c | 78 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 72 insertions(+), 6 deletions(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 6ab4b25583d7..93f172eaf026 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -917,6 +917,52 @@ static void lpuart_dma_rx_free(struct uart_port *port) sport->dma_rx_cookie = -EINVAL; } +static int lpuart_config_rs485(struct uart_port *port, + struct serial_rs485 *rs485) +{ + struct lpuart_port *sport = container_of(port, + struct lpuart_port, port); + + u8 modem = readb(sport->port.membase + UARTMODEM) & + ~(UARTMODEM_TXRTSPOL | UARTMODEM_TXRTSE); + writeb(modem, sport->port.membase + UARTMODEM); + + if (rs485->flags & SER_RS485_ENABLED) { + /* Enable auto RS-485 RTS mode */ + modem |= UARTMODEM_TXRTSE; + + /* + * RTS needs to be logic HIGH either during transer _or_ after + * transfer, other variants are not supported by the hardware. + */ + + if (!(rs485->flags & (SER_RS485_RTS_ON_SEND | + SER_RS485_RTS_AFTER_SEND))) + rs485->flags |= SER_RS485_RTS_ON_SEND; + + if (rs485->flags & SER_RS485_RTS_ON_SEND && + rs485->flags & SER_RS485_RTS_AFTER_SEND) + rs485->flags &= ~SER_RS485_RTS_AFTER_SEND; + + /* + * The hardware defaults to RTS logic HIGH while transfer. + * Switch polarity in case RTS shall be logic HIGH + * after transfer. + * Note: UART is assumed to be active high. + */ + if (rs485->flags & SER_RS485_RTS_ON_SEND) + modem &= ~UARTMODEM_TXRTSPOL; + else if (rs485->flags & SER_RS485_RTS_AFTER_SEND) + modem |= UARTMODEM_TXRTSPOL; + } + + /* Store the new configuration */ + sport->port.rs485 = *rs485; + + writeb(modem, sport->port.membase + UARTMODEM); + return 0; +} + static unsigned int lpuart_get_mctrl(struct uart_port *port) { unsigned int temp = 0; @@ -950,17 +996,22 @@ static unsigned int lpuart32_get_mctrl(struct uart_port *port) static void lpuart_set_mctrl(struct uart_port *port, unsigned int mctrl) { unsigned char temp; + struct lpuart_port *sport = container_of(port, + struct lpuart_port, port); - temp = readb(port->membase + UARTMODEM) & + /* Make sure RXRTSE bit is not set when RS485 is enabled */ + if (!(sport->port.rs485.flags & SER_RS485_ENABLED)) { + temp = readb(sport->port.membase + UARTMODEM) & ~(UARTMODEM_RXRTSE | UARTMODEM_TXCTSE); - if (mctrl & TIOCM_RTS) - temp |= UARTMODEM_RXRTSE; + if (mctrl & TIOCM_RTS) + temp |= UARTMODEM_RXRTSE; - if (mctrl & TIOCM_CTS) - temp |= UARTMODEM_TXCTSE; + if (mctrl & TIOCM_CTS) + temp |= UARTMODEM_TXCTSE; - writeb(temp, port->membase + UARTMODEM); + writeb(temp, port->membase + UARTMODEM); + } } static void lpuart32_set_mctrl(struct uart_port *port, unsigned int mctrl) @@ -1256,6 +1307,13 @@ lpuart_set_termios(struct uart_port *port, struct ktermios *termios, cr1 |= UARTCR1_M; } + /* + * When auto RS-485 RTS mode is enabled, + * hardware flow control need to be disabled. + */ + if (sport->port.rs485.flags & SER_RS485_ENABLED) + termios->c_cflag &= ~CRTSCTS; + if (termios->c_cflag & CRTSCTS) { modem |= (UARTMODEM_RXRTSE | UARTMODEM_TXCTSE); } else { @@ -1870,6 +1928,8 @@ static int lpuart_probe(struct platform_device *pdev) sport->port.ops = &lpuart_pops; sport->port.flags = UPF_BOOT_AUTOCONF; + sport->port.rs485_config = lpuart_config_rs485; + sport->clk = devm_clk_get(&pdev->dev, "ipg"); if (IS_ERR(sport->clk)) { ret = PTR_ERR(sport->clk); @@ -1910,6 +1970,12 @@ static int lpuart_probe(struct platform_device *pdev) dev_info(sport->port.dev, "DMA rx channel request failed, " "operating without rx DMA\n"); + if (of_property_read_bool(np, "linux,rs485-enabled-at-boot-time")) { + sport->port.rs485.flags |= SER_RS485_ENABLED; + sport->port.rs485.flags |= SER_RS485_RTS_ON_SEND; + writeb(UARTMODEM_TXRTSE, sport->port.membase + UARTMODEM); + } + return 0; } -- cgit v1.2.3 From 490d5ce2d0130540f2003c2d01c921fe0917607d Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Fri, 5 Aug 2016 10:56:45 +0200 Subject: tty/serial: at91: use of_property_read_bool Use of_property_read_bool to check for the existence of a property. The semantic patch that makes this change is as follows: (http://coccinelle.lip6.fr/) // @@ expression e1,e2; statement S2,S1; @@ - if (of_get_property(e1,e2,NULL)) + if (of_property_read_bool(e1,e2)) S1 else S2 // Signed-off-by: Julia Lawall Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/atmel_serial.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index 17592390e85c..b94683f7de9b 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -1635,8 +1635,8 @@ static void atmel_init_property(struct atmel_uart_port *atmel_port, if (np) { /* DMA/PDC usage specification */ - if (of_get_property(np, "atmel,use-dma-rx", NULL)) { - if (of_get_property(np, "dmas", NULL)) { + if (of_property_read_bool(np, "atmel,use-dma-rx")) { + if (of_property_read_bool(np, "dmas")) { atmel_port->use_dma_rx = true; atmel_port->use_pdc_rx = false; } else { @@ -1648,8 +1648,8 @@ static void atmel_init_property(struct atmel_uart_port *atmel_port, atmel_port->use_pdc_rx = false; } - if (of_get_property(np, "atmel,use-dma-tx", NULL)) { - if (of_get_property(np, "dmas", NULL)) { + if (of_property_read_bool(np, "atmel,use-dma-tx")) { + if (of_property_read_bool(np, "dmas")) { atmel_port->use_dma_tx = true; atmel_port->use_pdc_tx = false; } else { -- cgit v1.2.3 From 81bb549fdf144582f57d1df65a2517beb418dc8b Mon Sep 17 00:00:00 2001 From: Eddie Huang Date: Fri, 12 Aug 2016 10:41:11 +0800 Subject: serial: 8250_mtk: support big baud rate. mediatek can support baud rate up to 4M. the 'uart_get_baud_rate' function will limit the max baud rate. Modify max baud to remove the limit. Signed-off-by: Long Cheng Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_mtk.c | 6 +----- drivers/tty/serial/8250/8250_port.c | 4 +--- 2 files changed, 2 insertions(+), 8 deletions(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/8250/8250_mtk.c b/drivers/tty/serial/8250/8250_mtk.c index 3611ec9bb4fa..ce0cc471bfc3 100644 --- a/drivers/tty/serial/8250/8250_mtk.c +++ b/drivers/tty/serial/8250/8250_mtk.c @@ -62,7 +62,7 @@ mtk8250_set_termios(struct uart_port *port, struct ktermios *termios, */ baud = uart_get_baud_rate(port, termios, old, port->uartclk / 16 / 0xffff, - port->uartclk / 16); + port->uartclk); if (baud <= 115200) { serial_port_out(port, UART_MTK_HIGHS, 0x0); @@ -76,10 +76,6 @@ mtk8250_set_termios(struct uart_port *port, struct ktermios *termios, quot = DIV_ROUND_UP(port->uartclk, 4 * baud); } else { serial_port_out(port, UART_MTK_HIGHS, 0x3); - - /* Set to highest baudrate supported */ - if (baud >= 1152000) - baud = 921600; quot = DIV_ROUND_UP(port->uartclk, 256 * baud); } diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index 7481b95c6d84..c45c1e3f3631 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -2504,8 +2504,6 @@ static unsigned int serial8250_get_baud_rate(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { - unsigned int tolerance = port->uartclk / 100; - /* * Ask the core to calculate the divisor for us. * Allow 1% tolerance at the upper limit so uart clks marginally @@ -2514,7 +2512,7 @@ static unsigned int serial8250_get_baud_rate(struct uart_port *port, */ return uart_get_baud_rate(port, termios, old, port->uartclk / 16 / 0xffff, - (port->uartclk + tolerance) / 16); + port->uartclk); } void -- cgit v1.2.3 From 43c51bb573aab63cd67ad7216dc0b849036bc47a Mon Sep 17 00:00:00 2001 From: Florian Vallee Date: Tue, 19 Jul 2016 16:29:36 +0200 Subject: sc16is7xx: make sure device is in suspend once probed Previously sc16is7xx_power was called in order to set the device to a low power mode. However since SC16IS7XX_EFR_ENABLE_BIT was not set beforehand this suspend request had not effect. Also, soft-reset the device prior to port initialization. It may otherwise be in a state (interrupt pending, fifo not empty) which prevents it from sleeping. Signed-off-by: Florian Vallee Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sc16is7xx.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index f36e6df2fa90..a9d94f7cf683 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -1205,6 +1205,10 @@ static int sc16is7xx_probe(struct device *dev, } #endif + /* reset device, purging any pending irq / data */ + regmap_write(s->regmap, SC16IS7XX_IOCONTROL_REG << SC16IS7XX_REG_SHIFT, + SC16IS7XX_IOCONTROL_SRESET_BIT); + for (i = 0; i < devtype->nr_uart; ++i) { s->p[i].line = i; /* Initialize port data */ @@ -1234,6 +1238,22 @@ static int sc16is7xx_probe(struct device *dev, init_kthread_work(&s->p[i].reg_work, sc16is7xx_reg_proc); /* Register port */ uart_add_one_port(&sc16is7xx_uart, &s->p[i].port); + + /* Enable EFR */ + sc16is7xx_port_write(&s->p[i].port, SC16IS7XX_LCR_REG, + SC16IS7XX_LCR_CONF_MODE_B); + + regcache_cache_bypass(s->regmap, true); + + /* Enable write access to enhanced features */ + sc16is7xx_port_write(&s->p[i].port, SC16IS7XX_EFR_REG, + SC16IS7XX_EFR_ENABLE_BIT); + + regcache_cache_bypass(s->regmap, false); + + /* Restore access to general registers */ + sc16is7xx_port_write(&s->p[i].port, SC16IS7XX_LCR_REG, 0x00); + /* Go to suspend mode */ sc16is7xx_power(&s->p[i].port, 0); } -- cgit v1.2.3 From 8d17047207d52182ccd55b9529e1c8cc062c07c2 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 17 Aug 2016 19:20:24 +0300 Subject: serial: 8250_dma: switch to new dmaengine_terminate_* API Convert dmaengine_terminate_all() calls to synchronous and asynchronous versions where appropriate. Reviewed-by: Peter Hurley Reviewed-by: Heikki Krogerus Signed-off-by: Andy Shevchenko Tested-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_dma.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c index 3590d012001f..896e8f49b6f7 100644 --- a/drivers/tty/serial/8250/8250_dma.c +++ b/drivers/tty/serial/8250/8250_dma.c @@ -142,7 +142,7 @@ void serial8250_rx_dma_flush(struct uart_8250_port *p) if (dma->rx_running) { dmaengine_pause(dma->rxchan); __dma_rx_complete(p); - dmaengine_terminate_all(dma->rxchan); + dmaengine_terminate_async(dma->rxchan); } } EXPORT_SYMBOL_GPL(serial8250_rx_dma_flush); @@ -247,14 +247,14 @@ void serial8250_release_dma(struct uart_8250_port *p) return; /* Release RX resources */ - dmaengine_terminate_all(dma->rxchan); + dmaengine_terminate_sync(dma->rxchan); dma_free_coherent(dma->rxchan->device->dev, dma->rx_size, dma->rx_buf, dma->rx_addr); dma_release_channel(dma->rxchan); dma->rxchan = NULL; /* Release TX resources */ - dmaengine_terminate_all(dma->txchan); + dmaengine_terminate_sync(dma->txchan); dma_unmap_single(dma->txchan->device->dev, dma->tx_addr, UART_XMIT_SIZE, DMA_TO_DEVICE); dma_release_channel(dma->txchan); -- cgit v1.2.3 From d1834babe42246bf0bd0989b8bb19c9807851527 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 17 Aug 2016 19:20:25 +0300 Subject: serial: 8250_dma: adjust DMA address of the UART Some UARTs, e.g. one is used in Intel Quark, have a different address base for DMA operations. Introduce an additional field (per RX and TX DMA channels) in struct uart_8250_dma to cover those cases. Reviewed-by: Heikki Krogerus Signed-off-by: Andy Shevchenko Tested-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250.h | 5 +++++ drivers/tty/serial/8250/8250_dma.c | 8 ++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h index 122e0e4029fe..c807b7393d4a 100644 --- a/drivers/tty/serial/8250/8250.h +++ b/drivers/tty/serial/8250/8250.h @@ -33,6 +33,11 @@ struct uart_8250_dma { struct dma_chan *rxchan; struct dma_chan *txchan; + /* Device address base for DMA operations */ + phys_addr_t rx_dma_addr; + phys_addr_t tx_dma_addr; + + /* DMA address of the buffer in memory */ dma_addr_t rx_addr; dma_addr_t tx_addr; diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c index 896e8f49b6f7..fdbddbc6375d 100644 --- a/drivers/tty/serial/8250/8250_dma.c +++ b/drivers/tty/serial/8250/8250_dma.c @@ -150,6 +150,10 @@ EXPORT_SYMBOL_GPL(serial8250_rx_dma_flush); int serial8250_request_dma(struct uart_8250_port *p) { struct uart_8250_dma *dma = p->dma; + phys_addr_t rx_dma_addr = dma->rx_dma_addr ? + dma->rx_dma_addr : p->port.mapbase; + phys_addr_t tx_dma_addr = dma->tx_dma_addr ? + dma->tx_dma_addr : p->port.mapbase; dma_cap_mask_t mask; struct dma_slave_caps caps; int ret; @@ -157,11 +161,11 @@ int serial8250_request_dma(struct uart_8250_port *p) /* Default slave configuration parameters */ dma->rxconf.direction = DMA_DEV_TO_MEM; dma->rxconf.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; - dma->rxconf.src_addr = p->port.mapbase + UART_RX; + dma->rxconf.src_addr = rx_dma_addr + UART_RX; dma->txconf.direction = DMA_MEM_TO_DEV; dma->txconf.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; - dma->txconf.dst_addr = p->port.mapbase + UART_TX; + dma->txconf.dst_addr = tx_dma_addr + UART_TX; dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); -- cgit v1.2.3 From 68af490b03b40cc31b2cd47899661d87ab9f127d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 17 Aug 2016 19:20:26 +0300 Subject: serial: 8250: enable AFE on ports where FIFO is 16 bytes Intel Quark has 16550A compatible UART with autoflow feature enabled. It has only 16 bytes of FIFO. Currently serial8250_do_set_termios() prevents to enable autoflow since the minimum requirement of 32 bytes of FIFO size. Drop a FIFO size limitation to allow autoflow control be enabled on such UARTs. While here, comment out UART_CAP_AFE for PORT_AR7 since it wasn't working and it will be not a good idea to use it in conjunction with trigger level of 1 byte. Suggested-by: Peter Hurley Reviewed-by: Peter Hurley Signed-off-by: Andy Shevchenko Tested-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_port.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index c45c1e3f3631..1bd7ea0aff03 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -178,7 +178,7 @@ static const struct serial8250_config uart_config[] = { .fifo_size = 16, .tx_loadsz = 16, .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_00, - .flags = UART_CAP_FIFO | UART_CAP_AFE, + .flags = UART_CAP_FIFO /* | UART_CAP_AFE */, }, [PORT_U6_16550A] = { .name = "U6_16550A", @@ -2549,12 +2549,9 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, /* * MCR-based auto flow control. When AFE is enabled, RTS will be * deasserted when the receive FIFO contains more characters than - * the trigger, or the MCR RTS bit is cleared. In the case where - * the remote UART is not using CTS auto flow control, we must - * have sufficient FIFO entries for the latency of the remote - * UART to respond. IOW, at least 32 bytes of FIFO. + * the trigger, or the MCR RTS bit is cleared. */ - if (up->capabilities & UART_CAP_AFE && port->fifosize >= 32) { + if (up->capabilities & UART_CAP_AFE) { up->mcr &= ~UART_MCR_AFE; if (termios->c_cflag & CRTSCTS) up->mcr |= UART_MCR_AFE; -- cgit v1.2.3 From a13e19cf3dc1080cf8a3a174cefd9199610faed7 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 17 Aug 2016 19:20:27 +0300 Subject: serial: 8250_lpss: split LPSS driver to separate module The SoCs, such as Intel Braswell, have DesignWare UART IP. Split out the support of such chips to a separate module which also will be used for Intel Quark later. The rationale to have the separate driver to be existing: - Do not contaminate 8250_pci.c anymore with LPSS related quirks - All of them are using same DMA engine and they are Designware IP which means that in the future we might share the code between 8250_dw.c and 8250_lpss.c - It reduces the kernel memory footprint on non-X86 machines where 8250_pci.c is in use Besides the split the driver also has been refactored, in particular a) the DMA and port setup are separate functions, b) the two new structures lpss8250 and lpss8250_board are introduced to keep necessary data instead of pciserial_board, c) DMA parameters are passed to the DMA setup via mentioned custom structure. Most of the changes are done due to the future support of UART DMA on Intel Quark. The Intel Quark UART DMA support is based on bits taking from BSP code published by Intel earlier. The driver does not use any specific power management. PCI core takes care of the default behaviour during suspend and resume. Signed-off-by: Andy Shevchenko Tested-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_lpss.c | 280 ++++++++++++++++++++++++++++++++++++ drivers/tty/serial/8250/8250_pci.c | 242 ++----------------------------- drivers/tty/serial/8250/Kconfig | 15 +- drivers/tty/serial/8250/Makefile | 1 + 4 files changed, 303 insertions(+), 235 deletions(-) create mode 100644 drivers/tty/serial/8250/8250_lpss.c (limited to 'drivers/tty') diff --git a/drivers/tty/serial/8250/8250_lpss.c b/drivers/tty/serial/8250/8250_lpss.c new file mode 100644 index 000000000000..071f8781c65b --- /dev/null +++ b/drivers/tty/serial/8250/8250_lpss.c @@ -0,0 +1,280 @@ +/* + * 8250_lpss.c - Driver for UART on Intel Braswell and various other Intel SoCs + * + * Copyright (C) 2016 Intel Corporation + * Author: Andy Shevchenko + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include + +#include +#include + +#include "8250.h" + +#define PCI_DEVICE_ID_INTEL_BYT_UART1 0x0f0a +#define PCI_DEVICE_ID_INTEL_BYT_UART2 0x0f0c + +#define PCI_DEVICE_ID_INTEL_BSW_UART1 0x228a +#define PCI_DEVICE_ID_INTEL_BSW_UART2 0x228c + +#define PCI_DEVICE_ID_INTEL_BDW_UART1 0x9ce3 +#define PCI_DEVICE_ID_INTEL_BDW_UART2 0x9ce4 + +/* Intel LPSS specific registers */ + +#define BYT_PRV_CLK 0x800 +#define BYT_PRV_CLK_EN BIT(0) +#define BYT_PRV_CLK_M_VAL_SHIFT 1 +#define BYT_PRV_CLK_N_VAL_SHIFT 16 +#define BYT_PRV_CLK_UPDATE BIT(31) + +#define BYT_TX_OVF_INT 0x820 +#define BYT_TX_OVF_INT_MASK BIT(1) + +struct lpss8250; + +struct lpss8250_board { + unsigned long freq; + unsigned int base_baud; + int (*setup)(struct lpss8250 *, struct uart_port *p); +}; + +struct lpss8250 { + int line; + struct lpss8250_board *board; + + /* DMA parameters */ + struct uart_8250_dma dma; + struct dw_dma_slave dma_param; + u8 dma_maxburst; +}; + +static void byt_set_termios(struct uart_port *p, struct ktermios *termios, + struct ktermios *old) +{ + unsigned int baud = tty_termios_baud_rate(termios); + struct lpss8250 *lpss = p->private_data; + unsigned long fref = lpss->board->freq, fuart = baud * 16; + unsigned long w = BIT(15) - 1; + unsigned long m, n; + u32 reg; + + /* Gracefully handle the B0 case: fall back to B9600 */ + fuart = fuart ? fuart : 9600 * 16; + + /* Get Fuart closer to Fref */ + fuart *= rounddown_pow_of_two(fref / fuart); + + /* + * For baud rates 0.5M, 1M, 1.5M, 2M, 2.5M, 3M, 3.5M and 4M the + * dividers must be adjusted. + * + * uartclk = (m / n) * 100 MHz, where m <= n + */ + rational_best_approximation(fuart, fref, w, w, &m, &n); + p->uartclk = fuart; + + /* Reset the clock */ + reg = (m << BYT_PRV_CLK_M_VAL_SHIFT) | (n << BYT_PRV_CLK_N_VAL_SHIFT); + writel(reg, p->membase + BYT_PRV_CLK); + reg |= BYT_PRV_CLK_EN | BYT_PRV_CLK_UPDATE; + writel(reg, p->membase + BYT_PRV_CLK); + + p->status &= ~UPSTAT_AUTOCTS; + if (termios->c_cflag & CRTSCTS) + p->status |= UPSTAT_AUTOCTS; + + serial8250_do_set_termios(p, termios, old); +} + +static unsigned int byt_get_mctrl(struct uart_port *port) +{ + unsigned int ret = serial8250_do_get_mctrl(port); + + /* Force DCD and DSR signals to permanently be reported as active */ + ret |= TIOCM_CAR | TIOCM_DSR; + + return ret; +} + +static int byt_serial_setup(struct lpss8250 *lpss, struct uart_port *port) +{ + struct dw_dma_slave *param = &lpss->dma_param; + struct uart_8250_port *up = up_to_u8250p(port); + struct pci_dev *pdev = to_pci_dev(port->dev); + unsigned int dma_devfn = PCI_DEVFN(PCI_SLOT(pdev->devfn), 0); + struct pci_dev *dma_dev = pci_get_slot(pdev->bus, dma_devfn); + + switch (pdev->device) { + case PCI_DEVICE_ID_INTEL_BYT_UART1: + case PCI_DEVICE_ID_INTEL_BSW_UART1: + case PCI_DEVICE_ID_INTEL_BDW_UART1: + param->src_id = 3; + param->dst_id = 2; + break; + case PCI_DEVICE_ID_INTEL_BYT_UART2: + case PCI_DEVICE_ID_INTEL_BSW_UART2: + case PCI_DEVICE_ID_INTEL_BDW_UART2: + param->src_id = 5; + param->dst_id = 4; + break; + default: + return -EINVAL; + } + + param->dma_dev = &dma_dev->dev; + param->m_master = 0; + param->p_master = 1; + + /* TODO: Detect FIFO size automaticaly for DesignWare 8250 */ + port->fifosize = 64; + up->tx_loadsz = 64; + + lpss->dma_maxburst = 16; + + port->set_termios = byt_set_termios; + port->get_mctrl = byt_get_mctrl; + + /* Disable TX counter interrupts */ + writel(BYT_TX_OVF_INT_MASK, port->membase + BYT_TX_OVF_INT); + + return 0; +} + +static bool lpss8250_dma_filter(struct dma_chan *chan, void *param) +{ + struct dw_dma_slave *dws = param; + + if (dws->dma_dev != chan->device->dev) + return false; + + chan->private = dws; + return true; +} + +static int lpss8250_dma_setup(struct lpss8250 *lpss, struct uart_8250_port *port) +{ + struct uart_8250_dma *dma = &lpss->dma; + struct dw_dma_slave *rx_param, *tx_param; + struct device *dev = port->port.dev; + + rx_param = devm_kzalloc(dev, sizeof(*rx_param), GFP_KERNEL); + if (!rx_param) + return -ENOMEM; + + tx_param = devm_kzalloc(dev, sizeof(*tx_param), GFP_KERNEL); + if (!tx_param) + return -ENOMEM; + + *rx_param = lpss->dma_param; + dma->rxconf.src_maxburst = lpss->dma_maxburst; + + *tx_param = lpss->dma_param; + dma->txconf.dst_maxburst = lpss->dma_maxburst; + + dma->fn = lpss8250_dma_filter; + dma->rx_param = rx_param; + dma->tx_param = tx_param; + + port->dma = dma; + return 0; +} + +static int lpss8250_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct uart_8250_port uart; + struct lpss8250 *lpss; + int ret; + + ret = pcim_enable_device(pdev); + if (ret) + return ret; + + pci_set_master(pdev); + + lpss = devm_kzalloc(&pdev->dev, sizeof(*lpss), GFP_KERNEL); + if (!lpss) + return -ENOMEM; + + lpss->board = (struct lpss8250_board *)id->driver_data; + + memset(&uart, 0, sizeof(struct uart_8250_port)); + + uart.port.dev = &pdev->dev; + uart.port.irq = pdev->irq; + uart.port.private_data = lpss; + uart.port.type = PORT_16550A; + uart.port.iotype = UPIO_MEM; + uart.port.regshift = 2; + uart.port.uartclk = lpss->board->base_baud * 16; + uart.port.flags = UPF_SHARE_IRQ | UPF_FIXED_PORT | UPF_FIXED_TYPE; + uart.capabilities = UART_CAP_FIFO | UART_CAP_AFE; + uart.port.mapbase = pci_resource_start(pdev, 0); + uart.port.membase = pcim_iomap(pdev, 0, 0); + if (!uart.port.membase) + return -ENOMEM; + + ret = lpss->board->setup(lpss, &uart.port); + if (ret) + return ret; + + ret = lpss8250_dma_setup(lpss, &uart); + if (ret) + return ret; + + ret = serial8250_register_8250_port(&uart); + if (ret < 0) + return ret; + + lpss->line = ret; + + pci_set_drvdata(pdev, lpss); + return 0; +} + +static void lpss8250_remove(struct pci_dev *pdev) +{ + struct lpss8250 *lpss = pci_get_drvdata(pdev); + + serial8250_unregister_port(lpss->line); +} + +static const struct lpss8250_board byt_board = { + .freq = 100000000, + .base_baud = 2764800, + .setup = byt_serial_setup, +}; + +#define LPSS_DEVICE(id, board) { PCI_VDEVICE(INTEL, id), (kernel_ulong_t)&board } + +static const struct pci_device_id pci_ids[] = { + LPSS_DEVICE(PCI_DEVICE_ID_INTEL_BYT_UART1, byt_board), + LPSS_DEVICE(PCI_DEVICE_ID_INTEL_BYT_UART2, byt_board), + LPSS_DEVICE(PCI_DEVICE_ID_INTEL_BSW_UART1, byt_board), + LPSS_DEVICE(PCI_DEVICE_ID_INTEL_BSW_UART2, byt_board), + LPSS_DEVICE(PCI_DEVICE_ID_INTEL_BDW_UART1, byt_board), + LPSS_DEVICE(PCI_DEVICE_ID_INTEL_BDW_UART2, byt_board), + { }, +}; +MODULE_DEVICE_TABLE(pci, pci_ids); + +static struct pci_driver lpss8250_pci_driver = { + .name = "8250_lpss", + .id_table = pci_ids, + .probe = lpss8250_probe, + .remove = lpss8250_remove, +}; + +module_pci_driver(lpss8250_pci_driver); + +MODULE_AUTHOR("Intel Corporation"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Intel LPSS UART driver"); diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index 20ebaea5c414..e968c7019869 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -21,14 +21,10 @@ #include #include #include -#include #include #include -#include -#include - #include "8250.h" /* @@ -1349,160 +1345,6 @@ ce4100_serial_setup(struct serial_private *priv, return ret; } -#define PCI_DEVICE_ID_INTEL_BYT_UART1 0x0f0a -#define PCI_DEVICE_ID_INTEL_BYT_UART2 0x0f0c - -#define PCI_DEVICE_ID_INTEL_BSW_UART1 0x228a -#define PCI_DEVICE_ID_INTEL_BSW_UART2 0x228c - -#define PCI_DEVICE_ID_INTEL_BDW_UART1 0x9ce3 -#define PCI_DEVICE_ID_INTEL_BDW_UART2 0x9ce4 - -#define BYT_PRV_CLK 0x800 -#define BYT_PRV_CLK_EN (1 << 0) -#define BYT_PRV_CLK_M_VAL_SHIFT 1 -#define BYT_PRV_CLK_N_VAL_SHIFT 16 -#define BYT_PRV_CLK_UPDATE (1 << 31) - -#define BYT_TX_OVF_INT 0x820 -#define BYT_TX_OVF_INT_MASK (1 << 1) - -static void -byt_set_termios(struct uart_port *p, struct ktermios *termios, - struct ktermios *old) -{ - unsigned int baud = tty_termios_baud_rate(termios); - unsigned long fref = 100000000, fuart = baud * 16; - unsigned long w = BIT(15) - 1; - unsigned long m, n; - u32 reg; - - /* Gracefully handle the B0 case: fall back to B9600 */ - fuart = fuart ? fuart : 9600 * 16; - - /* Get Fuart closer to Fref */ - fuart *= rounddown_pow_of_two(fref / fuart); - - /* - * For baud rates 0.5M, 1M, 1.5M, 2M, 2.5M, 3M, 3.5M and 4M the - * dividers must be adjusted. - * - * uartclk = (m / n) * 100 MHz, where m <= n - */ - rational_best_approximation(fuart, fref, w, w, &m, &n); - p->uartclk = fuart; - - /* Reset the clock */ - reg = (m << BYT_PRV_CLK_M_VAL_SHIFT) | (n << BYT_PRV_CLK_N_VAL_SHIFT); - writel(reg, p->membase + BYT_PRV_CLK); - reg |= BYT_PRV_CLK_EN | BYT_PRV_CLK_UPDATE; - writel(reg, p->membase + BYT_PRV_CLK); - - p->status &= ~UPSTAT_AUTOCTS; - if (termios->c_cflag & CRTSCTS) - p->status |= UPSTAT_AUTOCTS; - - serial8250_do_set_termios(p, termios, old); -} - -static bool byt_dma_filter(struct dma_chan *chan, void *param) -{ - struct dw_dma_slave *dws = param; - - if (dws->dma_dev != chan->device->dev) - return false; - - chan->private = dws; - return true; -} - -static unsigned int -byt_get_mctrl(struct uart_port *port) -{ - unsigned int ret = serial8250_do_get_mctrl(port); - - /* Force DCD and DSR signals to permanently be reported as active. */ - ret |= TIOCM_CAR | TIOCM_DSR; - - return ret; -} - -static int -byt_serial_setup(struct serial_private *priv, - const struct pciserial_board *board, - struct uart_8250_port *port, int idx) -{ - struct pci_dev *pdev = priv->dev; - struct device *dev = port->port.dev; - struct uart_8250_dma *dma; - struct dw_dma_slave *tx_param, *rx_param; - struct pci_dev *dma_dev; - int ret; - - dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL); - if (!dma) - return -ENOMEM; - - tx_param = devm_kzalloc(dev, sizeof(*tx_param), GFP_KERNEL); - if (!tx_param) - return -ENOMEM; - - rx_param = devm_kzalloc(dev, sizeof(*rx_param), GFP_KERNEL); - if (!rx_param) - return -ENOMEM; - - switch (pdev->device) { - case PCI_DEVICE_ID_INTEL_BYT_UART1: - case PCI_DEVICE_ID_INTEL_BSW_UART1: - case PCI_DEVICE_ID_INTEL_BDW_UART1: - rx_param->src_id = 3; - tx_param->dst_id = 2; - break; - case PCI_DEVICE_ID_INTEL_BYT_UART2: - case PCI_DEVICE_ID_INTEL_BSW_UART2: - case PCI_DEVICE_ID_INTEL_BDW_UART2: - rx_param->src_id = 5; - tx_param->dst_id = 4; - break; - default: - return -EINVAL; - } - - rx_param->m_master = 0; - rx_param->p_master = 1; - - dma->rxconf.src_maxburst = 16; - - tx_param->m_master = 0; - tx_param->p_master = 1; - - dma->txconf.dst_maxburst = 16; - - dma_dev = pci_get_slot(pdev->bus, PCI_DEVFN(PCI_SLOT(pdev->devfn), 0)); - rx_param->dma_dev = &dma_dev->dev; - tx_param->dma_dev = &dma_dev->dev; - - dma->fn = byt_dma_filter; - dma->rx_param = rx_param; - dma->tx_param = tx_param; - - ret = pci_default_setup(priv, board, port, idx); - port->port.iotype = UPIO_MEM; - port->port.type = PORT_16550A; - port->port.flags = (port->port.flags | UPF_FIXED_PORT | UPF_FIXED_TYPE); - port->port.set_termios = byt_set_termios; - port->port.get_mctrl = byt_get_mctrl; - port->port.fifosize = 64; - port->tx_loadsz = 64; - port->dma = dma; - port->capabilities = UART_CAP_FIFO | UART_CAP_AFE; - - /* Disable Tx counter interrupts */ - writel(BYT_TX_OVF_INT_MASK, port->port.membase + BYT_TX_OVF_INT); - - return ret; -} - static int pci_omegapci_setup(struct serial_private *priv, const struct pciserial_board *board, @@ -2041,48 +1883,6 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .subdevice = PCI_ANY_ID, .setup = kt_serial_setup, }, - { - .vendor = PCI_VENDOR_ID_INTEL, - .device = PCI_DEVICE_ID_INTEL_BYT_UART1, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .setup = byt_serial_setup, - }, - { - .vendor = PCI_VENDOR_ID_INTEL, - .device = PCI_DEVICE_ID_INTEL_BYT_UART2, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .setup = byt_serial_setup, - }, - { - .vendor = PCI_VENDOR_ID_INTEL, - .device = PCI_DEVICE_ID_INTEL_BSW_UART1, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .setup = byt_serial_setup, - }, - { - .vendor = PCI_VENDOR_ID_INTEL, - .device = PCI_DEVICE_ID_INTEL_BSW_UART2, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .setup = byt_serial_setup, - }, - { - .vendor = PCI_VENDOR_ID_INTEL, - .device = PCI_DEVICE_ID_INTEL_BDW_UART1, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .setup = byt_serial_setup, - }, - { - .vendor = PCI_VENDOR_ID_INTEL, - .device = PCI_DEVICE_ID_INTEL_BDW_UART2, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .setup = byt_serial_setup, - }, /* * ITE */ @@ -2955,7 +2755,6 @@ enum pci_board_num_t { pbn_ADDIDATA_PCIe_4_3906250, pbn_ADDIDATA_PCIe_8_3906250, pbn_ce4100_1_115200, - pbn_byt, pbn_qrk, pbn_omegapci, pbn_NETMOS9900_2s_115200, @@ -3732,12 +3531,6 @@ static struct pciserial_board pci_boards[] = { .base_baud = 921600, .reg_shift = 2, }, - [pbn_byt] = { - .flags = FL_BASE0, - .num_ports = 1, - .base_baud = 2764800, - .reg_shift = 2, - }, [pbn_qrk] = { .flags = FL_BASE0, .num_ports = 1, @@ -3855,6 +3648,14 @@ static const struct pci_device_id blacklist[] = { { PCI_VDEVICE(INTEL, 0x081d), }, { PCI_VDEVICE(INTEL, 0x1191), }, { PCI_VDEVICE(INTEL, 0x19d8), }, + + /* Intel platforms with DesignWare UART */ + { PCI_VDEVICE(INTEL, 0x0f0a), }, + { PCI_VDEVICE(INTEL, 0x0f0c), }, + { PCI_VDEVICE(INTEL, 0x228a), }, + { PCI_VDEVICE(INTEL, 0x228c), }, + { PCI_VDEVICE(INTEL, 0x9ce3), }, + { PCI_VDEVICE(INTEL, 0x9ce4), }, }; /* @@ -5520,33 +5321,6 @@ static struct pci_device_id serial_pci_tbl[] = { { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CE4100_UART, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_ce4100_1_115200 }, - /* Intel BayTrail */ - { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BYT_UART1, - PCI_ANY_ID, PCI_ANY_ID, - PCI_CLASS_COMMUNICATION_SERIAL << 8, 0xff0000, - pbn_byt }, - { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BYT_UART2, - PCI_ANY_ID, PCI_ANY_ID, - PCI_CLASS_COMMUNICATION_SERIAL << 8, 0xff0000, - pbn_byt }, - { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BSW_UART1, - PCI_ANY_ID, PCI_ANY_ID, - PCI_CLASS_COMMUNICATION_SERIAL << 8, 0xff0000, - pbn_byt }, - { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BSW_UART2, - PCI_ANY_ID, PCI_ANY_ID, - PCI_CLASS_COMMUNICATION_SERIAL << 8, 0xff0000, - pbn_byt }, - - /* Intel Broadwell */ - { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BDW_UART1, - PCI_ANY_ID, PCI_ANY_ID, - PCI_CLASS_COMMUNICATION_SERIAL << 8, 0xff0000, - pbn_byt }, - { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BDW_UART2, - PCI_ANY_ID, PCI_ANY_ID, - PCI_CLASS_COMMUNICATION_SERIAL << 8, 0xff0000, - pbn_byt }, /* * Intel Quark x1000 diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig index c9ec839a5ddf..255a4326b7db 100644 --- a/drivers/tty/serial/8250/Kconfig +++ b/drivers/tty/serial/8250/Kconfig @@ -121,7 +121,6 @@ config SERIAL_8250_PCI tristate "8250/16550 PCI device support" if EXPERT depends on SERIAL_8250 && PCI default SERIAL_8250 - select RATIONAL help This builds standard PCI serial support. You may be able to disable this feature if you only need legacy serial support. @@ -403,6 +402,20 @@ config SERIAL_8250_INGENIC If you have a system using an Ingenic SoC and wish to make use of its UARTs, say Y to this option. If unsure, say N. +config SERIAL_8250_LPSS + tristate "Support for serial ports on Intel LPSS platforms" if EXPERT + default SERIAL_8250 + depends on SERIAL_8250 && PCI + depends on X86 || COMPILE_TEST + select DW_DMAC_CORE if SERIAL_8250_DMA + select DW_DMAC_PCI if (SERIAL_8250_DMA && X86_INTEL_LPSS) + select RATIONAL + help + Selecting this option will enable handling of the extra features + present on the UART found on various Intel platforms such as: + - Intel Baytrail SoC + - Intel Braswell SoC + config SERIAL_8250_MID tristate "Support for serial ports on Intel MID platforms" if EXPERT default SERIAL_8250 diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile index 367d403d28d5..276c6fb60337 100644 --- a/drivers/tty/serial/8250/Makefile +++ b/drivers/tty/serial/8250/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_SERIAL_8250_LPC18XX) += 8250_lpc18xx.o obj-$(CONFIG_SERIAL_8250_MT6577) += 8250_mtk.o obj-$(CONFIG_SERIAL_8250_UNIPHIER) += 8250_uniphier.o obj-$(CONFIG_SERIAL_8250_INGENIC) += 8250_ingenic.o +obj-$(CONFIG_SERIAL_8250_LPSS) += 8250_lpss.o obj-$(CONFIG_SERIAL_8250_MID) += 8250_mid.o obj-$(CONFIG_SERIAL_8250_MOXA) += 8250_moxa.o obj-$(CONFIG_SERIAL_OF_PLATFORM) += 8250_of.o -- cgit v1.2.3 From 6bb5d75eac8d6a9ef85b408909d40964eda53716 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 17 Aug 2016 19:20:28 +0300 Subject: serial: 8250_lpss: move Quark code from PCI driver Intel Quark has DesignWare UART. Move the code from 8250_pci to 8250_lpss. Reviewed-by: Bryan O'Donoghue Signed-off-by: Andy Shevchenko Tested-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_lpss.c | 11 +++++++++++ drivers/tty/serial/8250/8250_pci.c | 15 +-------------- drivers/tty/serial/8250/Kconfig | 1 + 3 files changed, 13 insertions(+), 14 deletions(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/8250/8250_lpss.c b/drivers/tty/serial/8250/8250_lpss.c index 071f8781c65b..f3cf4e8a5086 100644 --- a/drivers/tty/serial/8250/8250_lpss.c +++ b/drivers/tty/serial/8250/8250_lpss.c @@ -19,6 +19,8 @@ #include "8250.h" +#define PCI_DEVICE_ID_INTEL_QRK_UARTx 0x0936 + #define PCI_DEVICE_ID_INTEL_BYT_UART1 0x0f0a #define PCI_DEVICE_ID_INTEL_BYT_UART2 0x0f0c @@ -166,6 +168,9 @@ static int lpss8250_dma_setup(struct lpss8250 *lpss, struct uart_8250_port *port struct dw_dma_slave *rx_param, *tx_param; struct device *dev = port->port.dev; + if (!lpss->dma_param.dma_dev) + return 0; + rx_param = devm_kzalloc(dev, sizeof(*rx_param), GFP_KERNEL); if (!rx_param) return -ENOMEM; @@ -253,9 +258,15 @@ static const struct lpss8250_board byt_board = { .setup = byt_serial_setup, }; +static const struct lpss8250_board qrk_board = { + .freq = 44236800, + .base_baud = 2764800, +}; + #define LPSS_DEVICE(id, board) { PCI_VDEVICE(INTEL, id), (kernel_ulong_t)&board } static const struct pci_device_id pci_ids[] = { + LPSS_DEVICE(PCI_DEVICE_ID_INTEL_QRK_UARTx, qrk_board), LPSS_DEVICE(PCI_DEVICE_ID_INTEL_BYT_UART1, byt_board), LPSS_DEVICE(PCI_DEVICE_ID_INTEL_BYT_UART2, byt_board), LPSS_DEVICE(PCI_DEVICE_ID_INTEL_BSW_UART1, byt_board), diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index e968c7019869..89832c370813 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -1776,7 +1776,6 @@ pci_wch_ch38x_setup(struct serial_private *priv, #define PCI_DEVICE_ID_COMMTECH_4222PCIE 0x0022 #define PCI_DEVICE_ID_BROADCOM_TRUMANAGE 0x160a #define PCI_DEVICE_ID_AMCC_ADDIDATA_APCI7800 0x818e -#define PCI_DEVICE_ID_INTEL_QRK_UART 0x0936 #define PCI_VENDOR_ID_SUNIX 0x1fd4 #define PCI_DEVICE_ID_SUNIX_1999 0x1999 @@ -2755,7 +2754,6 @@ enum pci_board_num_t { pbn_ADDIDATA_PCIe_4_3906250, pbn_ADDIDATA_PCIe_8_3906250, pbn_ce4100_1_115200, - pbn_qrk, pbn_omegapci, pbn_NETMOS9900_2s_115200, pbn_brcm_trumanage, @@ -3531,12 +3529,6 @@ static struct pciserial_board pci_boards[] = { .base_baud = 921600, .reg_shift = 2, }, - [pbn_qrk] = { - .flags = FL_BASE0, - .num_ports = 1, - .base_baud = 2764800, - .reg_shift = 2, - }, [pbn_omegapci] = { .flags = FL_BASE0, .num_ports = 8, @@ -3650,6 +3642,7 @@ static const struct pci_device_id blacklist[] = { { PCI_VDEVICE(INTEL, 0x19d8), }, /* Intel platforms with DesignWare UART */ + { PCI_VDEVICE(INTEL, 0x0936), }, { PCI_VDEVICE(INTEL, 0x0f0a), }, { PCI_VDEVICE(INTEL, 0x0f0c), }, { PCI_VDEVICE(INTEL, 0x228a), }, @@ -5322,12 +5315,6 @@ static struct pci_device_id serial_pci_tbl[] = { PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_ce4100_1_115200 }, - /* - * Intel Quark x1000 - */ - { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_QRK_UART, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_qrk }, /* * Cronyx Omega PCI */ diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig index 255a4326b7db..357f31832538 100644 --- a/drivers/tty/serial/8250/Kconfig +++ b/drivers/tty/serial/8250/Kconfig @@ -415,6 +415,7 @@ config SERIAL_8250_LPSS present on the UART found on various Intel platforms such as: - Intel Baytrail SoC - Intel Braswell SoC + - Intel Quark X1000 SoC config SERIAL_8250_MID tristate "Support for serial ports on Intel MID platforms" if EXPERT -- cgit v1.2.3 From 60a9244a5d14218cbdd2030e140e3c693d0c030d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 17 Aug 2016 19:20:29 +0300 Subject: serial: 8250_lpss: enable MSI for Intel Quark Intel Quark SoC supports MSI for LPSS, in particular for UART. Enable MSI for Intel Quark. Signed-off-by: Andy Shevchenko Tested-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_lpss.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/8250/8250_lpss.c b/drivers/tty/serial/8250/8250_lpss.c index f3cf4e8a5086..c0db2a85c1f5 100644 --- a/drivers/tty/serial/8250/8250_lpss.c +++ b/drivers/tty/serial/8250/8250_lpss.c @@ -151,6 +151,20 @@ static int byt_serial_setup(struct lpss8250 *lpss, struct uart_port *port) return 0; } +static int qrk_serial_setup(struct lpss8250 *lpss, struct uart_port *port) +{ + struct pci_dev *pdev = to_pci_dev(port->dev); + int ret; + + ret = pci_alloc_irq_vectors(pdev, 1, 1, 0); + if (ret < 0) + return ret; + + port->irq = pci_irq_vector(pdev, 0); + + return 0; +} + static bool lpss8250_dma_filter(struct dma_chan *chan, void *param) { struct dw_dma_slave *dws = param; @@ -261,6 +275,7 @@ static const struct lpss8250_board byt_board = { static const struct lpss8250_board qrk_board = { .freq = 44236800, .base_baud = 2764800, + .setup = qrk_serial_setup, }; #define LPSS_DEVICE(id, board) { PCI_VDEVICE(INTEL, id), (kernel_ulong_t)&board } -- cgit v1.2.3 From fecdef932b0093b4a7e1ae88b1dbd63eb74ecc34 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 17 Aug 2016 19:20:30 +0300 Subject: serial: 8250_lpss: enable DMA on Intel Quark UART DMA on Intel Quark SoC is a part of UART IP block. Enable it. Signed-off-by: Andy Shevchenko Tested-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_lpss.c | 78 +++++++++++++++++++++++++++++++++++-- 1 file changed, 75 insertions(+), 3 deletions(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/8250/8250_lpss.c b/drivers/tty/serial/8250/8250_lpss.c index c0db2a85c1f5..886fcf37f291 100644 --- a/drivers/tty/serial/8250/8250_lpss.c +++ b/drivers/tty/serial/8250/8250_lpss.c @@ -15,7 +15,7 @@ #include #include -#include +#include #include "8250.h" @@ -47,6 +47,7 @@ struct lpss8250_board { unsigned long freq; unsigned int base_baud; int (*setup)(struct lpss8250 *, struct uart_port *p); + void (*exit)(struct lpss8250 *); }; struct lpss8250 { @@ -55,6 +56,7 @@ struct lpss8250 { /* DMA parameters */ struct uart_8250_dma dma; + struct dw_dma_chip dma_chip; struct dw_dma_slave dma_param; u8 dma_maxburst; }; @@ -151,6 +153,61 @@ static int byt_serial_setup(struct lpss8250 *lpss, struct uart_port *port) return 0; } +#ifdef CONFIG_SERIAL_8250_DMA +static const struct dw_dma_platform_data qrk_serial_dma_pdata = { + .nr_channels = 2, + .is_private = true, + .is_nollp = true, + .chan_allocation_order = CHAN_ALLOCATION_ASCENDING, + .chan_priority = CHAN_PRIORITY_ASCENDING, + .block_size = 4095, + .nr_masters = 1, + .data_width = {4}, +}; + +static void qrk_serial_setup_dma(struct lpss8250 *lpss, struct uart_port *port) +{ + struct uart_8250_dma *dma = &lpss->dma; + struct dw_dma_chip *chip = &lpss->dma_chip; + struct dw_dma_slave *param = &lpss->dma_param; + struct pci_dev *pdev = to_pci_dev(port->dev); + int ret; + + chip->dev = &pdev->dev; + chip->irq = pdev->irq; + chip->regs = pci_ioremap_bar(pdev, 1); + chip->pdata = &qrk_serial_dma_pdata; + + /* Falling back to PIO mode if DMA probing fails */ + ret = dw_dma_probe(chip); + if (ret) + return; + + /* Special DMA address for UART */ + dma->rx_dma_addr = 0xfffff000; + dma->tx_dma_addr = 0xfffff000; + + param->dma_dev = &pdev->dev; + param->src_id = 0; + param->dst_id = 1; + param->hs_polarity = true; + + lpss->dma_maxburst = 8; +} + +static void qrk_serial_exit_dma(struct lpss8250 *lpss) +{ + struct dw_dma_slave *param = &lpss->dma_param; + + if (!param->dma_dev) + return; + dw_dma_remove(&lpss->dma_chip); +} +#else /* CONFIG_SERIAL_8250_DMA */ +static void qrk_serial_setup_dma(struct lpss8250 *lpss, struct uart_port *port) {} +static void qrk_serial_exit_dma(struct lpss8250 *lpss) {} +#endif /* !CONFIG_SERIAL_8250_DMA */ + static int qrk_serial_setup(struct lpss8250 *lpss, struct uart_port *port) { struct pci_dev *pdev = to_pci_dev(port->dev); @@ -162,9 +219,15 @@ static int qrk_serial_setup(struct lpss8250 *lpss, struct uart_port *port) port->irq = pci_irq_vector(pdev, 0); + qrk_serial_setup_dma(lpss, port); return 0; } +static void qrk_serial_exit(struct lpss8250 *lpss) +{ + qrk_serial_exit_dma(lpss); +} + static bool lpss8250_dma_filter(struct dma_chan *chan, void *param) { struct dw_dma_slave *dws = param; @@ -247,22 +310,30 @@ static int lpss8250_probe(struct pci_dev *pdev, const struct pci_device_id *id) ret = lpss8250_dma_setup(lpss, &uart); if (ret) - return ret; + goto err_exit; ret = serial8250_register_8250_port(&uart); if (ret < 0) - return ret; + goto err_exit; lpss->line = ret; pci_set_drvdata(pdev, lpss); return 0; + +err_exit: + if (lpss->board->exit) + lpss->board->exit(lpss); + return ret; } static void lpss8250_remove(struct pci_dev *pdev) { struct lpss8250 *lpss = pci_get_drvdata(pdev); + if (lpss->board->exit) + lpss->board->exit(lpss); + serial8250_unregister_port(lpss->line); } @@ -276,6 +347,7 @@ static const struct lpss8250_board qrk_board = { .freq = 44236800, .base_baud = 2764800, .setup = qrk_serial_setup, + .exit = qrk_serial_exit, }; #define LPSS_DEVICE(id, board) { PCI_VDEVICE(INTEL, id), (kernel_ulong_t)&board } -- cgit v1.2.3 From 9d297239b8cbf276b1b3bc6cbde5c0dd9ca79a61 Mon Sep 17 00:00:00 2001 From: Nandor Han Date: Mon, 8 Aug 2016 15:38:27 +0300 Subject: serial: imx-serial - update UART IMX driver to use cyclic DMA The IMX UART has a 32 bytes HW buffer which can be filled up in 2777us at 115200 baud or 80us at 4Mbaud (supported by IMX53). Taking this in consideration there is a good probability to lose data because of the DMA startup latency. Our tests (explained below) indicates a latency up to 4400us when creating interrupt load and ~70us without. When creating interrupt load I was able to see continuous overrun errors by checking serial driver statistics using the command: `cat /proc/tty/driver/IMX-uart`. Replace manual restart of DMA with cyclic DMA to eliminate data loss due to DMA engine startup latency (similar approch to atmel_serial.c driver). As result the DMA engine will start on the first serial data transfer and stops only when serial port is closed. Tests environment: Using the m53evk board I have used a GPIO for profiling the IMX serial driver. - The RX line and GPIO were connected to oscilloscope. - Run a small test program on the m53evk board that will only open and read data from ttymxc2 port. - Connect the ttymxc2 port to my laptop using a USB serial converter where another test program is running, able to send configurable packet lengths and intervals. - Serial ports configured at 115200 8N1. - Interrupts load created by disconnecting/connecting (3s interval) a USB hub, using a digital switch, with 4 USB devices (USB-Serial converter, USB SD card, etc) connected. (around 160 interrupts/second generated) - The GPIO was toggled HI in the `imx_int` when USR1_RRDY or USR1_AGTIM events are received and toggled back, once the DMA configuration is finalized, at the end of `imx_dma_rxint`. Measurements: The measurements were done from the end of the last byte (RX line) until the GPIO was toggled back LOW. Note: The GPIO toggling was done using `gpiod_set_value` method. Tests performed: 1. Sending 9 bytes packets at 8ms interval. Having the 9 bytes packets will activate the RRDY threshold event and IMX serial interrupt called. Results: - DMA start latency (interrupt start latency + DMA configuration) consistently 70us when system not loaded. - DMA start latency up to 4400us when system loaded. 2. Sending 40 bytes packet at 8mS interval. Results with load: - Able to observe overruns by running: `watch -n1 cat /proc/tty/driver/IMX-uart` Tested-by: Peter Senna Tschudin Acked-by: Peter Senna Tschudin Signed-off-by: Nandor Han Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/imx.c | 141 ++++++++++++++++++++++++++--------------------- 1 file changed, 78 insertions(+), 63 deletions(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 0df2b1c091ae..1912136f7257 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -222,6 +222,9 @@ struct imx_port { struct dma_chan *dma_chan_rx, *dma_chan_tx; struct scatterlist rx_sgl, tx_sgl[2]; void *rx_buf; + struct circ_buf rx_ring; + unsigned int rx_periods; + dma_cookie_t rx_cookie; unsigned int tx_bytes; unsigned int dma_tx_nents; wait_queue_head_t dma_wait; @@ -932,30 +935,6 @@ static void imx_timeout(unsigned long data) } #define RX_BUF_SIZE (PAGE_SIZE) -static void imx_rx_dma_done(struct imx_port *sport) -{ - unsigned long temp; - unsigned long flags; - - spin_lock_irqsave(&sport->port.lock, flags); - - /* re-enable interrupts to get notified when new symbols are incoming */ - temp = readl(sport->port.membase + UCR1); - temp |= UCR1_RRDYEN; - writel(temp, sport->port.membase + UCR1); - - temp = readl(sport->port.membase + UCR2); - temp |= UCR2_ATEN; - writel(temp, sport->port.membase + UCR2); - - sport->dma_is_rxing = 0; - - /* Is the shutdown waiting for us? */ - if (waitqueue_active(&sport->dma_wait)) - wake_up(&sport->dma_wait); - - spin_unlock_irqrestore(&sport->port.lock, flags); -} /* * There are two kinds of RX DMA interrupts(such as in the MX6Q): @@ -972,43 +951,75 @@ static void dma_rx_callback(void *data) struct scatterlist *sgl = &sport->rx_sgl; struct tty_port *port = &sport->port.state->port; struct dma_tx_state state; + struct circ_buf *rx_ring = &sport->rx_ring; enum dma_status status; - unsigned int count; - - /* unmap it first */ - dma_unmap_sg(sport->port.dev, sgl, 1, DMA_FROM_DEVICE); + unsigned int w_bytes = 0; + unsigned int r_bytes; + unsigned int bd_size; status = dmaengine_tx_status(chan, (dma_cookie_t)0, &state); - count = RX_BUF_SIZE - state.residue; - dev_dbg(sport->port.dev, "We get %d bytes.\n", count); + if (status == DMA_ERROR) { + dev_err(sport->port.dev, "DMA transaction error.\n"); + return; + } + + if (!(sport->port.ignore_status_mask & URXD_DUMMY_READ)) { + + /* + * The state-residue variable represents the empty space + * relative to the entire buffer. Taking this in consideration + * the head is always calculated base on the buffer total + * length - DMA transaction residue. The UART script from the + * SDMA firmware will jump to the next buffer descriptor, + * once a DMA transaction if finalized (IMX53 RM - A.4.1.2.4). + * Taking this in consideration the tail is always at the + * beginning of the buffer descriptor that contains the head. + */ + + /* Calculate the head */ + rx_ring->head = sg_dma_len(sgl) - state.residue; + + /* Calculate the tail. */ + bd_size = sg_dma_len(sgl) / sport->rx_periods; + rx_ring->tail = ((rx_ring->head-1) / bd_size) * bd_size; + + if (rx_ring->head <= sg_dma_len(sgl) && + rx_ring->head > rx_ring->tail) { + + /* Move data from tail to head */ + r_bytes = rx_ring->head - rx_ring->tail; + + /* CPU claims ownership of RX DMA buffer */ + dma_sync_sg_for_cpu(sport->port.dev, sgl, 1, + DMA_FROM_DEVICE); - if (count) { - if (!(sport->port.ignore_status_mask & URXD_DUMMY_READ)) { - int bytes = tty_insert_flip_string(port, sport->rx_buf, - count); + w_bytes = tty_insert_flip_string(port, + sport->rx_buf + rx_ring->tail, r_bytes); - if (bytes != count) + /* UART retrieves ownership of RX DMA buffer */ + dma_sync_sg_for_device(sport->port.dev, sgl, 1, + DMA_FROM_DEVICE); + + if (w_bytes != r_bytes) sport->port.icount.buf_overrun++; + + sport->port.icount.rx += w_bytes; + } else { + WARN_ON(rx_ring->head > sg_dma_len(sgl)); + WARN_ON(rx_ring->head <= rx_ring->tail); } - tty_flip_buffer_push(port); - sport->port.icount.rx += count; } - /* - * Restart RX DMA directly if more data is available in order to skip - * the roundtrip through the IRQ handler. If there is some data already - * in the FIFO, DMA needs to be restarted soon anyways. - * - * Otherwise stop the DMA and reactivate FIFO IRQs to restart DMA once - * data starts to arrive again. - */ - if (readl(sport->port.membase + USR2) & USR2_RDR) - start_rx_dma(sport); - else - imx_rx_dma_done(sport); + if (w_bytes) { + tty_flip_buffer_push(port); + dev_dbg(sport->port.dev, "We get %d bytes.\n", w_bytes); + } } +/* RX DMA buffer periods */ +#define RX_DMA_PERIODS 4 + static int start_rx_dma(struct imx_port *sport) { struct scatterlist *sgl = &sport->rx_sgl; @@ -1017,14 +1028,21 @@ static int start_rx_dma(struct imx_port *sport) struct dma_async_tx_descriptor *desc; int ret; + sport->rx_ring.head = 0; + sport->rx_ring.tail = 0; + sport->rx_periods = RX_DMA_PERIODS; + sg_init_one(sgl, sport->rx_buf, RX_BUF_SIZE); ret = dma_map_sg(dev, sgl, 1, DMA_FROM_DEVICE); if (ret == 0) { dev_err(dev, "DMA mapping error for RX.\n"); return -EINVAL; } - desc = dmaengine_prep_slave_sg(chan, sgl, 1, DMA_DEV_TO_MEM, - DMA_PREP_INTERRUPT); + + desc = dmaengine_prep_dma_cyclic(chan, sg_dma_address(sgl), + sg_dma_len(sgl), sg_dma_len(sgl) / sport->rx_periods, + DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT); + if (!desc) { dma_unmap_sg(dev, sgl, 1, DMA_FROM_DEVICE); dev_err(dev, "We cannot prepare for the RX slave dma!\n"); @@ -1034,7 +1052,7 @@ static int start_rx_dma(struct imx_port *sport) desc->callback_param = sport; dev_dbg(dev, "RX: prepare for the DMA.\n"); - dmaengine_submit(desc); + sport->rx_cookie = dmaengine_submit(desc); dma_async_issue_pending(chan); return 0; } @@ -1058,14 +1076,16 @@ static void imx_setup_ufcr(struct imx_port *sport, static void imx_uart_dma_exit(struct imx_port *sport) { if (sport->dma_chan_rx) { + dmaengine_terminate_all(sport->dma_chan_rx); dma_release_channel(sport->dma_chan_rx); sport->dma_chan_rx = NULL; - + sport->rx_cookie = -EINVAL; kfree(sport->rx_buf); sport->rx_buf = NULL; } if (sport->dma_chan_tx) { + dmaengine_terminate_all(sport->dma_chan_tx); dma_release_channel(sport->dma_chan_tx); sport->dma_chan_tx = NULL; } @@ -1103,6 +1123,7 @@ static int imx_uart_dma_init(struct imx_port *sport) ret = -ENOMEM; goto err; } + sport->rx_ring.buf = sport->rx_buf; /* Prepare for TX : */ sport->dma_chan_tx = dma_request_slave_channel(dev, "tx"); @@ -1283,17 +1304,11 @@ static void imx_shutdown(struct uart_port *port) unsigned long flags; if (sport->dma_is_enabled) { - int ret; + sport->dma_is_rxing = 0; + sport->dma_is_txing = 0; + dmaengine_terminate_all(sport->dma_chan_tx); + dmaengine_terminate_all(sport->dma_chan_rx); - /* We have to wait for the DMA to finish. */ - ret = wait_event_interruptible(sport->dma_wait, - !sport->dma_is_rxing && !sport->dma_is_txing); - if (ret != 0) { - sport->dma_is_rxing = 0; - sport->dma_is_txing = 0; - dmaengine_terminate_all(sport->dma_chan_tx); - dmaengine_terminate_all(sport->dma_chan_rx); - } spin_lock_irqsave(&sport->port.lock, flags); imx_stop_tx(port); imx_stop_rx(port); -- cgit v1.2.3 From 41d98b5da92f8b7bd11885e7c4797197b5f3e2c3 Mon Sep 17 00:00:00 2001 From: Nandor Han Date: Mon, 8 Aug 2016 15:38:28 +0300 Subject: serial: imx-serial - update RX error counters when DMA is used Update error counters when DMA is used for receiving data. Do this by using DMA transaction error event instead error interrupts to reduce interrupt load. Tested-by: Peter Senna Tschudin Acked-by: Peter Senna Tschudin Signed-off-by: Nandor Han Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/imx.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 1912136f7257..08ccfe17640d 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -704,6 +704,7 @@ out: return IRQ_HANDLED; } +static void clear_rx_errors(struct imx_port *sport); static int start_rx_dma(struct imx_port *sport); /* * If the RXFIFO is filled with some data, and then we @@ -729,6 +730,11 @@ static void imx_dma_rxint(struct imx_port *sport) temp &= ~(UCR2_ATEN); writel(temp, sport->port.membase + UCR2); + /* disable the rx errors interrupts */ + temp = readl(sport->port.membase + UCR4); + temp &= ~UCR4_OREN; + writel(temp, sport->port.membase + UCR4); + /* tell the DMA to receive the data. */ start_rx_dma(sport); } @@ -961,6 +967,7 @@ static void dma_rx_callback(void *data) if (status == DMA_ERROR) { dev_err(sport->port.dev, "DMA transaction error.\n"); + clear_rx_errors(sport); return; } @@ -1057,6 +1064,31 @@ static int start_rx_dma(struct imx_port *sport) return 0; } +static void clear_rx_errors(struct imx_port *sport) +{ + unsigned int status_usr1, status_usr2; + + status_usr1 = readl(sport->port.membase + USR1); + status_usr2 = readl(sport->port.membase + USR2); + + if (status_usr2 & USR2_BRCD) { + sport->port.icount.brk++; + writel(USR2_BRCD, sport->port.membase + USR2); + } else if (status_usr1 & USR1_FRAMERR) { + sport->port.icount.frame++; + writel(USR1_FRAMERR, sport->port.membase + USR1); + } else if (status_usr1 & USR1_PARITYERR) { + sport->port.icount.parity++; + writel(USR1_PARITYERR, sport->port.membase + USR1); + } + + if (status_usr2 & USR2_ORE) { + sport->port.icount.overrun++; + writel(USR2_ORE, sport->port.membase + USR2); + } + +} + #define TXTL_DEFAULT 2 /* reset default */ #define RXTL_DEFAULT 1 /* reset default */ #define TXTL_DMA 8 /* DMA burst setting */ -- cgit v1.2.3 From 2cb78eab2376a36d688f6740d60c63739149a4a3 Mon Sep 17 00:00:00 2001 From: Kefeng Wang Date: Thu, 1 Sep 2016 10:24:19 +0800 Subject: serial: 8250_dw: Use an unified new dev variable in probe Use an unified new dev variable instead of &pdev->dev and p->dev in probe function. Reviewed-by: Heikki Krogerus Signed-off-by: Kefeng Wang Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_dw.c | 45 ++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 22 deletions(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index b022f5a01e63..47e6d08eb6fd 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -360,18 +360,19 @@ static int dw8250_probe(struct platform_device *pdev) struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); int irq = platform_get_irq(pdev, 0); struct uart_port *p = &uart.port; + struct device *dev = &pdev->dev; struct dw8250_data *data; int err; u32 val; if (!regs) { - dev_err(&pdev->dev, "no registers defined\n"); + dev_err(dev, "no registers defined\n"); return -EINVAL; } if (irq < 0) { if (irq != -EPROBE_DEFER) - dev_err(&pdev->dev, "cannot get irq\n"); + dev_err(dev, "cannot get irq\n"); return irq; } @@ -382,16 +383,16 @@ static int dw8250_probe(struct platform_device *pdev) p->pm = dw8250_do_pm; p->type = PORT_8250; p->flags = UPF_SHARE_IRQ | UPF_FIXED_PORT; - p->dev = &pdev->dev; + p->dev = dev; p->iotype = UPIO_MEM; p->serial_in = dw8250_serial_in; p->serial_out = dw8250_serial_out; - p->membase = devm_ioremap(&pdev->dev, regs->start, resource_size(regs)); + p->membase = devm_ioremap(dev, regs->start, resource_size(regs)); if (!p->membase) return -ENOMEM; - data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; @@ -399,57 +400,57 @@ static int dw8250_probe(struct platform_device *pdev) data->usr_reg = DW_UART_USR; p->private_data = data; - data->uart_16550_compatible = device_property_read_bool(p->dev, + data->uart_16550_compatible = device_property_read_bool(dev, "snps,uart-16550-compatible"); - err = device_property_read_u32(p->dev, "reg-shift", &val); + err = device_property_read_u32(dev, "reg-shift", &val); if (!err) p->regshift = val; - err = device_property_read_u32(p->dev, "reg-io-width", &val); + err = device_property_read_u32(dev, "reg-io-width", &val); if (!err && val == 4) { p->iotype = UPIO_MEM32; p->serial_in = dw8250_serial_in32; p->serial_out = dw8250_serial_out32; } - if (device_property_read_bool(p->dev, "dcd-override")) { + if (device_property_read_bool(dev, "dcd-override")) { /* Always report DCD as active */ data->msr_mask_on |= UART_MSR_DCD; data->msr_mask_off |= UART_MSR_DDCD; } - if (device_property_read_bool(p->dev, "dsr-override")) { + if (device_property_read_bool(dev, "dsr-override")) { /* Always report DSR as active */ data->msr_mask_on |= UART_MSR_DSR; data->msr_mask_off |= UART_MSR_DDSR; } - if (device_property_read_bool(p->dev, "cts-override")) { + if (device_property_read_bool(dev, "cts-override")) { /* Always report CTS as active */ data->msr_mask_on |= UART_MSR_CTS; data->msr_mask_off |= UART_MSR_DCTS; } - if (device_property_read_bool(p->dev, "ri-override")) { + if (device_property_read_bool(dev, "ri-override")) { /* Always report Ring indicator as inactive */ data->msr_mask_off |= UART_MSR_RI; data->msr_mask_off |= UART_MSR_TERI; } /* Always ask for fixed clock rate from a property. */ - device_property_read_u32(p->dev, "clock-frequency", &p->uartclk); + device_property_read_u32(dev, "clock-frequency", &p->uartclk); /* If there is separate baudclk, get the rate from it. */ - data->clk = devm_clk_get(&pdev->dev, "baudclk"); + data->clk = devm_clk_get(dev, "baudclk"); if (IS_ERR(data->clk) && PTR_ERR(data->clk) != -EPROBE_DEFER) - data->clk = devm_clk_get(&pdev->dev, NULL); + data->clk = devm_clk_get(dev, NULL); if (IS_ERR(data->clk) && PTR_ERR(data->clk) == -EPROBE_DEFER) return -EPROBE_DEFER; if (!IS_ERR_OR_NULL(data->clk)) { err = clk_prepare_enable(data->clk); if (err) - dev_warn(&pdev->dev, "could not enable optional baudclk: %d\n", + dev_warn(dev, "could not enable optional baudclk: %d\n", err); else p->uartclk = clk_get_rate(data->clk); @@ -457,11 +458,11 @@ static int dw8250_probe(struct platform_device *pdev) /* If no clock rate is defined, fail. */ if (!p->uartclk) { - dev_err(&pdev->dev, "clock rate not defined\n"); + dev_err(dev, "clock rate not defined\n"); return -EINVAL; } - data->pclk = devm_clk_get(&pdev->dev, "apb_pclk"); + data->pclk = devm_clk_get(dev, "apb_pclk"); if (IS_ERR(data->pclk) && PTR_ERR(data->pclk) == -EPROBE_DEFER) { err = -EPROBE_DEFER; goto err_clk; @@ -469,12 +470,12 @@ static int dw8250_probe(struct platform_device *pdev) if (!IS_ERR(data->pclk)) { err = clk_prepare_enable(data->pclk); if (err) { - dev_err(&pdev->dev, "could not enable apb_pclk\n"); + dev_err(dev, "could not enable apb_pclk\n"); goto err_clk; } } - data->rst = devm_reset_control_get_optional(&pdev->dev, NULL); + data->rst = devm_reset_control_get_optional(dev, NULL); if (IS_ERR(data->rst) && PTR_ERR(data->rst) == -EPROBE_DEFER) { err = -EPROBE_DEFER; goto err_pclk; @@ -506,8 +507,8 @@ static int dw8250_probe(struct platform_device *pdev) platform_set_drvdata(pdev, data); - pm_runtime_set_active(&pdev->dev); - pm_runtime_enable(&pdev->dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); return 0; -- cgit v1.2.3 From 8232884e2dfa606edef66b2c889d4d2ebe042cb8 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Thu, 1 Sep 2016 19:51:38 +0200 Subject: serial/arc: constify uart_ops structures Check for uart_ops structures that are only stored in the ops field of a uart_port structure. This field is declared const, so uart_ops structures that have this property can be declared as const also. The semantic patch that makes this change is as follows: (http://coccinelle.lip6.fr/) // @r disable optional_qualifier@ identifier i; position p; @@ static struct uart_ops i@p = { ... }; @ok@ identifier r.i; struct uart_port e; position p; @@ e.ops = &i@p; @bad@ position p != {r.p,ok.p}; identifier r.i; struct uart_ops e; @@ e@i@p @depends on !bad disable optional_qualifier@ identifier r.i; @@ static +const struct uart_ops i = { ... }; // Signed-off-by: Julia Lawall Acked-by: Vineet Gupta Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/arc_uart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/arc_uart.c b/drivers/tty/serial/arc_uart.c index 3a1de5c87cb4..5ac06fcaa9c6 100644 --- a/drivers/tty/serial/arc_uart.c +++ b/drivers/tty/serial/arc_uart.c @@ -464,7 +464,7 @@ static int arc_serial_poll_getchar(struct uart_port *port) } #endif -static struct uart_ops arc_serial_pops = { +static const struct uart_ops arc_serial_pops = { .tx_empty = arc_serial_tx_empty, .set_mctrl = arc_serial_set_mctrl, .get_mctrl = arc_serial_get_mctrl, -- cgit v1.2.3 From eeb8bc1a92751557d3eea69d54e1b422569e7238 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Thu, 1 Sep 2016 19:51:30 +0200 Subject: serial: st-asc: constify uart_ops structures Check for uart_ops structures that are only stored in the ops field of a uart_port structure. This field is declared const, so uart_ops structures that have this property can be declared as const also. The semantic patch that makes this change is as follows: (http://coccinelle.lip6.fr/) // @r disable optional_qualifier@ identifier i; position p; @@ static struct uart_ops i@p = { ... }; @ok@ identifier r.i; struct uart_port e; position p; @@ e.ops = &i@p; @bad@ position p != {r.p,ok.p}; identifier r.i; struct uart_ops e; @@ e@i@p @depends on !bad disable optional_qualifier@ identifier r.i; @@ static +const struct uart_ops i = { ... }; // Signed-off-by: Julia Lawall Acked-by: Peter Griffin Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/st-asc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/st-asc.c b/drivers/tty/serial/st-asc.c index 2d78cb3627ae..379e5bd37df9 100644 --- a/drivers/tty/serial/st-asc.c +++ b/drivers/tty/serial/st-asc.c @@ -639,7 +639,7 @@ static void asc_put_poll_char(struct uart_port *port, unsigned char c) /*---------------------------------------------------------------------*/ -static struct uart_ops asc_uart_ops = { +static const struct uart_ops asc_uart_ops = { .tx_empty = asc_tx_empty, .set_mctrl = asc_set_mctrl, .get_mctrl = asc_get_mctrl, -- cgit v1.2.3 From 5c7dcdb60d88e4d68bc59eff7c6a5c418808552b Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Thu, 1 Sep 2016 19:51:31 +0200 Subject: tty/serial: at91: constify uart_ops structures Check for uart_ops structures that are only stored in the ops field of a uart_port structure. This field is declared const, so uart_ops structures that have this property can be declared as const also. The semantic patch that makes this change is as follows: (http://coccinelle.lip6.fr/) // @r disable optional_qualifier@ identifier i; position p; @@ static struct uart_ops i@p = { ... }; @ok@ identifier r.i; struct uart_port e; position p; @@ e.ops = &i@p; @bad@ position p != {r.p,ok.p}; identifier r.i; struct uart_ops e; @@ e@i@p @depends on !bad disable optional_qualifier@ identifier r.i; @@ static +const struct uart_ops i = { ... }; // Signed-off-by: Julia Lawall Acked-by: Nicolas Ferre Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/atmel_serial.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index b94683f7de9b..72064b5e4f36 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -2317,7 +2317,7 @@ static void atmel_poll_put_char(struct uart_port *port, unsigned char ch) } #endif -static struct uart_ops atmel_pops = { +static const struct uart_ops atmel_pops = { .tx_empty = atmel_tx_empty, .set_mctrl = atmel_set_mctrl, .get_mctrl = atmel_get_mctrl, -- cgit v1.2.3 From 03bd797f33ddbe1d14de7abc70a9dd21b2a12d3f Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Thu, 1 Sep 2016 19:51:32 +0200 Subject: serial: altera: constify uart_ops structures Check for uart_ops structures that are only stored in the ops field of a uart_port structure. This field is declared const, so uart_ops structures that have this property can be declared as const also. The semantic patch that makes this change is as follows: (http://coccinelle.lip6.fr/) // @r disable optional_qualifier@ identifier i; position p; @@ static struct uart_ops i@p = { ... }; @ok@ identifier r.i; struct uart_port e; position p; @@ e.ops = &i@p; @bad@ position p != {r.p,ok.p}; identifier r.i; struct uart_ops e; @@ e@i@p @depends on !bad disable optional_qualifier@ identifier r.i; @@ static +const struct uart_ops i = { ... }; // Signed-off-by: Julia Lawall Acked-by: Tobias Klauser Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/altera_jtaguart.c | 2 +- drivers/tty/serial/altera_uart.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/altera_jtaguart.c b/drivers/tty/serial/altera_jtaguart.c index 32df2a0cb060..e409d7dac7ab 100644 --- a/drivers/tty/serial/altera_jtaguart.c +++ b/drivers/tty/serial/altera_jtaguart.c @@ -280,7 +280,7 @@ static int altera_jtaguart_verify_port(struct uart_port *port, /* * Define the basic serial functions we support. */ -static struct uart_ops altera_jtaguart_ops = { +static const struct uart_ops altera_jtaguart_ops = { .tx_empty = altera_jtaguart_tx_empty, .get_mctrl = altera_jtaguart_get_mctrl, .set_mctrl = altera_jtaguart_set_mctrl, diff --git a/drivers/tty/serial/altera_uart.c b/drivers/tty/serial/altera_uart.c index 61b607f2488e..820a74208696 100644 --- a/drivers/tty/serial/altera_uart.c +++ b/drivers/tty/serial/altera_uart.c @@ -404,7 +404,7 @@ static void altera_uart_poll_put_char(struct uart_port *port, unsigned char c) /* * Define the basic serial functions we support. */ -static struct uart_ops altera_uart_ops = { +static const struct uart_ops altera_uart_ops = { .tx_empty = altera_uart_tx_empty, .get_mctrl = altera_uart_get_mctrl, .set_mctrl = altera_uart_set_mctrl, -- cgit v1.2.3 From 1f1d8703a098b17c12da840a23b4c6a36aa27c72 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Thu, 1 Sep 2016 19:51:33 +0200 Subject: serial/bcm63xx_uart: constify uart_ops structures Check for uart_ops structures that are only stored in the ops field of a uart_port structure. This field is declared const, so uart_ops structures that have this property can be declared as const also. The semantic patch that makes this change is as follows: (http://coccinelle.lip6.fr/) // @r disable optional_qualifier@ identifier i; position p; @@ static struct uart_ops i@p = { ... }; @ok@ identifier r.i; struct uart_port e; position p; @@ e.ops = &i@p; @bad@ position p != {r.p,ok.p}; identifier r.i; struct uart_ops e; @@ e@i@p @depends on !bad disable optional_qualifier@ identifier r.i; @@ static +const struct uart_ops i = { ... }; // Signed-off-by: Julia Lawall Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/bcm63xx_uart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/bcm63xx_uart.c b/drivers/tty/serial/bcm63xx_uart.c index 5108fab953aa..583c9a0c7ecc 100644 --- a/drivers/tty/serial/bcm63xx_uart.c +++ b/drivers/tty/serial/bcm63xx_uart.c @@ -631,7 +631,7 @@ static int bcm_uart_verify_port(struct uart_port *port, } /* serial core callbacks */ -static struct uart_ops bcm_uart_ops = { +static const struct uart_ops bcm_uart_ops = { .tx_empty = bcm_uart_tx_empty, .get_mctrl = bcm_uart_get_mctrl, .set_mctrl = bcm_uart_set_mctrl, -- cgit v1.2.3 From 5d9b9530e36bca7cd8306c5097623692e1134c39 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Thu, 1 Sep 2016 19:51:34 +0200 Subject: tty: serial: jsm_tty: constify uart_ops structures Check for uart_ops structures that are only stored in the ops field of a uart_port structure. This field is declared const, so uart_ops structures that have this property can be declared as const also. The semantic patch that makes this change is as follows: (http://coccinelle.lip6.fr/) // @r disable optional_qualifier@ identifier i; position p; @@ static struct uart_ops i@p = { ... }; @ok@ identifier r.i; struct uart_port e; position p; @@ e.ops = &i@p; @bad@ position p != {r.p,ok.p}; identifier r.i; struct uart_ops e; @@ e@i@p @depends on !bad disable optional_qualifier@ identifier r.i; @@ static +const struct uart_ops i = { ... }; // Signed-off-by: Julia Lawall Acked-by: Gabriel Krisman Bertazi Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/jsm/jsm_tty.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/jsm/jsm_tty.c b/drivers/tty/serial/jsm/jsm_tty.c index c5ddfe542451..ec7d8383900f 100644 --- a/drivers/tty/serial/jsm/jsm_tty.c +++ b/drivers/tty/serial/jsm/jsm_tty.c @@ -346,7 +346,7 @@ static void jsm_config_port(struct uart_port *port, int flags) port->type = PORT_JSM; } -static struct uart_ops jsm_ops = { +static const struct uart_ops jsm_ops = { .tx_empty = jsm_tty_tx_empty, .set_mctrl = jsm_tty_set_mctrl, .get_mctrl = jsm_tty_get_mctrl, -- cgit v1.2.3 From 069a47e5adfd5a1544c3c6d87a36889a691ea156 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Thu, 1 Sep 2016 19:51:35 +0200 Subject: tty: serial: constify uart_ops structures Check for uart_ops structures that are only stored in the ops field of a uart_port structure. This field is declared const, so uart_ops structures that have this property can be declared as const also. The semantic patch that makes this change is as follows: (http://coccinelle.lip6.fr/) // @r disable optional_qualifier@ identifier i; position p; @@ static struct uart_ops i@p = { ... }; @ok@ identifier r.i; struct uart_port e; position p; @@ e.ops = &i@p; @bad@ position p != {r.p,ok.p}; identifier r.i; struct uart_ops e; @@ e@i@p @depends on !bad disable optional_qualifier@ identifier r.i; @@ static +const struct uart_ops i = { ... }; // Signed-off-by: Julia Lawall Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/fsl_lpuart.c | 4 ++-- drivers/tty/serial/imx.c | 2 +- drivers/tty/serial/max3100.c | 2 +- drivers/tty/serial/men_z135_uart.c | 2 +- drivers/tty/serial/mxs-auart.c | 2 +- drivers/tty/serial/pch_uart.c | 2 +- drivers/tty/serial/sh-sci.c | 2 +- drivers/tty/serial/timbuart.c | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 93f172eaf026..b21f3e541714 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -1579,7 +1579,7 @@ static int lpuart_verify_port(struct uart_port *port, struct serial_struct *ser) return ret; } -static struct uart_ops lpuart_pops = { +static const struct uart_ops lpuart_pops = { .tx_empty = lpuart_tx_empty, .set_mctrl = lpuart_set_mctrl, .get_mctrl = lpuart_get_mctrl, @@ -1598,7 +1598,7 @@ static struct uart_ops lpuart_pops = { .flush_buffer = lpuart_flush_buffer, }; -static struct uart_ops lpuart32_pops = { +static const struct uart_ops lpuart32_pops = { .tx_empty = lpuart32_tx_empty, .set_mctrl = lpuart32_set_mctrl, .get_mctrl = lpuart32_get_mctrl, diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 08ccfe17640d..dc6d6e35488c 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -1737,7 +1737,7 @@ static int imx_rs485_config(struct uart_port *port, return 0; } -static struct uart_ops imx_pops = { +static const struct uart_ops imx_pops = { .tx_empty = imx_tx_empty, .set_mctrl = imx_set_mctrl, .get_mctrl = imx_get_mctrl, diff --git a/drivers/tty/serial/max3100.c b/drivers/tty/serial/max3100.c index 5c4c280b3207..ace82645b123 100644 --- a/drivers/tty/serial/max3100.c +++ b/drivers/tty/serial/max3100.c @@ -712,7 +712,7 @@ static void max3100_break_ctl(struct uart_port *port, int break_state) dev_dbg(&s->spi->dev, "%s\n", __func__); } -static struct uart_ops max3100_ops = { +static const struct uart_ops max3100_ops = { .tx_empty = max3100_tx_empty, .set_mctrl = max3100_set_mctrl, .get_mctrl = max3100_get_mctrl, diff --git a/drivers/tty/serial/men_z135_uart.c b/drivers/tty/serial/men_z135_uart.c index a44290e9b5a8..e72ea61c70db 100644 --- a/drivers/tty/serial/men_z135_uart.c +++ b/drivers/tty/serial/men_z135_uart.c @@ -775,7 +775,7 @@ static int men_z135_verify_port(struct uart_port *port, return -EINVAL; } -static struct uart_ops men_z135_ops = { +static const struct uart_ops men_z135_ops = { .tx_empty = men_z135_tx_empty, .set_mctrl = men_z135_set_mctrl, .get_mctrl = men_z135_get_mctrl, diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c index eb54e5c77ead..2f04ec2b5691 100644 --- a/drivers/tty/serial/mxs-auart.c +++ b/drivers/tty/serial/mxs-auart.c @@ -1317,7 +1317,7 @@ static void mxs_auart_break_ctl(struct uart_port *u, int ctl) mxs_clr(AUART_LINECTRL_BRK, s, REG_LINECTRL); } -static struct uart_ops mxs_auart_ops = { +static const struct uart_ops mxs_auart_ops = { .tx_empty = mxs_auart_tx_empty, .start_tx = mxs_auart_start_tx, .stop_tx = mxs_auart_stop_tx, diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c index ea4ffc2ebb2f..b5874d1e26b6 100644 --- a/drivers/tty/serial/pch_uart.c +++ b/drivers/tty/serial/pch_uart.c @@ -1603,7 +1603,7 @@ static void pch_uart_put_poll_char(struct uart_port *port, } #endif /* CONFIG_CONSOLE_POLL */ -static struct uart_ops pch_uart_ops = { +static const struct uart_ops pch_uart_ops = { .tx_empty = pch_uart_tx_empty, .set_mctrl = pch_uart_set_mctrl, .get_mctrl = pch_uart_get_mctrl, diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index d86eee38aae6..4b26252c2885 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -2533,7 +2533,7 @@ static int sci_verify_port(struct uart_port *port, struct serial_struct *ser) return 0; } -static struct uart_ops sci_uart_ops = { +static const struct uart_ops sci_uart_ops = { .tx_empty = sci_tx_empty, .set_mctrl = sci_set_mctrl, .get_mctrl = sci_get_mctrl, diff --git a/drivers/tty/serial/timbuart.c b/drivers/tty/serial/timbuart.c index 512c162634a3..5da7fe40e391 100644 --- a/drivers/tty/serial/timbuart.c +++ b/drivers/tty/serial/timbuart.c @@ -394,7 +394,7 @@ static int timbuart_verify_port(struct uart_port *port, return -EINVAL; } -static struct uart_ops timbuart_ops = { +static const struct uart_ops timbuart_ops = { .tx_empty = timbuart_tx_empty, .set_mctrl = timbuart_set_mctrl, .get_mctrl = timbuart_get_mctrl, -- cgit v1.2.3 From f098a0ae62f7eb7cef27bd83245d798bd45b428d Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Thu, 1 Sep 2016 19:51:36 +0200 Subject: tty: xuartps: constify uart_ops structures Check for uart_ops structures that are only stored in the ops field of a uart_port structure. This field is declared const, so uart_ops structures that have this property can be declared as const also. The semantic patch that makes this change is as follows: (http://coccinelle.lip6.fr/) // @r disable optional_qualifier@ identifier i; position p; @@ static struct uart_ops i@p = { ... }; @ok@ identifier r.i; struct uart_port e; position p; @@ e.ops = &i@p; @bad@ position p != {r.p,ok.p}; identifier r.i; struct uart_ops e; @@ e@i@p @depends on !bad disable optional_qualifier@ identifier r.i; @@ static +const struct uart_ops i = { ... }; // Signed-off-by: Julia Lawall Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/xilinx_uartps.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index 9ca1a4d1b66a..11a2b36e14bb 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -993,7 +993,7 @@ static void cdns_uart_pm(struct uart_port *port, unsigned int state, } } -static struct uart_ops cdns_uart_ops = { +static const struct uart_ops cdns_uart_ops = { .set_mctrl = cdns_uart_set_mctrl, .get_mctrl = cdns_uart_get_mctrl, .start_tx = cdns_uart_start_tx, -- cgit v1.2.3 From 31d054dccc2cd840fb1e17e97021b92b3402c701 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Thu, 1 Sep 2016 19:51:37 +0200 Subject: serial-uartlite: constify uart_ops structures Check for uart_ops structures that are only stored in the ops field of a uart_port structure. This field is declared const, so uart_ops structures that have this property can be declared as const also. The semantic patch that makes this change is as follows: (http://coccinelle.lip6.fr/) // @r disable optional_qualifier@ identifier i; position p; @@ static struct uart_ops i@p = { ... }; @ok@ identifier r.i; struct uart_port e; position p; @@ e.ops = &i@p; @bad@ position p != {r.p,ok.p}; identifier r.i; struct uart_ops e; @@ e@i@p @depends on !bad disable optional_qualifier@ identifier r.i; @@ static +const struct uart_ops i = { ... }; // Signed-off-by: Julia Lawall Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/uartlite.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/uartlite.c b/drivers/tty/serial/uartlite.c index 05089b6c2f30..817bb0d3f326 100644 --- a/drivers/tty/serial/uartlite.c +++ b/drivers/tty/serial/uartlite.c @@ -387,7 +387,7 @@ static void ulite_put_poll_char(struct uart_port *port, unsigned char ch) } #endif -static struct uart_ops ulite_ops = { +static const struct uart_ops ulite_ops = { .tx_empty = ulite_tx_empty, .set_mctrl = ulite_set_mctrl, .get_mctrl = ulite_get_mctrl, -- cgit v1.2.3 From 1c06bde643d0a0b17f117539e961300cad69aad3 Mon Sep 17 00:00:00 2001 From: Martyn Welch Date: Thu, 1 Sep 2016 11:30:46 +0200 Subject: Allowing UART DMA to be configured on i.MX53 The UART DMA was only being configured on i.MX6Q compatible devices. We know that the DMA also works for i.MX53 devices, so enable uart DMA for imx53 and let the device tree to configure if DMA should be used or not. Signed-off-by: Martyn Welch Signed-off-by: Fabien Lahoudere Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/imx.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index dc6d6e35488c..782b0e524e14 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -190,6 +190,7 @@ enum imx_uart_type { IMX1_UART, IMX21_UART, + IMX53_UART, IMX6Q_UART, }; @@ -247,6 +248,10 @@ static struct imx_uart_data imx_uart_devdata[] = { .uts_reg = IMX21_UTS, .devtype = IMX21_UART, }, + [IMX53_UART] = { + .uts_reg = IMX21_UTS, + .devtype = IMX53_UART, + }, [IMX6Q_UART] = { .uts_reg = IMX21_UTS, .devtype = IMX6Q_UART, @@ -260,6 +265,9 @@ static const struct platform_device_id imx_uart_devtype[] = { }, { .name = "imx21-uart", .driver_data = (kernel_ulong_t) &imx_uart_devdata[IMX21_UART], + }, { + .name = "imx53-uart", + .driver_data = (kernel_ulong_t) &imx_uart_devdata[IMX53_UART], }, { .name = "imx6q-uart", .driver_data = (kernel_ulong_t) &imx_uart_devdata[IMX6Q_UART], @@ -271,6 +279,7 @@ MODULE_DEVICE_TABLE(platform, imx_uart_devtype); static const struct of_device_id imx_uart_dt_ids[] = { { .compatible = "fsl,imx6q-uart", .data = &imx_uart_devdata[IMX6Q_UART], }, + { .compatible = "fsl,imx53-uart", .data = &imx_uart_devdata[IMX53_UART], }, { .compatible = "fsl,imx1-uart", .data = &imx_uart_devdata[IMX1_UART], }, { .compatible = "fsl,imx21-uart", .data = &imx_uart_devdata[IMX21_UART], }, { /* sentinel */ } @@ -292,6 +301,11 @@ static inline int is_imx21_uart(struct imx_port *sport) return sport->devdata->devtype == IMX21_UART; } +static inline int is_imx53_uart(struct imx_port *sport) +{ + return sport->devdata->devtype == IMX53_UART; +} + static inline int is_imx6q_uart(struct imx_port *sport) { return sport->devdata->devtype == IMX6Q_UART; @@ -1254,8 +1268,7 @@ static int imx_startup(struct uart_port *port) writel(temp & ~UCR4_DREN, sport->port.membase + UCR4); /* Can we enable the DMA support? */ - if (is_imx6q_uart(sport) && !uart_console(port) && - !sport->dma_is_inited) + if (!uart_console(port) && !sport->dma_is_inited) imx_uart_dma_init(sport); spin_lock_irqsave(&sport->port.lock, flags); -- cgit v1.2.3 From b3965767d86cf4534dfe1affbde0453d3224ed7f Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 31 Aug 2016 19:46:55 +0300 Subject: serial: 8250_port: fix runtime PM use in __do_stop_tx_rs485() There are calls to serial8250_rpm_{get|put}() in __do_stop_tx_rs485() that are certainly placed in a wrong location. I dunno how it had been tested with runtime PM enabled because it is obvious "sleep in atomic context" error. Besides that serial8250_rpm_get() is called immediately after an IO just happened. It implies that the device is already powered on, see implementation of serial8250_em485_rts_after_send() and serial8250_clear_fifos() for the details. There is no bug have been seen due to, as I can guess, use of auto suspend mode when scheduled transaction to suspend is invoked quite lately than it's needed for a few writes to the port. It might be possible to trigger a warning if stop_tx_timer fires when device is suspended. Refactor the code to use runtime PM only in case of timer function. Fixes: 0c66940d584d ("tty/serial/8250: fix RS485 half-duplex RX") Cc: "Matwey V. Kornilov" Tested-by: Yegor Yefremov Signed-off-by: Andy Shevchenko Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_port.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index 1bd7ea0aff03..a8ecc253ef66 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -1414,12 +1414,8 @@ static void __do_stop_tx_rs485(struct uart_8250_port *p) if (!(p->port.rs485.flags & SER_RS485_RX_DURING_TX)) { serial8250_clear_fifos(p); - serial8250_rpm_get(p); - p->ier |= UART_IER_RLSI | UART_IER_RDI; serial_port_out(&p->port, UART_IER, p->ier); - - serial8250_rpm_put(p); } } @@ -1429,6 +1425,7 @@ static void serial8250_em485_handle_stop_tx(unsigned long arg) struct uart_8250_em485 *em485 = p->em485; unsigned long flags; + serial8250_rpm_get(p); spin_lock_irqsave(&p->port.lock, flags); if (em485 && em485->active_timer == &em485->stop_tx_timer) { @@ -1436,6 +1433,7 @@ static void serial8250_em485_handle_stop_tx(unsigned long arg) em485->active_timer = NULL; } spin_unlock_irqrestore(&p->port.lock, flags); + serial8250_rpm_put(p); } static void __stop_tx_rs485(struct uart_8250_port *p) @@ -1475,7 +1473,7 @@ static inline void __stop_tx(struct uart_8250_port *p) unsigned char lsr = serial_in(p, UART_LSR); /* * To provide required timeing and allow FIFO transfer, - * __stop_tx_rs485 must be called only when both FIFO and + * __stop_tx_rs485() must be called only when both FIFO and * shift register are empty. It is for device driver to enable * interrupt on TEMT. */ @@ -1484,9 +1482,10 @@ static inline void __stop_tx(struct uart_8250_port *p) del_timer(&em485->start_tx_timer); em485->active_timer = NULL; + + __stop_tx_rs485(p); } __do_stop_tx(p); - __stop_tx_rs485(p); } static void serial8250_stop_tx(struct uart_port *port) -- cgit v1.2.3 From f3bf26326f1cd27b498b44618855512b2f19769b Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 31 Aug 2016 19:46:56 +0300 Subject: serial: 8250_port: unify check of em485 variable Unify the check of em485 variable to be either (em485) or (!em485) instead of the explicit comparison to NULL. While here, remove redundant check in __do_stop_tx_rs485() and __stop_tx_rs485() since the functions ain't called with NULL value of em485 variable. Cc: "Matwey V. Kornilov" Tested-by: Yegor Yefremov Signed-off-by: Andy Shevchenko Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_port.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index a8ecc253ef66..aa418011381a 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -585,11 +585,11 @@ EXPORT_SYMBOL_GPL(serial8250_rpm_put); */ int serial8250_em485_init(struct uart_8250_port *p) { - if (p->em485 != NULL) + if (p->em485) return 0; p->em485 = kmalloc(sizeof(struct uart_8250_em485), GFP_ATOMIC); - if (p->em485 == NULL) + if (!p->em485) return -ENOMEM; setup_timer(&p->em485->stop_tx_timer, @@ -619,7 +619,7 @@ EXPORT_SYMBOL_GPL(serial8250_em485_init); */ void serial8250_em485_destroy(struct uart_8250_port *p) { - if (p->em485 == NULL) + if (!p->em485) return; del_timer(&p->em485->start_tx_timer); @@ -1402,10 +1402,8 @@ static void serial8250_stop_rx(struct uart_port *port) static void __do_stop_tx_rs485(struct uart_8250_port *p) { - if (!p->em485) - return; - serial8250_em485_rts_after_send(p); + /* * Empty the RX FIFO, we are not interested in anything * received during the half-duplex transmission. @@ -1440,9 +1438,6 @@ static void __stop_tx_rs485(struct uart_8250_port *p) { struct uart_8250_em485 *em485 = p->em485; - if (!em485) - return; - /* * __do_stop_tx_rs485 is going to set RTS according to config * AND flush RX FIFO if required. -- cgit v1.2.3 From e06b6b854163a7f9931d6e0d859b9378a23fe7af Mon Sep 17 00:00:00 2001 From: Kefeng Wang Date: Wed, 31 Aug 2016 11:29:12 +0800 Subject: serial: 8250_dw: add ACPI support for uart on Hisilicon Hip05 SoC Add ACPI identifier for UART on Hisilicon Hip05 SoC, be careful that it is not 16550 compatible, and "reg-io-width" and "reg-shift" need be set properly by _DSD method in DSDT. Signed-off-by: Kefeng Wang Acked-by: Heikki Krogerus Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_dw.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index 47e6d08eb6fd..5e4b80ecb883 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -620,6 +620,7 @@ static const struct acpi_device_id dw8250_acpi_match[] = { { "APMC0D08", 0}, { "AMD0020", 0 }, { "AMDI0020", 0 }, + { "HISI0031", 0 }, { }, }; MODULE_DEVICE_TABLE(acpi, dw8250_acpi_match); -- cgit v1.2.3 From 8f975fe49dba5d15a828f34ad8ec3dc8beb60ca0 Mon Sep 17 00:00:00 2001 From: Baoyou Xie Date: Thu, 1 Sep 2016 19:43:31 +0800 Subject: tty/serial: mark __init early_smh_setup() static We get 1 warning when building kernel with W=1: drivers/tty/serial/earlycon-arm-semihost.c:56:12: warning: no previous prototype for 'early_smh_setup' [-Wmissing-prototypes] In fact, this function is only used in the file in which it is declared and don't need a declaration, but can be made static. so this patch marks this function with 'static'. Signed-off-by: Baoyou Xie Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/earlycon-arm-semihost.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/earlycon-arm-semihost.c b/drivers/tty/serial/earlycon-arm-semihost.c index 383db10fbb49..6bbeb699777c 100644 --- a/drivers/tty/serial/earlycon-arm-semihost.c +++ b/drivers/tty/serial/earlycon-arm-semihost.c @@ -53,7 +53,8 @@ static void smh_write(struct console *con, const char *s, unsigned n) uart_console_write(&dev->port, s, n, smh_putc); } -int __init early_smh_setup(struct earlycon_device *device, const char *opt) +static int +__init early_smh_setup(struct earlycon_device *device, const char *opt) { device->con->write = smh_write; return 0; -- cgit v1.2.3 From 46e36683f433528bfb7e5754ca5c5c86c204c40a Mon Sep 17 00:00:00 2001 From: Alexander Sverdlin Date: Fri, 2 Sep 2016 13:20:21 +0200 Subject: serial: earlycon: Extend earlycon command line option to support 64-bit addresses earlycon implementation used "unsigned long" internally, but there are systems (ARM with LPAE) where sizeof(unsigned long) == 4 and uart is mapped beyond 4GiB address range. Switch to resource_size_t internally and replace obsoleted simple_strtoul() with kstrtoull(). Signed-off-by: Alexander Sverdlin Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_core.c | 2 +- drivers/tty/serial/earlycon.c | 7 +++---- drivers/tty/serial/serial_core.c | 12 +++++++++--- include/linux/serial_core.h | 2 +- 4 files changed, 14 insertions(+), 9 deletions(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 13ad5c3d2e68..f64d6cd311ed 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -639,7 +639,7 @@ static int univ8250_console_match(struct console *co, char *name, int idx, { char match[] = "uart"; /* 8250-specific earlycon name */ unsigned char iotype; - unsigned long addr; + resource_size_t addr; int i; if (strncmp(name, match, 4) != 0) diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c index 067783f0523c..3940280afa9c 100644 --- a/drivers/tty/serial/earlycon.c +++ b/drivers/tty/serial/earlycon.c @@ -38,7 +38,7 @@ static struct earlycon_device early_console_dev = { .con = &early_con, }; -static void __iomem * __init earlycon_map(unsigned long paddr, size_t size) +static void __iomem * __init earlycon_map(resource_size_t paddr, size_t size) { void __iomem *base; #ifdef CONFIG_FIX_EARLYCON_MEM @@ -49,8 +49,7 @@ static void __iomem * __init earlycon_map(unsigned long paddr, size_t size) base = ioremap(paddr, size); #endif if (!base) - pr_err("%s: Couldn't map 0x%llx\n", __func__, - (unsigned long long)paddr); + pr_err("%s: Couldn't map %pa\n", __func__, &paddr); return base; } @@ -92,7 +91,7 @@ static int __init parse_options(struct earlycon_device *device, char *options) { struct uart_port *port = &device->port; int length; - unsigned long addr; + resource_size_t addr; if (uart_parse_earlycon(options, &port->iotype, &addr, &options)) return -EINVAL; diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index e183d2eff16d..240d3e7a548c 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -1892,11 +1892,14 @@ uart_get_console(struct uart_port *ports, int nr, struct console *co) * console=,0x, * is also accepted; the returned @iotype will be UPIO_MEM. * - * Returns 0 on success or -EINVAL on failure + * Returns 0 on success, -EINVAL or -ERANGE on failure */ -int uart_parse_earlycon(char *p, unsigned char *iotype, unsigned long *addr, +int uart_parse_earlycon(char *p, unsigned char *iotype, resource_size_t *addr, char **options) { + int ret; + unsigned long long tmp; + if (strncmp(p, "mmio,", 5) == 0) { *iotype = UPIO_MEM; p += 5; @@ -1922,7 +1925,10 @@ int uart_parse_earlycon(char *p, unsigned char *iotype, unsigned long *addr, return -EINVAL; } - *addr = simple_strtoul(p, NULL, 0); + ret = kstrtoull(p, 0, &tmp); + if (ret) + return ret; + *addr = tmp; p = strchr(p, ','); if (p) p++; diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 2f44e2013654..cdba6f144f72 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -374,7 +374,7 @@ extern int of_setup_earlycon(const struct earlycon_id *match, struct uart_port *uart_get_console(struct uart_port *ports, int nr, struct console *c); -int uart_parse_earlycon(char *p, unsigned char *iotype, unsigned long *addr, +int uart_parse_earlycon(char *p, unsigned char *iotype, resource_size_t *addr, char **options); void uart_parse_options(char *options, int *baud, int *parity, int *bits, int *flow); -- cgit v1.2.3 From d2f5a7311bcaed681a41cb3419b8fe92a7b68bf5 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 23 Aug 2016 16:09:40 +0300 Subject: dmaengine: hsu: refactor hsu_dma_do_irq() to return int Since we have nice macro IRQ_RETVAL() we would use it to convert a flag of handled interrupt from int to irqreturn_t. The rationale of doing this is: a) hence we implicitly mark hsu_dma_do_irq() as an auxiliary function that can't be used as interrupt handler directly, and b) to be in align with serial driver which is using serial8250_handle_irq() that returns plain int by design. Signed-off-by: Andy Shevchenko Signed-off-by: Greg Kroah-Hartman --- drivers/dma/hsu/hsu.c | 9 ++++----- drivers/dma/hsu/pci.c | 6 +++--- drivers/tty/serial/8250/8250_mid.c | 8 ++++---- include/linux/dma/hsu.h | 9 ++++----- 4 files changed, 15 insertions(+), 17 deletions(-) (limited to 'drivers/tty') diff --git a/drivers/dma/hsu/hsu.c b/drivers/dma/hsu/hsu.c index c5f21efd6090..29d04ca71d52 100644 --- a/drivers/dma/hsu/hsu.c +++ b/drivers/dma/hsu/hsu.c @@ -200,10 +200,9 @@ EXPORT_SYMBOL_GPL(hsu_dma_get_status); * is not a normal timeout interrupt, ie. hsu_dma_get_status() returned 0. * * Return: - * IRQ_NONE for invalid channel number, IRQ_HANDLED otherwise. + * 0 for invalid channel number, 1 otherwise. */ -irqreturn_t hsu_dma_do_irq(struct hsu_dma_chip *chip, unsigned short nr, - u32 status) +int hsu_dma_do_irq(struct hsu_dma_chip *chip, unsigned short nr, u32 status) { struct hsu_dma_chan *hsuc; struct hsu_dma_desc *desc; @@ -211,7 +210,7 @@ irqreturn_t hsu_dma_do_irq(struct hsu_dma_chip *chip, unsigned short nr, /* Sanity check */ if (nr >= chip->hsu->nr_channels) - return IRQ_NONE; + return 0; hsuc = &chip->hsu->chan[nr]; @@ -230,7 +229,7 @@ irqreturn_t hsu_dma_do_irq(struct hsu_dma_chip *chip, unsigned short nr, } spin_unlock_irqrestore(&hsuc->vchan.lock, flags); - return IRQ_HANDLED; + return 1; } EXPORT_SYMBOL_GPL(hsu_dma_do_irq); diff --git a/drivers/dma/hsu/pci.c b/drivers/dma/hsu/pci.c index 9916058531d9..b51639f045ed 100644 --- a/drivers/dma/hsu/pci.c +++ b/drivers/dma/hsu/pci.c @@ -29,7 +29,7 @@ static irqreturn_t hsu_pci_irq(int irq, void *dev) u32 dmaisr; u32 status; unsigned short i; - irqreturn_t ret = IRQ_NONE; + int ret = 0; int err; dmaisr = readl(chip->regs + HSU_PCI_DMAISR); @@ -37,14 +37,14 @@ static irqreturn_t hsu_pci_irq(int irq, void *dev) if (dmaisr & 0x1) { err = hsu_dma_get_status(chip, i, &status); if (err > 0) - ret |= IRQ_HANDLED; + ret |= 1; else if (err == 0) ret |= hsu_dma_do_irq(chip, i, status); } dmaisr >>= 1; } - return ret; + return IRQ_RETVAL(ret); } static int hsu_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) diff --git a/drivers/tty/serial/8250/8250_mid.c b/drivers/tty/serial/8250/8250_mid.c index 339de9cd0866..121a7f2d4697 100644 --- a/drivers/tty/serial/8250/8250_mid.c +++ b/drivers/tty/serial/8250/8250_mid.c @@ -99,27 +99,27 @@ static int dnv_handle_irq(struct uart_port *p) struct uart_8250_port *up = up_to_u8250p(p); unsigned int fisr = serial_port_in(p, INTEL_MID_UART_DNV_FISR); u32 status; - int ret = IRQ_NONE; + int ret = 0; int err; if (fisr & BIT(2)) { err = hsu_dma_get_status(&mid->dma_chip, 1, &status); if (err > 0) { serial8250_rx_dma_flush(up); - ret |= IRQ_HANDLED; + ret |= 1; } else if (err == 0) ret |= hsu_dma_do_irq(&mid->dma_chip, 1, status); } if (fisr & BIT(1)) { err = hsu_dma_get_status(&mid->dma_chip, 0, &status); if (err > 0) - ret |= IRQ_HANDLED; + ret |= 1; else if (err == 0) ret |= hsu_dma_do_irq(&mid->dma_chip, 0, status); } if (fisr & BIT(0)) ret |= serial8250_handle_irq(p, serial_port_in(p, UART_IIR)); - return ret; + return IRQ_RETVAL(ret); } #define DNV_DMA_CHAN_OFFSET 0x80 diff --git a/include/linux/dma/hsu.h b/include/linux/dma/hsu.h index aaff68efba5d..197eec63e501 100644 --- a/include/linux/dma/hsu.h +++ b/include/linux/dma/hsu.h @@ -41,8 +41,7 @@ struct hsu_dma_chip { /* Export to the internal users */ int hsu_dma_get_status(struct hsu_dma_chip *chip, unsigned short nr, u32 *status); -irqreturn_t hsu_dma_do_irq(struct hsu_dma_chip *chip, unsigned short nr, - u32 status); +int hsu_dma_do_irq(struct hsu_dma_chip *chip, unsigned short nr, u32 status); /* Export to the platform drivers */ int hsu_dma_probe(struct hsu_dma_chip *chip); @@ -53,10 +52,10 @@ static inline int hsu_dma_get_status(struct hsu_dma_chip *chip, { return 0; } -static inline irqreturn_t hsu_dma_do_irq(struct hsu_dma_chip *chip, - unsigned short nr, u32 status) +static inline int hsu_dma_do_irq(struct hsu_dma_chip *chip, unsigned short nr, + u32 status) { - return IRQ_NONE; + return 0; } static inline int hsu_dma_probe(struct hsu_dma_chip *chip) { return -ENODEV; } static inline int hsu_dma_remove(struct hsu_dma_chip *chip) { return 0; } -- cgit v1.2.3 From 1e512d45332bb3a2e97ca262101d43adca274bb2 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 8 Sep 2016 14:27:53 +0200 Subject: serial: imx: add error messages when .probe fails Some functions called by serial_imx_probe emit an error message themself (like kmalloc() and friends). clk_prepare_enable() and devm_request_irq() however don't which might make the driver silently fail to probe. So add an error message for these. Signed-off-by: Uwe Kleine-König Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/imx.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 782b0e524e14..5240f9c080c3 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -2137,8 +2137,10 @@ static int serial_imx_probe(struct platform_device *pdev) /* For register access, we only need to enable the ipg clock. */ ret = clk_prepare_enable(sport->clk_ipg); - if (ret) + if (ret) { + dev_err(&pdev->dev, "failed to enable per clk: %d\n", ret); return ret; + } /* Disable interrupts before requesting them */ reg = readl_relaxed(sport->port.membase + UCR1); @@ -2155,18 +2157,26 @@ static int serial_imx_probe(struct platform_device *pdev) if (txirq > 0) { ret = devm_request_irq(&pdev->dev, rxirq, imx_rxint, 0, dev_name(&pdev->dev), sport); - if (ret) + if (ret) { + dev_err(&pdev->dev, "failed to request rx irq: %d\n", + ret); return ret; + } ret = devm_request_irq(&pdev->dev, txirq, imx_txint, 0, dev_name(&pdev->dev), sport); - if (ret) + if (ret) { + dev_err(&pdev->dev, "failed to request tx irq: %d\n", + ret); return ret; + } } else { ret = devm_request_irq(&pdev->dev, rxirq, imx_int, 0, dev_name(&pdev->dev), sport); - if (ret) + if (ret) { + dev_err(&pdev->dev, "failed to request irq: %d\n", ret); return ret; + } } imx_ports[sport->port.line] = sport; -- cgit v1.2.3 From 8b2303de399f66b0da2c7e5bcc8296be574766f1 Mon Sep 17 00:00:00 2001 From: Alexander Sverdlin Date: Mon, 12 Sep 2016 13:29:29 +0200 Subject: serial: core: Fix handling of options after MMIO address Guenter Roeck reported a regression caused by commit "serial: earlycon: Extend earlycon command line option to support 64-bit addresses": console= and earlycon= options have the following format: ...,, Historically used here simple_strtoul() had no problems with comma, but the new and recommended kstrtoull() requires null-terminated string and returns -EINVAL in case there are "options" at the end. There is no recommended to use function currently that will support it, so stick to obsolete simple_strtoull() variant. Signed-off-by: Alexander Sverdlin Reported-by: Guenter Roeck Reviewed-by: Guenter Roeck Tested-by: Guenter Roeck Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_core.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 240d3e7a548c..6b7f857fc3b0 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -1892,14 +1892,11 @@ uart_get_console(struct uart_port *ports, int nr, struct console *co) * console=,0x, * is also accepted; the returned @iotype will be UPIO_MEM. * - * Returns 0 on success, -EINVAL or -ERANGE on failure + * Returns 0 on success or -EINVAL on failure */ int uart_parse_earlycon(char *p, unsigned char *iotype, resource_size_t *addr, char **options) { - int ret; - unsigned long long tmp; - if (strncmp(p, "mmio,", 5) == 0) { *iotype = UPIO_MEM; p += 5; @@ -1925,10 +1922,11 @@ int uart_parse_earlycon(char *p, unsigned char *iotype, resource_size_t *addr, return -EINVAL; } - ret = kstrtoull(p, 0, &tmp); - if (ret) - return ret; - *addr = tmp; + /* + * Before you replace it with kstrtoull(), think about options separator + * (',') it will not tolerate + */ + *addr = simple_strtoull(p, NULL, 0); p = strchr(p, ','); if (p) p++; -- cgit v1.2.3 From 5d7519dfc963ec8b6a10a51356840580fc005a1e Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Fri, 9 Sep 2016 08:31:33 -0300 Subject: serial: mxs-auart: Disable clock on error path We should disable the previously acquired clock when enabling s->clk fails. Signed-off-by: Fabio Estevam Reviewed-by: Uwe Kleine-König Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/mxs-auart.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c index 2f04ec2b5691..680270b85ff7 100644 --- a/drivers/tty/serial/mxs-auart.c +++ b/drivers/tty/serial/mxs-auart.c @@ -1543,10 +1543,14 @@ static int mxs_get_clks(struct mxs_auart_port *s, err = clk_prepare_enable(s->clk); if (err) { dev_err(s->dev, "Failed to enable clk!\n"); - return err; + goto disable_clk_ahb; } return 0; + +disable_clk_ahb: + clk_disable_unprepare(s->clk_ahb); + return err; } /* -- cgit v1.2.3 From 0d2665b6bcfd6b7df828582a0b8a12d29691b66d Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Sat, 10 Sep 2016 12:22:17 +0000 Subject: serial: mxs-auart: Use PTR_ERR_OR_ZERO() to simplify the code Use PTR_ERR_OR_ZERO rather than if(IS_ERR(...)) + PTR_ERR. Generated by: scripts/coccinelle/api/ptr_ret.cocci Signed-off-by: Wei Yongjun Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/mxs-auart.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c index 680270b85ff7..ec0ab33efd9d 100644 --- a/drivers/tty/serial/mxs-auart.c +++ b/drivers/tty/serial/mxs-auart.c @@ -1510,10 +1510,7 @@ static int mxs_get_clks(struct mxs_auart_port *s, if (!is_asm9260_auart(s)) { s->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(s->clk)) - return PTR_ERR(s->clk); - - return 0; + return PTR_ERR_OR_ZERO(s->clk); } s->clk = devm_clk_get(s->dev, "mod"); -- cgit v1.2.3 From c164b008b62720fda072b55c378d32da3260f271 Mon Sep 17 00:00:00 2001 From: Liu Xiang Date: Wed, 7 Sep 2016 22:05:01 +0800 Subject: serial: max310x: Set IRQF_TRIGGER_FALLING flag when dev.of_node is not NULL When dev.of_node is not NULL, we also need to set IRQF_TRIGGER_FALLING flag, otherwise it may cause uncertain interrupts. Signed-off-by: Liu Xiang Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/max310x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index 9360801df3c4..8a3e92638e10 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -1329,9 +1329,9 @@ static int max310x_spi_probe(struct spi_device *spi) const struct spi_device_id *id_entry = spi_get_device_id(spi); devtype = (struct max310x_devtype *)id_entry->driver_data; - flags = IRQF_TRIGGER_FALLING; } + flags = IRQF_TRIGGER_FALLING; regcfg.max_register = devtype->nr * 0x20 - 1; regmap = devm_regmap_init_spi(spi, ®cfg); -- cgit v1.2.3 From 33ddca08bf9d68086e3e9ad6bef06ce881777894 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Thu, 8 Sep 2016 15:03:24 +0000 Subject: tty: serial: fsl_lpuart: use GFP_ATOMIC under spin lock The function lpuart_start_rx_dma() is called from several places, in some of which, such as lpuart_startup(), a lock be held here, so we should use GFP_ATOMIC when a lock is held. Fixes: 5887ad43ee02 ("tty: serial: fsl_lpuart: Use cyclic DMA for Rx") Signed-off-by: Wei Yongjun Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/fsl_lpuart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index b21f3e541714..de9d5107c00a 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -852,7 +852,7 @@ static inline int lpuart_start_rx_dma(struct lpuart_port *sport) if (sport->rx_dma_rng_buf_len < 16) sport->rx_dma_rng_buf_len = 16; - ring->buf = kmalloc(sport->rx_dma_rng_buf_len, GFP_KERNEL); + ring->buf = kmalloc(sport->rx_dma_rng_buf_len, GFP_ATOMIC); if (!ring->buf) { dev_err(sport->port.dev, "Ring buf alloc failed\n"); return -ENOMEM; -- cgit v1.2.3 From 0ae9fdefb6c37237d826d6195e27810ffcc0b6e0 Mon Sep 17 00:00:00 2001 From: Richard Genoud Date: Mon, 12 Sep 2016 15:34:41 +0200 Subject: BUG: atmel_serial: Interrupts not disabled on close Since commit 18dfef9c7f87 ("serial: atmel: convert to irq handling provided mctrl-gpio"), interrupts from GPIOs are not disabled any more when the serial port is closed, leading to an oops when the one of the input pin is toggled (CTS/DSR/DCD/RNG). This is only the case if those pins are used as GPIOs, i.e. declared like that: usart1: serial@f8020000 { /* CTS and DTS will be handled by GPIO */ status = "okay"; rts-gpios = <&pioB 17 GPIO_ACTIVE_LOW>; cts-gpios = <&pioB 16 GPIO_ACTIVE_LOW>; dtr-gpios = <&pioB 14 GPIO_ACTIVE_LOW>; dsr-gpios = <&pioC 31 GPIO_ACTIVE_LOW>; rng-gpios = <&pioB 12 GPIO_ACTIVE_LOW>; dcd-gpios = <&pioB 15 GPIO_ACTIVE_LOW>; }; That's because modem interrupts used to be freed in atmel_shutdown(). After commit 18dfef9c7f87 ("serial: atmel: convert to irq handling provided mctrl-gpio"), this code was just removed. Calling atmel_disable_ms() disables the interrupts and everything works fine again. Tested on at91sam9g35-cm (This patch doesn't apply on -stable kernels, fixes for 4.4 and 4.7 will be sent after this one is applied.) Signed-off-by: Richard Genoud Fixes: 18dfef9c7f87 ("serial: atmel: convert to irq handling provided mctrl-gpio") Acked-by: Nicolas Ferre Acked-by: Uwe Kleine-König Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/atmel_serial.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index 72064b5e4f36..5f550d9feed9 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -1937,6 +1937,9 @@ static void atmel_shutdown(struct uart_port *port) { struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + /* Disable modem control lines interrupts */ + atmel_disable_ms(port); + /* Disable interrupts at device level */ atmel_uart_writel(port, ATMEL_US_IDR, -1); @@ -1987,8 +1990,6 @@ static void atmel_shutdown(struct uart_port *port) */ free_irq(port->irq, port); - atmel_port->ms_irq_enabled = false; - atmel_flush_buffer(port); } -- cgit v1.2.3 From a5a2b13074fd8d290bad32a5b0a0c5be9f3a84fa Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 13 Sep 2016 00:23:42 +0300 Subject: serial: core: fix potential NULL pointer dereference The commit 761ed4a94582 ("tty: serial_core: convert uart_close to use tty_port_close") refactored uart_close() to use tty_port_close(). At the same time it introduced a potential NULL pointer dereference. Rearrange the code to avoid kernel crash. Fixes: 761ed4a94582 ("tty: serial_core: convert uart_close to use tty_port_close") Cc: Rob Herring Signed-off-by: Andy Shevchenko Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 6b7f857fc3b0..6e4f63627479 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -1484,15 +1484,15 @@ static void uart_tty_port_shutdown(struct tty_port *port) struct uart_state *state = container_of(port, struct uart_state, port); struct uart_port *uport = uart_port_check(state); - spin_lock_irq(&uport->lock); /* * At this point, we stop accepting input. To do this, we * disable the receive line status interrupts. */ - WARN(!uport, "detached port still initialized!\n"); + if (WARN(!uport, "detached port still initialized!\n")) + return; + spin_lock_irq(&uport->lock); uport->ops->stop_rx(uport); - spin_unlock_irq(&uport->lock); uart_port_shutdown(port); -- cgit v1.2.3 From e5e8960236dbff4b49eac1ec7f47b1c912531b62 Mon Sep 17 00:00:00 2001 From: Fabien Lahoudere Date: Tue, 13 Sep 2016 10:17:05 +0200 Subject: serial: imx: Replace dmaengine old API dmaengine_terminate_all() is deprecated and should be replaced by dmaengine_terminate_sync() in non-atomic context or dmaengine_terminate_async() with dmaengine_synchronize(). See commit b36f09c3c441 ("dmaengine: Add transfer termination synchronization support") Signed-off-by: Fabien Lahoudere Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/imx.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 5240f9c080c3..a1d8174a098b 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -1122,7 +1122,7 @@ static void imx_setup_ufcr(struct imx_port *sport, static void imx_uart_dma_exit(struct imx_port *sport) { if (sport->dma_chan_rx) { - dmaengine_terminate_all(sport->dma_chan_rx); + dmaengine_terminate_sync(sport->dma_chan_rx); dma_release_channel(sport->dma_chan_rx); sport->dma_chan_rx = NULL; sport->rx_cookie = -EINVAL; @@ -1131,7 +1131,7 @@ static void imx_uart_dma_exit(struct imx_port *sport) } if (sport->dma_chan_tx) { - dmaengine_terminate_all(sport->dma_chan_tx); + dmaengine_terminate_sync(sport->dma_chan_tx); dma_release_channel(sport->dma_chan_tx); sport->dma_chan_tx = NULL; } @@ -1351,8 +1351,8 @@ static void imx_shutdown(struct uart_port *port) if (sport->dma_is_enabled) { sport->dma_is_rxing = 0; sport->dma_is_txing = 0; - dmaengine_terminate_all(sport->dma_chan_tx); - dmaengine_terminate_all(sport->dma_chan_rx); + dmaengine_terminate_sync(sport->dma_chan_tx); + dmaengine_terminate_sync(sport->dma_chan_rx); spin_lock_irqsave(&sport->port.lock, flags); imx_stop_tx(port); -- cgit v1.2.3 From d2ec3f77de8e67b7a3dab3ec827467e0fd797c86 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Thu, 8 Sep 2016 15:35:59 -0700 Subject: pty: make ptmx file ops read-only after init The ptmx_fops structure is only changed during init, so mark it as such. Signed-off-by: Kees Cook Signed-off-by: Greg Kroah-Hartman --- drivers/tty/pty.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/tty') diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index 51e0d32883ba..a23fa5ed1d67 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -800,7 +800,7 @@ out_free_file: return retval; } -static struct file_operations ptmx_fops; +static struct file_operations ptmx_fops __ro_after_init; static void __init unix98_pty_init(void) { -- cgit v1.2.3 From 669e0a51b1b50052b1615683cde64e1c28ae895f Mon Sep 17 00:00:00 2001 From: Adam Borowski Date: Thu, 15 Sep 2016 16:47:09 +0200 Subject: vt: Fix a read-past-array in vc_t416_color(). This makes it show up on UBSAN: perl -e 'for (0..15) {my @x=("0")x$_;push @x,qw(38 2 64 128 192 4);printf "\e[%smAfter %d zeroes.\e[0m\n", join(";",@x[0..($_+5<15?$_+5:15)]), $_}' Seems harmless: if you can programmatically read attributes of a vt character (/dev/vcsa*), multiple probes can obtain parts of vt_mode then lowest byte (5th on 64-bit big-endian) of a pointer. Signed-off-by: Adam Borowski Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/tty') diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 2705ca960e92..b51586fea4e8 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -1316,7 +1316,7 @@ static int vc_t416_color(struct vc_data *vc, int i, /* 256 colours -- ubiquitous */ i++; rgb_from_256(vc->vc_par[i], &c); - } else if (vc->vc_par[i] == 2 && i <= vc->vc_npar + 3) { + } else if (vc->vc_par[i] == 2 && i + 3 <= vc->vc_npar) { /* 24 bit -- extremely rare */ c.r = vc->vc_par[i + 1]; c.g = vc->vc_par[i + 2]; -- cgit v1.2.3 From 0bf1f4a8a399d2c8c0396b8cc0439f8d6ca3581b Mon Sep 17 00:00:00 2001 From: Adam Borowski Date: Thu, 15 Sep 2016 16:47:10 +0200 Subject: vt: Make a comparison <= for readability. All other uses of vc_npar are inclusive (save for < NPAR) which raises eyebrows, so let's at least do so consistently. Signed-off-by: Adam Borowski Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/tty') diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index b51586fea4e8..52442ccf20c0 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -1312,7 +1312,7 @@ static int vc_t416_color(struct vc_data *vc, int i, if (i > vc->vc_npar) return i; - if (vc->vc_par[i] == 5 && i < vc->vc_npar) { + if (vc->vc_par[i] == 5 && i + 1 <= vc->vc_npar) { /* 256 colours -- ubiquitous */ i++; rgb_from_256(vc->vc_par[i], &c); -- cgit v1.2.3 From 3e7ec4a0e635d7b95c5ec30df3ed79164bbf3acb Mon Sep 17 00:00:00 2001 From: Adam Borowski Date: Thu, 15 Sep 2016 16:47:11 +0200 Subject: vt: Drop a no longer true comment. Some guy went on a patching spree, adding 24-bit colour support all around: https://gist.github.com/XVilka/8346728 Signed-off-by: Adam Borowski Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/tty') diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 52442ccf20c0..20d89c2a780a 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -1313,11 +1313,11 @@ static int vc_t416_color(struct vc_data *vc, int i, return i; if (vc->vc_par[i] == 5 && i + 1 <= vc->vc_npar) { - /* 256 colours -- ubiquitous */ + /* 256 colours */ i++; rgb_from_256(vc->vc_par[i], &c); } else if (vc->vc_par[i] == 2 && i + 3 <= vc->vc_npar) { - /* 24 bit -- extremely rare */ + /* 24 bit */ c.r = vc->vc_par[i + 1]; c.g = vc->vc_par[i + 2]; c.b = vc->vc_par[i + 3]; -- cgit v1.2.3 From cc67dc28b33917227214a692534d817d727fb934 Mon Sep 17 00:00:00 2001 From: Adam Borowski Date: Thu, 15 Sep 2016 16:47:12 +0200 Subject: vt: Support \e[90-97m (bright foreground colors). These codes are supported by all major terminals, thus they occasionally see some use despite being redundant with \e[38;5;(x+8)m or (less exactly) \e[1;3(x)m. Signed-off-by: Adam Borowski Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vt.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/tty') diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 20d89c2a780a..5cabd796a195 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -1415,6 +1415,10 @@ static void csi_m(struct vc_data *vc) (vc->vc_color & 0x0f); break; default: + if (vc->vc_par[i] >= 90 && vc->vc_par[i] <= 97) { + vc->vc_intensity = 2; + vc->vc_par[i] -= 60; + } if (vc->vc_par[i] >= 30 && vc->vc_par[i] <= 37) vc->vc_color = color_table[vc->vc_par[i] - 30] | (vc->vc_color & 0xf0); -- cgit v1.2.3 From fadb4244085cd04fd9c8b3a4b3bc161f506431f3 Mon Sep 17 00:00:00 2001 From: Adam Borowski Date: Thu, 15 Sep 2016 16:47:13 +0200 Subject: vt: Emulate \e[100-107m (bright background colors). For now, these fall back to regular (dark) colors. It'd be tempting to replace blink with bright backgrounds, as permitted by CGA/VGA -- we already muck with the other programmable bit (foreground brightness vs 512 character font). This would bring vgacon in line with fbcon, which doesn't support blink anywhere but on some drivers renders that bit as bright background. If that is done, this commit should be amended to be one of ways of setting that bit. Signed-off-by: Adam Borowski Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vt.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/tty') diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 5cabd796a195..e841a4e0e726 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -1415,8 +1415,9 @@ static void csi_m(struct vc_data *vc) (vc->vc_color & 0x0f); break; default: - if (vc->vc_par[i] >= 90 && vc->vc_par[i] <= 97) { - vc->vc_intensity = 2; + if (vc->vc_par[i] >= 90 && vc->vc_par[i] <= 107) { + if (vc->vc_par[i] < 100) + vc->vc_intensity = 2; vc->vc_par[i] -= 60; } if (vc->vc_par[i] >= 30 && vc->vc_par[i] <= 37) -- cgit v1.2.3 From 1664bc40d31403a755fe88307410ed858fe2c6c9 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Sat, 17 Sep 2016 01:13:46 +0000 Subject: serial: mxs-auart: Fix missing clk_disable_unprepare() on error in mxs_get_clks() Commit 5d7519dfc963 ("serial: mxs-auart: Disable clock on error path") try to disable clock on error path, but still missing the clk_set_rate() error handling path. Signed-off-by: Wei Yongjun Reviewed-by: Fabio Estevam Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/mxs-auart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c index ec0ab33efd9d..770454e0dfa3 100644 --- a/drivers/tty/serial/mxs-auart.c +++ b/drivers/tty/serial/mxs-auart.c @@ -1534,7 +1534,7 @@ static int mxs_get_clks(struct mxs_auart_port *s, err = clk_set_rate(s->clk, clk_get_rate(s->clk_ahb)); if (err) { dev_err(s->dev, "Failed to set rate!\n"); - return err; + goto disable_clk_ahb; } err = clk_prepare_enable(s->clk); -- cgit v1.2.3 From e06690bff6ab642e0ab1b22e5bec532f6faf2f5d Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Sat, 17 Sep 2016 14:14:38 +0800 Subject: tty: amba-pl011: uart_amba_port is not available with earlycon function Commit 0e125a5facf8 ("tty: amba-pl011: define flag register bits for ZTE device") changes earlycon function pl011_putc() to use a pointer to uart_amba_port. This causes a regression when earlycon is enabled, because uart_amba_port is not available yet at earlycon time. Let's revert the change on pl011_putc() to fix the regression. The earlycon support for ZTE device can probably be added later by declaring a new earlycon setup function with a vendor specific compatible. Reported-by: Sudeep Holla Fixes: 0e125a5facf8 ("tty: amba-pl011: define flag register bits for ZTE device") Signed-off-by: Shawn Guo Tested-by: Sudeep Holla Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/amba-pl011.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 0b78b04e895e..2d9ffab16ffe 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -2330,16 +2330,13 @@ static struct console amba_console = { static void pl011_putc(struct uart_port *port, int c) { - struct uart_amba_port *uap = - container_of(port, struct uart_amba_port, port); - while (readl(port->membase + UART01x_FR) & UART01x_FR_TXFF) cpu_relax(); if (port->iotype == UPIO_MEM32) writel(c, port->membase + UART01x_DR); else writeb(c, port->membase + UART01x_DR); - while (readl(port->membase + UART01x_FR) & uap->vendor->fr_busy) + while (readl(port->membase + UART01x_FR) & UART01x_FR_BUSY) cpu_relax(); } -- cgit v1.2.3 From b6fce7382d72274336aeafe6e44da755a371ed32 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Mon, 19 Sep 2016 06:56:59 +0200 Subject: serial: 8250_pci: Use symbolic constants for EXAR's MPIO registers Less magic that only requires comments. Signed-off-by: Jan Kiszka Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_pci.c | 55 +++++++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 21 deletions(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index 4ea8b51dd5d3..b98c1578f45a 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -1583,6 +1583,19 @@ static int pci_eg20t_init(struct pci_dev *dev) #define PCI_DEVICE_ID_EXAR_XR17V4358 0x4358 #define PCI_DEVICE_ID_EXAR_XR17V8358 0x8358 +#define UART_EXAR_MPIOINT_7_0 0x8f /* MPIOINT[7:0] */ +#define UART_EXAR_MPIOLVL_7_0 0x90 /* MPIOLVL[7:0] */ +#define UART_EXAR_MPIO3T_7_0 0x91 /* MPIO3T[7:0] */ +#define UART_EXAR_MPIOINV_7_0 0x92 /* MPIOINV[7:0] */ +#define UART_EXAR_MPIOSEL_7_0 0x93 /* MPIOSEL[7:0] */ +#define UART_EXAR_MPIOOD_7_0 0x94 /* MPIOOD[7:0] */ +#define UART_EXAR_MPIOINT_15_8 0x95 /* MPIOINT[15:8] */ +#define UART_EXAR_MPIOLVL_15_8 0x96 /* MPIOLVL[15:8] */ +#define UART_EXAR_MPIO3T_15_8 0x97 /* MPIO3T[15:8] */ +#define UART_EXAR_MPIOINV_15_8 0x98 /* MPIOINV[15:8] */ +#define UART_EXAR_MPIOSEL_15_8 0x99 /* MPIOSEL[15:8] */ +#define UART_EXAR_MPIOOD_15_8 0x9a /* MPIOOD[15:8] */ + static int pci_xr17c154_setup(struct serial_private *priv, const struct pciserial_board *board, @@ -1625,18 +1638,18 @@ pci_xr17v35x_setup(struct serial_private *priv, * Setup Multipurpose Input/Output pins. */ if (idx == 0) { - writeb(0x00, p + 0x8f); /*MPIOINT[7:0]*/ - writeb(0x00, p + 0x90); /*MPIOLVL[7:0]*/ - writeb(0x00, p + 0x91); /*MPIO3T[7:0]*/ - writeb(0x00, p + 0x92); /*MPIOINV[7:0]*/ - writeb(0x00, p + 0x93); /*MPIOSEL[7:0]*/ - writeb(0x00, p + 0x94); /*MPIOOD[7:0]*/ - writeb(0x00, p + 0x95); /*MPIOINT[15:8]*/ - writeb(0x00, p + 0x96); /*MPIOLVL[15:8]*/ - writeb(0x00, p + 0x97); /*MPIO3T[15:8]*/ - writeb(0x00, p + 0x98); /*MPIOINV[15:8]*/ - writeb(0x00, p + 0x99); /*MPIOSEL[15:8]*/ - writeb(0x00, p + 0x9a); /*MPIOOD[15:8]*/ + writeb(0x00, p + UART_EXAR_MPIOINT_7_0); + writeb(0x00, p + UART_EXAR_MPIOLVL_7_0); + writeb(0x00, p + UART_EXAR_MPIO3T_7_0); + writeb(0x00, p + UART_EXAR_MPIOINV_7_0); + writeb(0x00, p + UART_EXAR_MPIOSEL_7_0); + writeb(0x00, p + UART_EXAR_MPIOOD_7_0); + writeb(0x00, p + UART_EXAR_MPIOINT_15_8); + writeb(0x00, p + UART_EXAR_MPIOLVL_15_8); + writeb(0x00, p + UART_EXAR_MPIO3T_15_8); + writeb(0x00, p + UART_EXAR_MPIOINV_15_8); + writeb(0x00, p + UART_EXAR_MPIOSEL_15_8); + writeb(0x00, p + UART_EXAR_MPIOOD_15_8); } writeb(0x00, p + UART_EXAR_8XMODE); writeb(UART_FCTR_EXAR_TRGD, p + UART_EXAR_FCTR); @@ -1672,20 +1685,20 @@ pci_fastcom335_setup(struct serial_private *priv, switch (priv->dev->device) { case PCI_DEVICE_ID_COMMTECH_4222PCI335: case PCI_DEVICE_ID_COMMTECH_4224PCI335: - writeb(0x78, p + 0x90); /* MPIOLVL[7:0] */ - writeb(0x00, p + 0x92); /* MPIOINV[7:0] */ - writeb(0x00, p + 0x93); /* MPIOSEL[7:0] */ + writeb(0x78, p + UART_EXAR_MPIOLVL_7_0); + writeb(0x00, p + UART_EXAR_MPIOINV_7_0); + writeb(0x00, p + UART_EXAR_MPIOSEL_7_0); break; case PCI_DEVICE_ID_COMMTECH_2324PCI335: case PCI_DEVICE_ID_COMMTECH_2328PCI335: - writeb(0x00, p + 0x90); /* MPIOLVL[7:0] */ - writeb(0xc0, p + 0x92); /* MPIOINV[7:0] */ - writeb(0xc0, p + 0x93); /* MPIOSEL[7:0] */ + writeb(0x00, p + UART_EXAR_MPIOLVL_7_0); + writeb(0xc0, p + UART_EXAR_MPIOINV_7_0); + writeb(0xc0, p + UART_EXAR_MPIOSEL_7_0); break; } - writeb(0x00, p + 0x8f); /* MPIOINT[7:0] */ - writeb(0x00, p + 0x91); /* MPIO3T[7:0] */ - writeb(0x00, p + 0x94); /* MPIOOD[7:0] */ + writeb(0x00, p + UART_EXAR_MPIOINT_7_0); + writeb(0x00, p + UART_EXAR_MPIO3T_7_0); + writeb(0x00, p + UART_EXAR_MPIOOD_7_0); } writeb(0x00, p + UART_EXAR_8XMODE); writeb(UART_FCTR_EXAR_TRGD, p + UART_EXAR_FCTR); -- cgit v1.2.3 From 54f19b4a679149130f78413c421a5780e90a9d0a Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Wed, 21 Sep 2016 16:43:15 +0200 Subject: tty/serial/8250: Touch NMI watchdog in wait_for_xmitr First loop in wait_for_xmitr could also trigger NMI watchdog in case reading from the port is slow: PID: 0 TASK: ffffffff819c1460 CPU: 0 COMMAND: "swapper/0" #0 [ffff88019f405e58] crash_nmi_callback at ffffffff8104d382 #1 [ffff88019f405e68] nmi_handle at ffffffff8168ead9 #2 [ffff88019f405eb0] do_nmi at ffffffff8168ec53 #3 [ffff88019f405ef0] end_repeat_nmi at ffffffff8168df13 [exception RIP: delay_tsc+50] RIP: ffffffff81325642 RSP: ffff88019f403bb0 RFLAGS: 00000083 RAX: 00000000000005c8 RBX: ffffffff81f83000 RCX: 0000024e4fb88a8b RDX: 0000024e4fb89053 RSI: 0000000000000000 RDI: 00000000000007d1 RBP: ffff88019f403bb0 R8: 000000000000000a R9: 0000000000000000 R10: 0000000000000000 R11: ffff88019f403ad6 R12: 000000000000250f R13: 0000000000000020 R14: ffffffff81d360c7 R15: 0000000000000047 ORIG_RAX: ffffffffffffffff CS: 0010 SS: 0018 --- --- #4 [ffff88019f403bb0] delay_tsc at ffffffff81325642 #5 [ffff88019f403bb8] __const_udelay at ffffffff813255a8 #6 [ffff88019f403bc8] wait_for_xmitr at ffffffff81404390 #7 [ffff88019f403bf0] serial8250_console_putchar at ffffffff8140455c #8 [ffff88019f403c10] uart_console_write at ffffffff813ff00a #9 [ffff88019f403c40] serial8250_console_write at ffffffff814044ae #10 [ffff88019f403c88] call_console_drivers.constprop.15 at ffffffff81086b01 #11 [ffff88019f403cb0] console_unlock at ffffffff8108842f #12 [ffff88019f403ce8] vprintk_emit at ffffffff81088834 #13 [ffff88019f403d58] vprintk_default at ffffffff81088ba9 #14 [ffff88019f403d68] printk at ffffffff8167f034 Adding touch_nmi_watchdog call to the first loop as well. Reported-by: Chunyu Hu Signed-off-by: Jiri Olsa Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_port.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index ae3a10462b2a..28d29fac1973 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -1982,6 +1982,7 @@ static void wait_for_xmitr(struct uart_8250_port *up, int bits) if (--tmout == 0) break; udelay(1); + touch_nmi_watchdog(); } /* Wait up to 1s for flow control if necessary */ -- cgit v1.2.3 From 3816b2f886d0918d8a8ae593b2db203ab905a889 Mon Sep 17 00:00:00 2001 From: Nava kishore Manne Date: Thu, 15 Sep 2016 14:45:29 +0530 Subject: serial: xuartps: Adds RXBS register support for zynqmp This patch adds RXBS register access support for zynqmp. To avoid the corner error conditions it will consider only RXBS[2:0] bits while checking the error conditions (Parity,Framing and BRAK). Signed-off-by: Nava kishore Manne Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/xilinx_uartps.c | 101 +++++++++++++++++++++++++++++-------- 1 file changed, 81 insertions(+), 20 deletions(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index 11a2b36e14bb..bf06d2c6b96e 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -57,7 +57,7 @@ MODULE_PARM_DESC(rx_timeout, "Rx timeout, 1-255"); #define CDNS_UART_IMR 0x10 /* Interrupt Mask */ #define CDNS_UART_ISR 0x14 /* Interrupt Status */ #define CDNS_UART_BAUDGEN 0x18 /* Baud Rate Generator */ -#define CDNS_UART_RXTOUT 0x1C /* RX Timeout */ +#define CDNS_UART_RXTOUT 0x1C /* RX Timeout */ #define CDNS_UART_RXWM 0x20 /* RX FIFO Trigger Level */ #define CDNS_UART_MODEMCR 0x24 /* Modem Control */ #define CDNS_UART_MODEMSR 0x28 /* Modem Status */ @@ -68,6 +68,7 @@ MODULE_PARM_DESC(rx_timeout, "Rx timeout, 1-255"); #define CDNS_UART_IRRX_PWIDTH 0x3C /* IR Min Received Pulse Width */ #define CDNS_UART_IRTX_PWIDTH 0x40 /* IR Transmitted pulse Width */ #define CDNS_UART_TXWM 0x44 /* TX FIFO Trigger Level */ +#define CDNS_UART_RXBS 0x48 /* RX FIFO byte status register */ /* Control Register Bit Definitions */ #define CDNS_UART_CR_STOPBRK 0x00000100 /* Stop TX break */ @@ -79,6 +80,9 @@ MODULE_PARM_DESC(rx_timeout, "Rx timeout, 1-255"); #define CDNS_UART_CR_TXRST 0x00000002 /* TX logic reset */ #define CDNS_UART_CR_RXRST 0x00000001 /* RX logic reset */ #define CDNS_UART_CR_RST_TO 0x00000040 /* Restart Timeout Counter */ +#define CDNS_UART_RXBS_PARITY 0x00000001 /* Parity error status */ +#define CDNS_UART_RXBS_FRAMING 0x00000002 /* Framing error status */ +#define CDNS_UART_RXBS_BRK 0x00000004 /* Overrun error status */ /* * Mode Register: @@ -131,8 +135,9 @@ MODULE_PARM_DESC(rx_timeout, "Rx timeout, 1-255"); CDNS_UART_IXR_TOUT) /* Goes in read_status_mask for break detection as the HW doesn't do it*/ -#define CDNS_UART_IXR_BRK 0x80000000 +#define CDNS_UART_IXR_BRK 0x00002000 +#define CDNS_UART_RXBS_SUPPORT BIT(1) /* * Modem Control register: * The read/write Modem Control register controls the interface with the modem @@ -172,18 +177,29 @@ struct cdns_uart { struct clk *pclk; unsigned int baud; struct notifier_block clk_rate_change_nb; + u32 quirks; +}; +struct cdns_platform_data { + u32 quirks; }; #define to_cdns_uart(_nb) container_of(_nb, struct cdns_uart, \ clk_rate_change_nb); static void cdns_uart_handle_rx(struct uart_port *port, unsigned int isrstatus) { + struct cdns_uart *cdns_uart = port->private_data; + bool is_rxbs_support; + unsigned int rxbs_status = 0; + unsigned int status_mask; + + is_rxbs_support = cdns_uart->quirks & CDNS_UART_RXBS_SUPPORT; + /* * There is no hardware break detection, so we interpret framing * error with all-zeros data as a break sequence. Most of the time, * there's another non-zero byte at the end of the sequence. */ - if (isrstatus & CDNS_UART_IXR_FRAMING) { + if (!is_rxbs_support && (isrstatus & CDNS_UART_IXR_FRAMING)) { while (!(readl(port->membase + CDNS_UART_SR) & CDNS_UART_SR_RXEMPTY)) { if (!readl(port->membase + CDNS_UART_FIFO)) { @@ -200,6 +216,8 @@ static void cdns_uart_handle_rx(struct uart_port *port, unsigned int isrstatus) isrstatus &= port->read_status_mask; isrstatus &= ~port->ignore_status_mask; + status_mask = port->read_status_mask; + status_mask &= ~port->ignore_status_mask; if (!(isrstatus & (CDNS_UART_IXR_TOUT | CDNS_UART_IXR_RXTRIG))) return; @@ -208,6 +226,9 @@ static void cdns_uart_handle_rx(struct uart_port *port, unsigned int isrstatus) u32 data; char status = TTY_NORMAL; + if (is_rxbs_support) + rxbs_status = readl(port->membase + CDNS_UART_RXBS); + data = readl(port->membase + CDNS_UART_FIFO); /* Non-NULL byte after BREAK is garbage (99%) */ @@ -217,21 +238,41 @@ static void cdns_uart_handle_rx(struct uart_port *port, unsigned int isrstatus) if (uart_handle_break(port)) continue; } + if (is_rxbs_support && (rxbs_status & CDNS_UART_RXBS_BRK)) { + port->icount.brk++; + status = TTY_BREAK; + if (uart_handle_break(port)) + continue; + } if (uart_handle_sysrq_char(port, data)) continue; port->icount.rx++; - if (isrstatus & CDNS_UART_IXR_PARITY) { - port->icount.parity++; - status = TTY_PARITY; - } else if (isrstatus & CDNS_UART_IXR_FRAMING) { - port->icount.frame++; - status = TTY_FRAME; - } else if (isrstatus & CDNS_UART_IXR_OVERRUN) { - port->icount.overrun++; + if (is_rxbs_support) { + if ((rxbs_status & CDNS_UART_RXBS_PARITY) + && (status_mask & CDNS_UART_IXR_PARITY)) { + port->icount.parity++; + status = TTY_PARITY; + } + if ((rxbs_status & CDNS_UART_RXBS_FRAMING) + && (status_mask & CDNS_UART_IXR_PARITY)) { + port->icount.frame++; + status = TTY_FRAME; + } + } else { + if (isrstatus & CDNS_UART_IXR_PARITY) { + port->icount.parity++; + status = TTY_PARITY; + } + if (isrstatus & CDNS_UART_IXR_FRAMING) { + port->icount.frame++; + status = TTY_FRAME; + } } + if (isrstatus & CDNS_UART_IXR_OVERRUN) + port->icount.overrun++; uart_insert_char(port, isrstatus, CDNS_UART_IXR_OVERRUN, data, status); @@ -736,10 +777,14 @@ static void cdns_uart_set_termios(struct uart_port *port, */ static int cdns_uart_startup(struct uart_port *port) { + struct cdns_uart *cdns_uart = port->private_data; + bool is_brk_support; int ret; unsigned long flags; unsigned int status = 0; + is_brk_support = cdns_uart->quirks & CDNS_UART_RXBS_SUPPORT; + spin_lock_irqsave(&port->lock, flags); /* Disable the TX and RX */ @@ -794,7 +839,11 @@ static int cdns_uart_startup(struct uart_port *port) } /* Set the Interrupt Registers with desired interrupts */ - writel(CDNS_UART_RX_IRQS, port->membase + CDNS_UART_IER); + if (is_brk_support) + writel(CDNS_UART_RX_IRQS | CDNS_UART_IXR_BRK, + port->membase + CDNS_UART_IER); + else + writel(CDNS_UART_RX_IRQS, port->membase + CDNS_UART_IER); return 0; } @@ -1328,6 +1377,18 @@ static int cdns_uart_resume(struct device *device) static SIMPLE_DEV_PM_OPS(cdns_uart_dev_pm_ops, cdns_uart_suspend, cdns_uart_resume); +static const struct cdns_platform_data zynqmp_uart_def = { + .quirks = CDNS_UART_RXBS_SUPPORT, }; + +/* Match table for of_platform binding */ +static const struct of_device_id cdns_uart_of_match[] = { + { .compatible = "xlnx,xuartps", }, + { .compatible = "cdns,uart-r1p8", }, + { .compatible = "cdns,uart-r1p12", .data = &zynqmp_uart_def }, + {} +}; +MODULE_DEVICE_TABLE(of, cdns_uart_of_match); + /** * cdns_uart_probe - Platform driver probe * @pdev: Pointer to the platform device structure @@ -1340,12 +1401,20 @@ static int cdns_uart_probe(struct platform_device *pdev) struct uart_port *port; struct resource *res; struct cdns_uart *cdns_uart_data; + const struct of_device_id *match; cdns_uart_data = devm_kzalloc(&pdev->dev, sizeof(*cdns_uart_data), GFP_KERNEL); if (!cdns_uart_data) return -ENOMEM; + match = of_match_node(cdns_uart_of_match, pdev->dev.of_node); + if (match && match->data) { + const struct cdns_platform_data *data = match->data; + + cdns_uart_data->quirks = data->quirks; + } + cdns_uart_data->pclk = devm_clk_get(&pdev->dev, "pclk"); if (IS_ERR(cdns_uart_data->pclk)) { cdns_uart_data->pclk = devm_clk_get(&pdev->dev, "aper_clk"); @@ -1471,14 +1540,6 @@ static int cdns_uart_remove(struct platform_device *pdev) return rc; } -/* Match table for of_platform binding */ -static const struct of_device_id cdns_uart_of_match[] = { - { .compatible = "xlnx,xuartps", }, - { .compatible = "cdns,uart-r1p8", }, - {} -}; -MODULE_DEVICE_TABLE(of, cdns_uart_of_match); - static struct platform_driver cdns_uart_platform_driver = { .probe = cdns_uart_probe, .remove = cdns_uart_remove, -- cgit v1.2.3 From 27b17ae0732a607532e58b3d52b0b636d82269da Mon Sep 17 00:00:00 2001 From: Nava kishore Manne Date: Thu, 15 Sep 2016 14:45:31 +0530 Subject: tty: serial: xuartps: Wait for rx and tx reset done status After issuing the reset, driver is not checking the rx and tx reset done status. So, modified driver to wait for the reset done status. Signed-off-by: Nava kishore Manne Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/xilinx_uartps.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index bf06d2c6b96e..e0c6a8619b9f 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -694,6 +694,10 @@ static void cdns_uart_set_termios(struct uart_port *port, ctrl_reg |= CDNS_UART_CR_TXRST | CDNS_UART_CR_RXRST; writel(ctrl_reg, port->membase + CDNS_UART_CR); + while (readl(port->membase + CDNS_UART_CR) & + (CDNS_UART_CR_TXRST | CDNS_UART_CR_RXRST)) + cpu_relax(); + /* * Clear the RX disable and TX disable bits and then set the TX enable * bit and RX enable bit to enable the transmitter and receiver. @@ -797,6 +801,10 @@ static int cdns_uart_startup(struct uart_port *port) writel(CDNS_UART_CR_TXRST | CDNS_UART_CR_RXRST, port->membase + CDNS_UART_CR); + while (readl(port->membase + CDNS_UART_CR) & + (CDNS_UART_CR_TXRST | CDNS_UART_CR_RXRST)) + cpu_relax(); + /* * Clear the RX disable bit and then set the RX enable bit to enable * the receiver. -- cgit v1.2.3 From ada8618ff3bfe183cc2c1ae34239a5168ba34411 Mon Sep 17 00:00:00 2001 From: Alexandre TORGUE Date: Thu, 15 Sep 2016 18:42:33 +0200 Subject: serial: stm32: adding support for stm32f7 Register offset management rework to support both stm32f4 (default) and stm32f7. Driver rework to ensure same functional level on both stm32f4 and stm32f7: no new feature in this version yet. Signed-off-by: Gerald Baeza Signed-off-by: Alexandre TORGUE Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/stm32-usart.c | 269 +++++++++++++++++++++++++++++++-------- 1 file changed, 219 insertions(+), 50 deletions(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index f89d1f79be18..e7ffd01669f5 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -1,6 +1,7 @@ /* * Copyright (C) Maxime Coquelin 2015 - * Author: Maxime Coquelin + * Authors: Maxime Coquelin + * Gerald Baeza * License terms: GNU General Public License (GPL), version 2 * * Inspired by st-asc.c from STMicroelectronics (c) @@ -29,16 +30,74 @@ #define DRIVER_NAME "stm32-usart" +struct stm32_usart_offsets { + u8 cr1; + u8 cr2; + u8 cr3; + u8 brr; + u8 gtpr; + u8 rtor; + u8 rqr; + u8 isr; + u8 icr; + u8 rdr; + u8 tdr; +}; + +struct stm32_usart_config { + u8 uart_enable_bit; /* USART_CR1_UE */ + bool has_7bits_data; +}; + +struct stm32_usart_info { + struct stm32_usart_offsets ofs; + struct stm32_usart_config cfg; +}; + +#define UNDEF_REG ~0 + /* Register offsets */ -#define USART_SR 0x00 -#define USART_DR 0x04 -#define USART_BRR 0x08 -#define USART_CR1 0x0c -#define USART_CR2 0x10 -#define USART_CR3 0x14 -#define USART_GTPR 0x18 - -/* USART_SR */ +struct stm32_usart_info stm32f4_info = { + .ofs = { + .isr = 0x00, + .rdr = 0x04, + .tdr = 0x04, + .brr = 0x08, + .cr1 = 0x0c, + .cr2 = 0x10, + .cr3 = 0x14, + .gtpr = 0x18, + .rtor = UNDEF_REG, + .rqr = UNDEF_REG, + .icr = UNDEF_REG, + }, + .cfg = { + .uart_enable_bit = 13, + .has_7bits_data = false, + } +}; + +struct stm32_usart_info stm32f7_info = { + .ofs = { + .cr1 = 0x00, + .cr2 = 0x04, + .cr3 = 0x08, + .brr = 0x0c, + .gtpr = 0x10, + .rtor = 0x14, + .rqr = 0x18, + .isr = 0x1c, + .icr = 0x20, + .rdr = 0x24, + .tdr = 0x28, + }, + .cfg = { + .uart_enable_bit = 0, + .has_7bits_data = true, + } +}; + +/* USART_SR (F4) / USART_ISR (F7) */ #define USART_SR_PE BIT(0) #define USART_SR_FE BIT(1) #define USART_SR_NF BIT(2) @@ -48,7 +107,16 @@ #define USART_SR_TC BIT(6) #define USART_SR_TXE BIT(7) #define USART_SR_LBD BIT(8) -#define USART_SR_CTS BIT(9) +#define USART_SR_CTSIF BIT(9) +#define USART_SR_CTS BIT(10) /* F7 */ +#define USART_SR_RTOF BIT(11) /* F7 */ +#define USART_SR_EOBF BIT(12) /* F7 */ +#define USART_SR_ABRE BIT(14) /* F7 */ +#define USART_SR_ABRF BIT(15) /* F7 */ +#define USART_SR_BUSY BIT(16) /* F7 */ +#define USART_SR_CMF BIT(17) /* F7 */ +#define USART_SR_SBKF BIT(18) /* F7 */ +#define USART_SR_TEACK BIT(21) /* F7 */ #define USART_SR_ERR_MASK (USART_SR_LBD | USART_SR_ORE | \ USART_SR_FE | USART_SR_PE) /* Dummy bits */ @@ -64,7 +132,7 @@ /* USART_CR1 */ #define USART_CR1_SBK BIT(0) -#define USART_CR1_RWU BIT(1) +#define USART_CR1_RWU BIT(1) /* F4 */ #define USART_CR1_RE BIT(2) #define USART_CR1_TE BIT(3) #define USART_CR1_IDLEIE BIT(4) @@ -76,12 +144,20 @@ #define USART_CR1_PCE BIT(10) #define USART_CR1_WAKE BIT(11) #define USART_CR1_M BIT(12) -#define USART_CR1_UE BIT(13) +#define USART_CR1_M0 BIT(12) /* F7 */ +#define USART_CR1_MME BIT(13) /* F7 */ +#define USART_CR1_CMIE BIT(14) /* F7 */ #define USART_CR1_OVER8 BIT(15) -#define USART_CR1_IE_MASK GENMASK(8, 4) +#define USART_CR1_DEDT_MASK GENMASK(20, 16) /* F7 */ +#define USART_CR1_DEAT_MASK GENMASK(25, 21) /* F7 */ +#define USART_CR1_RTOIE BIT(26) /* F7 */ +#define USART_CR1_EOBIE BIT(27) /* F7 */ +#define USART_CR1_M1 BIT(28) /* F7 */ +#define USART_CR1_IE_MASK (GENMASK(8, 4) | BIT(14) | BIT(26) | BIT(27)) /* USART_CR2 */ -#define USART_CR2_ADD_MASK GENMASK(3, 0) +#define USART_CR2_ADD_MASK GENMASK(3, 0) /* F4 */ +#define USART_CR2_ADDM7 BIT(4) /* F7 */ #define USART_CR2_LBDL BIT(5) #define USART_CR2_LBDIE BIT(6) #define USART_CR2_LBCL BIT(8) @@ -91,6 +167,15 @@ #define USART_CR2_STOP_2B BIT(13) #define USART_CR2_STOP_MASK GENMASK(13, 12) #define USART_CR2_LINEN BIT(14) +#define USART_CR2_SWAP BIT(15) /* F7 */ +#define USART_CR2_RXINV BIT(16) /* F7 */ +#define USART_CR2_TXINV BIT(17) /* F7 */ +#define USART_CR2_DATAINV BIT(18) /* F7 */ +#define USART_CR2_MSBFIRST BIT(19) /* F7 */ +#define USART_CR2_ABREN BIT(20) /* F7 */ +#define USART_CR2_ABRMOD_MASK GENMASK(22, 21) /* F7 */ +#define USART_CR2_RTOEN BIT(23) /* F7 */ +#define USART_CR2_ADD_F7_MASK GENMASK(31, 24) /* F7 */ /* USART_CR3 */ #define USART_CR3_EIE BIT(0) @@ -105,18 +190,47 @@ #define USART_CR3_CTSE BIT(9) #define USART_CR3_CTSIE BIT(10) #define USART_CR3_ONEBIT BIT(11) +#define USART_CR3_OVRDIS BIT(12) /* F7 */ +#define USART_CR3_DDRE BIT(13) /* F7 */ +#define USART_CR3_DEM BIT(14) /* F7 */ +#define USART_CR3_DEP BIT(15) /* F7 */ +#define USART_CR3_SCARCNT_MASK GENMASK(19, 17) /* F7 */ /* USART_GTPR */ #define USART_GTPR_PSC_MASK GENMASK(7, 0) #define USART_GTPR_GT_MASK GENMASK(15, 8) -#define DRIVER_NAME "stm32-usart" +/* USART_RTOR */ +#define USART_RTOR_RTO_MASK GENMASK(23, 0) /* F7 */ +#define USART_RTOR_BLEN_MASK GENMASK(31, 24) /* F7 */ + +/* USART_RQR */ +#define USART_RQR_ABRRQ BIT(0) /* F7 */ +#define USART_RQR_SBKRQ BIT(1) /* F7 */ +#define USART_RQR_MMRQ BIT(2) /* F7 */ +#define USART_RQR_RXFRQ BIT(3) /* F7 */ +#define USART_RQR_TXFRQ BIT(4) /* F7 */ + +/* USART_ICR */ +#define USART_ICR_PECF BIT(0) /* F7 */ +#define USART_ICR_FFECF BIT(1) /* F7 */ +#define USART_ICR_NCF BIT(2) /* F7 */ +#define USART_ICR_ORECF BIT(3) /* F7 */ +#define USART_ICR_IDLECF BIT(4) /* F7 */ +#define USART_ICR_TCCF BIT(6) /* F7 */ +#define USART_ICR_LBDCF BIT(8) /* F7 */ +#define USART_ICR_CTSCF BIT(9) /* F7 */ +#define USART_ICR_RTOCF BIT(11) /* F7 */ +#define USART_ICR_EOBCF BIT(12) /* F7 */ +#define USART_ICR_CMCF BIT(17) /* F7 */ + #define STM32_SERIAL_NAME "ttyS" #define STM32_MAX_PORTS 6 struct stm32_port { struct uart_port port; struct clk *clk; + struct stm32_usart_info *info; bool hw_flow_control; }; @@ -151,6 +265,8 @@ static void stm32_clr_bits(struct uart_port *port, u32 reg, u32 bits) static void stm32_receive_chars(struct uart_port *port) { struct tty_port *tport = &port->state->port; + struct stm32_port *stm32_port = to_stm32_port(port); + struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; unsigned long c; u32 sr; char flag; @@ -158,9 +274,9 @@ static void stm32_receive_chars(struct uart_port *port) if (port->irq_wake) pm_wakeup_event(tport->tty->dev, 0); - while ((sr = readl_relaxed(port->membase + USART_SR)) & USART_SR_RXNE) { + while ((sr = readl_relaxed(port->membase + ofs->isr)) & USART_SR_RXNE) { sr |= USART_SR_DUMMY_RX; - c = readl_relaxed(port->membase + USART_DR); + c = readl_relaxed(port->membase + ofs->rdr); flag = TTY_NORMAL; port->icount.rx++; @@ -170,6 +286,10 @@ static void stm32_receive_chars(struct uart_port *port) if (uart_handle_break(port)) continue; } else if (sr & USART_SR_ORE) { + if (ofs->icr != UNDEF_REG) + writel_relaxed(USART_ICR_ORECF, + port->membase + + ofs->icr); port->icount.overrun++; } else if (sr & USART_SR_PE) { port->icount.parity++; @@ -199,10 +319,12 @@ static void stm32_receive_chars(struct uart_port *port) static void stm32_transmit_chars(struct uart_port *port) { + struct stm32_port *stm32_port = to_stm32_port(port); + struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; struct circ_buf *xmit = &port->state->xmit; if (port->x_char) { - writel_relaxed(port->x_char, port->membase + USART_DR); + writel_relaxed(port->x_char, port->membase + ofs->tdr); port->x_char = 0; port->icount.tx++; return; @@ -218,7 +340,7 @@ static void stm32_transmit_chars(struct uart_port *port) return; } - writel_relaxed(xmit->buf[xmit->tail], port->membase + USART_DR); + writel_relaxed(xmit->buf[xmit->tail], port->membase + ofs->tdr); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); port->icount.tx++; @@ -232,11 +354,13 @@ static void stm32_transmit_chars(struct uart_port *port) static irqreturn_t stm32_interrupt(int irq, void *ptr) { struct uart_port *port = ptr; + struct stm32_port *stm32_port = to_stm32_port(port); + struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; u32 sr; spin_lock(&port->lock); - sr = readl_relaxed(port->membase + USART_SR); + sr = readl_relaxed(port->membase + ofs->isr); if (sr & USART_SR_RXNE) stm32_receive_chars(port); @@ -251,15 +375,21 @@ static irqreturn_t stm32_interrupt(int irq, void *ptr) static unsigned int stm32_tx_empty(struct uart_port *port) { - return readl_relaxed(port->membase + USART_SR) & USART_SR_TXE; + struct stm32_port *stm32_port = to_stm32_port(port); + struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; + + return readl_relaxed(port->membase + ofs->isr) & USART_SR_TXE; } static void stm32_set_mctrl(struct uart_port *port, unsigned int mctrl) { + struct stm32_port *stm32_port = to_stm32_port(port); + struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; + if ((mctrl & TIOCM_RTS) && (port->status & UPSTAT_AUTORTS)) - stm32_set_bits(port, USART_CR3, USART_CR3_RTSE); + stm32_set_bits(port, ofs->cr3, USART_CR3_RTSE); else - stm32_clr_bits(port, USART_CR3, USART_CR3_RTSE); + stm32_clr_bits(port, ofs->cr3, USART_CR3_RTSE); } static unsigned int stm32_get_mctrl(struct uart_port *port) @@ -271,44 +401,56 @@ static unsigned int stm32_get_mctrl(struct uart_port *port) /* Transmit stop */ static void stm32_stop_tx(struct uart_port *port) { - stm32_clr_bits(port, USART_CR1, USART_CR1_TXEIE); + struct stm32_port *stm32_port = to_stm32_port(port); + struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; + + stm32_clr_bits(port, ofs->cr1, USART_CR1_TXEIE); } /* There are probably characters waiting to be transmitted. */ static void stm32_start_tx(struct uart_port *port) { + struct stm32_port *stm32_port = to_stm32_port(port); + struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; struct circ_buf *xmit = &port->state->xmit; if (uart_circ_empty(xmit)) return; - stm32_set_bits(port, USART_CR1, USART_CR1_TXEIE | USART_CR1_TE); + stm32_set_bits(port, ofs->cr1, USART_CR1_TXEIE | USART_CR1_TE); } /* Throttle the remote when input buffer is about to overflow. */ static void stm32_throttle(struct uart_port *port) { + struct stm32_port *stm32_port = to_stm32_port(port); + struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; unsigned long flags; spin_lock_irqsave(&port->lock, flags); - stm32_clr_bits(port, USART_CR1, USART_CR1_RXNEIE); + stm32_clr_bits(port, ofs->cr1, USART_CR1_RXNEIE); spin_unlock_irqrestore(&port->lock, flags); } /* Unthrottle the remote, the input buffer can now accept data. */ static void stm32_unthrottle(struct uart_port *port) { + struct stm32_port *stm32_port = to_stm32_port(port); + struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; unsigned long flags; spin_lock_irqsave(&port->lock, flags); - stm32_set_bits(port, USART_CR1, USART_CR1_RXNEIE); + stm32_set_bits(port, ofs->cr1, USART_CR1_RXNEIE); spin_unlock_irqrestore(&port->lock, flags); } /* Receive stop */ static void stm32_stop_rx(struct uart_port *port) { - stm32_clr_bits(port, USART_CR1, USART_CR1_RXNEIE); + struct stm32_port *stm32_port = to_stm32_port(port); + struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; + + stm32_clr_bits(port, ofs->cr1, USART_CR1_RXNEIE); } /* Handle breaks - ignored by us */ @@ -318,6 +460,8 @@ static void stm32_break_ctl(struct uart_port *port, int break_state) static int stm32_startup(struct uart_port *port) { + struct stm32_port *stm32_port = to_stm32_port(port); + struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; const char *name = to_platform_device(port->dev)->name; u32 val; int ret; @@ -327,17 +471,19 @@ static int stm32_startup(struct uart_port *port) return ret; val = USART_CR1_RXNEIE | USART_CR1_TE | USART_CR1_RE; - stm32_set_bits(port, USART_CR1, val); + stm32_set_bits(port, ofs->cr1, val); return 0; } static void stm32_shutdown(struct uart_port *port) { + struct stm32_port *stm32_port = to_stm32_port(port); + struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; u32 val; val = USART_CR1_TXEIE | USART_CR1_RXNEIE | USART_CR1_TE | USART_CR1_RE; - stm32_set_bits(port, USART_CR1, val); + stm32_set_bits(port, ofs->cr1, val); free_irq(port->irq, port); } @@ -346,6 +492,8 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { struct stm32_port *stm32_port = to_stm32_port(port); + struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; + struct stm32_usart_config *cfg = &stm32_port->info->cfg; unsigned int baud; u32 usartdiv, mantissa, fraction, oversampling; tcflag_t cflag = termios->c_cflag; @@ -360,9 +508,10 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios, spin_lock_irqsave(&port->lock, flags); /* Stop serial port and reset value */ - writel_relaxed(0, port->membase + USART_CR1); + writel_relaxed(0, port->membase + ofs->cr1); - cr1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_UE | USART_CR1_RXNEIE; + cr1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_RXNEIE; + cr1 |= BIT(cfg->uart_enable_bit); cr2 = 0; cr3 = 0; @@ -371,8 +520,12 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios, if (cflag & PARENB) { cr1 |= USART_CR1_PCE; - if ((cflag & CSIZE) == CS8) - cr1 |= USART_CR1_M; + if ((cflag & CSIZE) == CS8) { + if (cfg->has_7bits_data) + cr1 |= USART_CR1_M0; + else + cr1 |= USART_CR1_M; + } } if (cflag & PARODD) @@ -394,15 +547,15 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios, */ if (usartdiv < 16) { oversampling = 8; - stm32_set_bits(port, USART_CR1, USART_CR1_OVER8); + stm32_set_bits(port, ofs->cr1, USART_CR1_OVER8); } else { oversampling = 16; - stm32_clr_bits(port, USART_CR1, USART_CR1_OVER8); + stm32_clr_bits(port, ofs->cr1, USART_CR1_OVER8); } mantissa = (usartdiv / oversampling) << USART_BRR_DIV_M_SHIFT; fraction = usartdiv % oversampling; - writel_relaxed(mantissa | fraction, port->membase + USART_BRR); + writel_relaxed(mantissa | fraction, port->membase + ofs->brr); uart_update_timeout(port, cflag, baud); @@ -430,9 +583,9 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios, if ((termios->c_cflag & CREAD) == 0) port->ignore_status_mask |= USART_SR_DUMMY_RX; - writel_relaxed(cr3, port->membase + USART_CR3); - writel_relaxed(cr2, port->membase + USART_CR2); - writel_relaxed(cr1, port->membase + USART_CR1); + writel_relaxed(cr3, port->membase + ofs->cr3); + writel_relaxed(cr2, port->membase + ofs->cr2); + writel_relaxed(cr1, port->membase + ofs->cr1); spin_unlock_irqrestore(&port->lock, flags); } @@ -469,6 +622,8 @@ static void stm32_pm(struct uart_port *port, unsigned int state, { struct stm32_port *stm32port = container_of(port, struct stm32_port, port); + struct stm32_usart_offsets *ofs = &stm32port->info->ofs; + struct stm32_usart_config *cfg = &stm32port->info->cfg; unsigned long flags = 0; switch (state) { @@ -477,7 +632,7 @@ static void stm32_pm(struct uart_port *port, unsigned int state, break; case UART_PM_STATE_OFF: spin_lock_irqsave(&port->lock, flags); - stm32_clr_bits(port, USART_CR1, USART_CR1_UE); + stm32_clr_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit)); spin_unlock_irqrestore(&port->lock, flags); clk_disable_unprepare(stm32port->clk); break; @@ -567,8 +722,10 @@ static struct stm32_port *stm32_of_get_stm32_port(struct platform_device *pdev) #ifdef CONFIG_OF static const struct of_device_id stm32_match[] = { - { .compatible = "st,stm32-usart", }, - { .compatible = "st,stm32-uart", }, + { .compatible = "st,stm32-usart", .data = &stm32f4_info}, + { .compatible = "st,stm32-uart", .data = &stm32f4_info}, + { .compatible = "st,stm32f7-usart", .data = &stm32f7_info}, + { .compatible = "st,stm32f7-uart", .data = &stm32f7_info}, {}, }; @@ -577,13 +734,20 @@ MODULE_DEVICE_TABLE(of, stm32_match); static int stm32_serial_probe(struct platform_device *pdev) { - int ret; + const struct of_device_id *match; struct stm32_port *stm32port; + int ret; stm32port = stm32_of_get_stm32_port(pdev); if (!stm32port) return -ENODEV; + match = of_match_device(stm32_match, &pdev->dev); + if (match && match->data) + stm32port->info = (struct stm32_usart_info *)match->data; + else + return -EINVAL; + ret = stm32_init_port(stm32port, pdev); if (ret) return ret; @@ -608,15 +772,20 @@ static int stm32_serial_remove(struct platform_device *pdev) #ifdef CONFIG_SERIAL_STM32_CONSOLE static void stm32_console_putchar(struct uart_port *port, int ch) { - while (!(readl_relaxed(port->membase + USART_SR) & USART_SR_TXE)) + struct stm32_port *stm32_port = to_stm32_port(port); + struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; + + while (!(readl_relaxed(port->membase + ofs->isr) & USART_SR_TXE)) cpu_relax(); - writel_relaxed(ch, port->membase + USART_DR); + writel_relaxed(ch, port->membase + ofs->tdr); } static void stm32_console_write(struct console *co, const char *s, unsigned cnt) { struct uart_port *port = &stm32_ports[co->index].port; + struct stm32_port *stm32_port = to_stm32_port(port); + struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; unsigned long flags; u32 old_cr1, new_cr1; int locked = 1; @@ -630,14 +799,14 @@ static void stm32_console_write(struct console *co, const char *s, unsigned cnt) spin_lock(&port->lock); /* Save and disable interrupts */ - old_cr1 = readl_relaxed(port->membase + USART_CR1); + old_cr1 = readl_relaxed(port->membase + ofs->cr1); new_cr1 = old_cr1 & ~USART_CR1_IE_MASK; - writel_relaxed(new_cr1, port->membase + USART_CR1); + writel_relaxed(new_cr1, port->membase + ofs->cr1); uart_console_write(port, s, cnt, stm32_console_putchar); /* Restore interrupt state */ - writel_relaxed(old_cr1, port->membase + USART_CR1); + writel_relaxed(old_cr1, port->membase + ofs->cr1); if (locked) spin_unlock(&port->lock); -- cgit v1.2.3 From bc5a0b55ba944cfe847cc2210609c44ff9dccd06 Mon Sep 17 00:00:00 2001 From: Alexandre TORGUE Date: Thu, 15 Sep 2016 18:42:35 +0200 Subject: serial: stm32: header file creation Signed-off-by: Gerald Baeza Signed-off-by: Alexandre TORGUE Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/stm32-usart.c | 209 +------------------------------------ drivers/tty/serial/stm32-usart.h | 215 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 216 insertions(+), 208 deletions(-) create mode 100644 drivers/tty/serial/stm32-usart.h (limited to 'drivers/tty') diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index e7ffd01669f5..6236deb28905 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -28,214 +28,7 @@ #include #include -#define DRIVER_NAME "stm32-usart" - -struct stm32_usart_offsets { - u8 cr1; - u8 cr2; - u8 cr3; - u8 brr; - u8 gtpr; - u8 rtor; - u8 rqr; - u8 isr; - u8 icr; - u8 rdr; - u8 tdr; -}; - -struct stm32_usart_config { - u8 uart_enable_bit; /* USART_CR1_UE */ - bool has_7bits_data; -}; - -struct stm32_usart_info { - struct stm32_usart_offsets ofs; - struct stm32_usart_config cfg; -}; - -#define UNDEF_REG ~0 - -/* Register offsets */ -struct stm32_usart_info stm32f4_info = { - .ofs = { - .isr = 0x00, - .rdr = 0x04, - .tdr = 0x04, - .brr = 0x08, - .cr1 = 0x0c, - .cr2 = 0x10, - .cr3 = 0x14, - .gtpr = 0x18, - .rtor = UNDEF_REG, - .rqr = UNDEF_REG, - .icr = UNDEF_REG, - }, - .cfg = { - .uart_enable_bit = 13, - .has_7bits_data = false, - } -}; - -struct stm32_usart_info stm32f7_info = { - .ofs = { - .cr1 = 0x00, - .cr2 = 0x04, - .cr3 = 0x08, - .brr = 0x0c, - .gtpr = 0x10, - .rtor = 0x14, - .rqr = 0x18, - .isr = 0x1c, - .icr = 0x20, - .rdr = 0x24, - .tdr = 0x28, - }, - .cfg = { - .uart_enable_bit = 0, - .has_7bits_data = true, - } -}; - -/* USART_SR (F4) / USART_ISR (F7) */ -#define USART_SR_PE BIT(0) -#define USART_SR_FE BIT(1) -#define USART_SR_NF BIT(2) -#define USART_SR_ORE BIT(3) -#define USART_SR_IDLE BIT(4) -#define USART_SR_RXNE BIT(5) -#define USART_SR_TC BIT(6) -#define USART_SR_TXE BIT(7) -#define USART_SR_LBD BIT(8) -#define USART_SR_CTSIF BIT(9) -#define USART_SR_CTS BIT(10) /* F7 */ -#define USART_SR_RTOF BIT(11) /* F7 */ -#define USART_SR_EOBF BIT(12) /* F7 */ -#define USART_SR_ABRE BIT(14) /* F7 */ -#define USART_SR_ABRF BIT(15) /* F7 */ -#define USART_SR_BUSY BIT(16) /* F7 */ -#define USART_SR_CMF BIT(17) /* F7 */ -#define USART_SR_SBKF BIT(18) /* F7 */ -#define USART_SR_TEACK BIT(21) /* F7 */ -#define USART_SR_ERR_MASK (USART_SR_LBD | USART_SR_ORE | \ - USART_SR_FE | USART_SR_PE) -/* Dummy bits */ -#define USART_SR_DUMMY_RX BIT(16) - -/* USART_DR */ -#define USART_DR_MASK GENMASK(8, 0) - -/* USART_BRR */ -#define USART_BRR_DIV_F_MASK GENMASK(3, 0) -#define USART_BRR_DIV_M_MASK GENMASK(15, 4) -#define USART_BRR_DIV_M_SHIFT 4 - -/* USART_CR1 */ -#define USART_CR1_SBK BIT(0) -#define USART_CR1_RWU BIT(1) /* F4 */ -#define USART_CR1_RE BIT(2) -#define USART_CR1_TE BIT(3) -#define USART_CR1_IDLEIE BIT(4) -#define USART_CR1_RXNEIE BIT(5) -#define USART_CR1_TCIE BIT(6) -#define USART_CR1_TXEIE BIT(7) -#define USART_CR1_PEIE BIT(8) -#define USART_CR1_PS BIT(9) -#define USART_CR1_PCE BIT(10) -#define USART_CR1_WAKE BIT(11) -#define USART_CR1_M BIT(12) -#define USART_CR1_M0 BIT(12) /* F7 */ -#define USART_CR1_MME BIT(13) /* F7 */ -#define USART_CR1_CMIE BIT(14) /* F7 */ -#define USART_CR1_OVER8 BIT(15) -#define USART_CR1_DEDT_MASK GENMASK(20, 16) /* F7 */ -#define USART_CR1_DEAT_MASK GENMASK(25, 21) /* F7 */ -#define USART_CR1_RTOIE BIT(26) /* F7 */ -#define USART_CR1_EOBIE BIT(27) /* F7 */ -#define USART_CR1_M1 BIT(28) /* F7 */ -#define USART_CR1_IE_MASK (GENMASK(8, 4) | BIT(14) | BIT(26) | BIT(27)) - -/* USART_CR2 */ -#define USART_CR2_ADD_MASK GENMASK(3, 0) /* F4 */ -#define USART_CR2_ADDM7 BIT(4) /* F7 */ -#define USART_CR2_LBDL BIT(5) -#define USART_CR2_LBDIE BIT(6) -#define USART_CR2_LBCL BIT(8) -#define USART_CR2_CPHA BIT(9) -#define USART_CR2_CPOL BIT(10) -#define USART_CR2_CLKEN BIT(11) -#define USART_CR2_STOP_2B BIT(13) -#define USART_CR2_STOP_MASK GENMASK(13, 12) -#define USART_CR2_LINEN BIT(14) -#define USART_CR2_SWAP BIT(15) /* F7 */ -#define USART_CR2_RXINV BIT(16) /* F7 */ -#define USART_CR2_TXINV BIT(17) /* F7 */ -#define USART_CR2_DATAINV BIT(18) /* F7 */ -#define USART_CR2_MSBFIRST BIT(19) /* F7 */ -#define USART_CR2_ABREN BIT(20) /* F7 */ -#define USART_CR2_ABRMOD_MASK GENMASK(22, 21) /* F7 */ -#define USART_CR2_RTOEN BIT(23) /* F7 */ -#define USART_CR2_ADD_F7_MASK GENMASK(31, 24) /* F7 */ - -/* USART_CR3 */ -#define USART_CR3_EIE BIT(0) -#define USART_CR3_IREN BIT(1) -#define USART_CR3_IRLP BIT(2) -#define USART_CR3_HDSEL BIT(3) -#define USART_CR3_NACK BIT(4) -#define USART_CR3_SCEN BIT(5) -#define USART_CR3_DMAR BIT(6) -#define USART_CR3_DMAT BIT(7) -#define USART_CR3_RTSE BIT(8) -#define USART_CR3_CTSE BIT(9) -#define USART_CR3_CTSIE BIT(10) -#define USART_CR3_ONEBIT BIT(11) -#define USART_CR3_OVRDIS BIT(12) /* F7 */ -#define USART_CR3_DDRE BIT(13) /* F7 */ -#define USART_CR3_DEM BIT(14) /* F7 */ -#define USART_CR3_DEP BIT(15) /* F7 */ -#define USART_CR3_SCARCNT_MASK GENMASK(19, 17) /* F7 */ - -/* USART_GTPR */ -#define USART_GTPR_PSC_MASK GENMASK(7, 0) -#define USART_GTPR_GT_MASK GENMASK(15, 8) - -/* USART_RTOR */ -#define USART_RTOR_RTO_MASK GENMASK(23, 0) /* F7 */ -#define USART_RTOR_BLEN_MASK GENMASK(31, 24) /* F7 */ - -/* USART_RQR */ -#define USART_RQR_ABRRQ BIT(0) /* F7 */ -#define USART_RQR_SBKRQ BIT(1) /* F7 */ -#define USART_RQR_MMRQ BIT(2) /* F7 */ -#define USART_RQR_RXFRQ BIT(3) /* F7 */ -#define USART_RQR_TXFRQ BIT(4) /* F7 */ - -/* USART_ICR */ -#define USART_ICR_PECF BIT(0) /* F7 */ -#define USART_ICR_FFECF BIT(1) /* F7 */ -#define USART_ICR_NCF BIT(2) /* F7 */ -#define USART_ICR_ORECF BIT(3) /* F7 */ -#define USART_ICR_IDLECF BIT(4) /* F7 */ -#define USART_ICR_TCCF BIT(6) /* F7 */ -#define USART_ICR_LBDCF BIT(8) /* F7 */ -#define USART_ICR_CTSCF BIT(9) /* F7 */ -#define USART_ICR_RTOCF BIT(11) /* F7 */ -#define USART_ICR_EOBCF BIT(12) /* F7 */ -#define USART_ICR_CMCF BIT(17) /* F7 */ - -#define STM32_SERIAL_NAME "ttyS" -#define STM32_MAX_PORTS 6 - -struct stm32_port { - struct uart_port port; - struct clk *clk; - struct stm32_usart_info *info; - bool hw_flow_control; -}; - -static struct stm32_port stm32_ports[STM32_MAX_PORTS]; -static struct uart_driver stm32_usart_driver; +#include "stm32-usart.h" static void stm32_stop_tx(struct uart_port *port); diff --git a/drivers/tty/serial/stm32-usart.h b/drivers/tty/serial/stm32-usart.h new file mode 100644 index 000000000000..75f8388c91df --- /dev/null +++ b/drivers/tty/serial/stm32-usart.h @@ -0,0 +1,215 @@ +/* + * Copyright (C) Maxime Coquelin 2015 + * Authors: Maxime Coquelin + * Gerald Baeza + * License terms: GNU General Public License (GPL), version 2 + */ + +#define DRIVER_NAME "stm32-usart" + +struct stm32_usart_offsets { + u8 cr1; + u8 cr2; + u8 cr3; + u8 brr; + u8 gtpr; + u8 rtor; + u8 rqr; + u8 isr; + u8 icr; + u8 rdr; + u8 tdr; +}; + +struct stm32_usart_config { + u8 uart_enable_bit; /* USART_CR1_UE */ + bool has_7bits_data; +}; + +struct stm32_usart_info { + struct stm32_usart_offsets ofs; + struct stm32_usart_config cfg; +}; + +#define UNDEF_REG ~0 + +/* Register offsets */ +struct stm32_usart_info stm32f4_info = { + .ofs = { + .isr = 0x00, + .rdr = 0x04, + .tdr = 0x04, + .brr = 0x08, + .cr1 = 0x0c, + .cr2 = 0x10, + .cr3 = 0x14, + .gtpr = 0x18, + .rtor = UNDEF_REG, + .rqr = UNDEF_REG, + .icr = UNDEF_REG, + }, + .cfg = { + .uart_enable_bit = 13, + .has_7bits_data = false, + } +}; + +struct stm32_usart_info stm32f7_info = { + .ofs = { + .cr1 = 0x00, + .cr2 = 0x04, + .cr3 = 0x08, + .brr = 0x0c, + .gtpr = 0x10, + .rtor = 0x14, + .rqr = 0x18, + .isr = 0x1c, + .icr = 0x20, + .rdr = 0x24, + .tdr = 0x28, + }, + .cfg = { + .uart_enable_bit = 0, + .has_7bits_data = true, + } +}; + +/* USART_SR (F4) / USART_ISR (F7) */ +#define USART_SR_PE BIT(0) +#define USART_SR_FE BIT(1) +#define USART_SR_NF BIT(2) +#define USART_SR_ORE BIT(3) +#define USART_SR_IDLE BIT(4) +#define USART_SR_RXNE BIT(5) +#define USART_SR_TC BIT(6) +#define USART_SR_TXE BIT(7) +#define USART_SR_LBD BIT(8) +#define USART_SR_CTSIF BIT(9) +#define USART_SR_CTS BIT(10) /* F7 */ +#define USART_SR_RTOF BIT(11) /* F7 */ +#define USART_SR_EOBF BIT(12) /* F7 */ +#define USART_SR_ABRE BIT(14) /* F7 */ +#define USART_SR_ABRF BIT(15) /* F7 */ +#define USART_SR_BUSY BIT(16) /* F7 */ +#define USART_SR_CMF BIT(17) /* F7 */ +#define USART_SR_SBKF BIT(18) /* F7 */ +#define USART_SR_TEACK BIT(21) /* F7 */ +#define USART_SR_ERR_MASK (USART_SR_LBD | USART_SR_ORE | \ + USART_SR_FE | USART_SR_PE) +/* Dummy bits */ +#define USART_SR_DUMMY_RX BIT(16) + +/* USART_DR */ +#define USART_DR_MASK GENMASK(8, 0) + +/* USART_BRR */ +#define USART_BRR_DIV_F_MASK GENMASK(3, 0) +#define USART_BRR_DIV_M_MASK GENMASK(15, 4) +#define USART_BRR_DIV_M_SHIFT 4 + +/* USART_CR1 */ +#define USART_CR1_SBK BIT(0) +#define USART_CR1_RWU BIT(1) /* F4 */ +#define USART_CR1_RE BIT(2) +#define USART_CR1_TE BIT(3) +#define USART_CR1_IDLEIE BIT(4) +#define USART_CR1_RXNEIE BIT(5) +#define USART_CR1_TCIE BIT(6) +#define USART_CR1_TXEIE BIT(7) +#define USART_CR1_PEIE BIT(8) +#define USART_CR1_PS BIT(9) +#define USART_CR1_PCE BIT(10) +#define USART_CR1_WAKE BIT(11) +#define USART_CR1_M BIT(12) +#define USART_CR1_M0 BIT(12) /* F7 */ +#define USART_CR1_MME BIT(13) /* F7 */ +#define USART_CR1_CMIE BIT(14) /* F7 */ +#define USART_CR1_OVER8 BIT(15) +#define USART_CR1_DEDT_MASK GENMASK(20, 16) /* F7 */ +#define USART_CR1_DEAT_MASK GENMASK(25, 21) /* F7 */ +#define USART_CR1_RTOIE BIT(26) /* F7 */ +#define USART_CR1_EOBIE BIT(27) /* F7 */ +#define USART_CR1_M1 BIT(28) /* F7 */ +#define USART_CR1_IE_MASK (GENMASK(8, 4) | BIT(14) | BIT(26) | BIT(27)) + +/* USART_CR2 */ +#define USART_CR2_ADD_MASK GENMASK(3, 0) /* F4 */ +#define USART_CR2_ADDM7 BIT(4) /* F7 */ +#define USART_CR2_LBDL BIT(5) +#define USART_CR2_LBDIE BIT(6) +#define USART_CR2_LBCL BIT(8) +#define USART_CR2_CPHA BIT(9) +#define USART_CR2_CPOL BIT(10) +#define USART_CR2_CLKEN BIT(11) +#define USART_CR2_STOP_2B BIT(13) +#define USART_CR2_STOP_MASK GENMASK(13, 12) +#define USART_CR2_LINEN BIT(14) +#define USART_CR2_SWAP BIT(15) /* F7 */ +#define USART_CR2_RXINV BIT(16) /* F7 */ +#define USART_CR2_TXINV BIT(17) /* F7 */ +#define USART_CR2_DATAINV BIT(18) /* F7 */ +#define USART_CR2_MSBFIRST BIT(19) /* F7 */ +#define USART_CR2_ABREN BIT(20) /* F7 */ +#define USART_CR2_ABRMOD_MASK GENMASK(22, 21) /* F7 */ +#define USART_CR2_RTOEN BIT(23) /* F7 */ +#define USART_CR2_ADD_F7_MASK GENMASK(31, 24) /* F7 */ + +/* USART_CR3 */ +#define USART_CR3_EIE BIT(0) +#define USART_CR3_IREN BIT(1) +#define USART_CR3_IRLP BIT(2) +#define USART_CR3_HDSEL BIT(3) +#define USART_CR3_NACK BIT(4) +#define USART_CR3_SCEN BIT(5) +#define USART_CR3_DMAR BIT(6) +#define USART_CR3_DMAT BIT(7) +#define USART_CR3_RTSE BIT(8) +#define USART_CR3_CTSE BIT(9) +#define USART_CR3_CTSIE BIT(10) +#define USART_CR3_ONEBIT BIT(11) +#define USART_CR3_OVRDIS BIT(12) /* F7 */ +#define USART_CR3_DDRE BIT(13) /* F7 */ +#define USART_CR3_DEM BIT(14) /* F7 */ +#define USART_CR3_DEP BIT(15) /* F7 */ +#define USART_CR3_SCARCNT_MASK GENMASK(19, 17) /* F7 */ + +/* USART_GTPR */ +#define USART_GTPR_PSC_MASK GENMASK(7, 0) +#define USART_GTPR_GT_MASK GENMASK(15, 8) + +/* USART_RTOR */ +#define USART_RTOR_RTO_MASK GENMASK(23, 0) /* F7 */ +#define USART_RTOR_BLEN_MASK GENMASK(31, 24) /* F7 */ + +/* USART_RQR */ +#define USART_RQR_ABRRQ BIT(0) /* F7 */ +#define USART_RQR_SBKRQ BIT(1) /* F7 */ +#define USART_RQR_MMRQ BIT(2) /* F7 */ +#define USART_RQR_RXFRQ BIT(3) /* F7 */ +#define USART_RQR_TXFRQ BIT(4) /* F7 */ + +/* USART_ICR */ +#define USART_ICR_PECF BIT(0) /* F7 */ +#define USART_ICR_FFECF BIT(1) /* F7 */ +#define USART_ICR_NCF BIT(2) /* F7 */ +#define USART_ICR_ORECF BIT(3) /* F7 */ +#define USART_ICR_IDLECF BIT(4) /* F7 */ +#define USART_ICR_TCCF BIT(6) /* F7 */ +#define USART_ICR_LBDCF BIT(8) /* F7 */ +#define USART_ICR_CTSCF BIT(9) /* F7 */ +#define USART_ICR_RTOCF BIT(11) /* F7 */ +#define USART_ICR_EOBCF BIT(12) /* F7 */ +#define USART_ICR_CMCF BIT(17) /* F7 */ + +#define STM32_SERIAL_NAME "ttyS" +#define STM32_MAX_PORTS 6 + +struct stm32_port { + struct uart_port port; + struct clk *clk; + struct stm32_usart_info *info; + bool hw_flow_control; +}; + +static struct stm32_port stm32_ports[STM32_MAX_PORTS]; +static struct uart_driver stm32_usart_driver; -- cgit v1.2.3 From a14f66a427f8c960ca9cf1048f1bbf7607f85809 Mon Sep 17 00:00:00 2001 From: Alexandre TORGUE Date: Thu, 15 Sep 2016 18:42:36 +0200 Subject: serial: stm32: disable tx and rx during shutdown Signed-off-by: Gerald Baeza Signed-off-by: Alexandre TORGUE Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/stm32-usart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index 6236deb28905..184b0183bb38 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -276,7 +276,7 @@ static void stm32_shutdown(struct uart_port *port) u32 val; val = USART_CR1_TXEIE | USART_CR1_RXNEIE | USART_CR1_TE | USART_CR1_RE; - stm32_set_bits(port, ofs->cr1, val); + stm32_clr_bits(port, ofs->cr1, val); free_irq(port->irq, port); } -- cgit v1.2.3 From 59bed2dfe03e9e572bfb5a2d29effc1791eedcbc Mon Sep 17 00:00:00 2001 From: Alexandre TORGUE Date: Thu, 15 Sep 2016 18:42:37 +0200 Subject: serial: stm32: correct flow control property spelling "st,hw-flow-ctrl" property is documented in device tree binding whereas "auto-flow-control" was used in the code. The driver is now aligned with the binding name "st,hw-flow-ctrl". Signed-off-by: Gerald Baeza Signed-off-by: Alexandre TORGUE Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/stm32-usart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index 184b0183bb38..ab294b927b67 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -508,7 +508,7 @@ static struct stm32_port *stm32_of_get_stm32_port(struct platform_device *pdev) return NULL; stm32_ports[id].hw_flow_control = of_property_read_bool(np, - "auto-flow-control"); + "st,hw-flow-ctrl"); stm32_ports[id].port.line = id; return &stm32_ports[id]; } -- cgit v1.2.3 From 511c7b1baa8ae86dbcf83b8f489d39ff4b64a123 Mon Sep 17 00:00:00 2001 From: Alexandre TORGUE Date: Thu, 15 Sep 2016 18:42:38 +0200 Subject: serial: stm32: clock disabling management Keep the clock enabled at the end of stm32_init_port but disable it in stm32_serial_remove. Note that stm32_pm function is there to manage the clock at runtime. Signed-off-by: Gerald Baeza Signed-off-by: Alexandre TORGUE Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/stm32-usart.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index ab294b927b67..520e7defc08d 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -487,8 +487,6 @@ static int stm32_init_port(struct stm32_port *stm32port, if (!stm32port->port.uartclk) ret = -EINVAL; - clk_disable_unprepare(stm32port->clk); - return ret; } @@ -557,6 +555,9 @@ static int stm32_serial_probe(struct platform_device *pdev) static int stm32_serial_remove(struct platform_device *pdev) { struct uart_port *port = platform_get_drvdata(pdev); + struct stm32_port *stm32_port = to_stm32_port(port); + + clk_disable_unprepare(stm32_port->clk); return uart_remove_one_port(&stm32_usart_driver, port); } -- cgit v1.2.3 From 3489187204eb75e5635d8836babfd0a18be613f4 Mon Sep 17 00:00:00 2001 From: Alexandre TORGUE Date: Thu, 15 Sep 2016 18:42:40 +0200 Subject: serial: stm32: adding dma support This patch adds dma mode support for rx and tx with pio mode as fallback in case of dma error. Signed-off-by: Gerald Baeza Signed-off-by: Alexandre TORGUE Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/stm32-usart.c | 389 ++++++++++++++++++++++++++++++++++++--- drivers/tty/serial/stm32-usart.h | 14 ++ 2 files changed, 378 insertions(+), 25 deletions(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index 520e7defc08d..24c4d821472a 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -11,26 +11,31 @@ #define SUPPORT_SYSRQ #endif -#include -#include +#include #include -#include -#include +#include +#include +#include +#include #include +#include #include -#include -#include -#include -#include -#include +#include #include #include +#include +#include #include -#include +#include +#include +#include +#include +#include #include "stm32-usart.h" static void stm32_stop_tx(struct uart_port *port); +static void stm32_transmit_chars(struct uart_port *port); static inline struct stm32_port *to_stm32_port(struct uart_port *port) { @@ -55,7 +60,48 @@ static void stm32_clr_bits(struct uart_port *port, u32 reg, u32 bits) writel_relaxed(val, port->membase + reg); } -static void stm32_receive_chars(struct uart_port *port) +int stm32_pending_rx(struct uart_port *port, u32 *sr, int *last_res, + bool threaded) +{ + struct stm32_port *stm32_port = to_stm32_port(port); + struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; + enum dma_status status; + struct dma_tx_state state; + + *sr = readl_relaxed(port->membase + ofs->isr); + + if (threaded && stm32_port->rx_ch) { + status = dmaengine_tx_status(stm32_port->rx_ch, + stm32_port->rx_ch->cookie, + &state); + if ((status == DMA_IN_PROGRESS) && + (*last_res != state.residue)) + return 1; + else + return 0; + } else if (*sr & USART_SR_RXNE) { + return 1; + } + return 0; +} + +unsigned long stm32_get_char(struct uart_port *port, u32 *sr, int *last_res) +{ + struct stm32_port *stm32_port = to_stm32_port(port); + struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; + unsigned long c; + + if (stm32_port->rx_ch) { + c = stm32_port->rx_buf[RX_BUF_L - (*last_res)--]; + if ((*last_res) == 0) + *last_res = RX_BUF_L; + return c; + } else { + return readl_relaxed(port->membase + ofs->rdr); + } +} + +static void stm32_receive_chars(struct uart_port *port, bool threaded) { struct tty_port *tport = &port->state->port; struct stm32_port *stm32_port = to_stm32_port(port); @@ -63,13 +109,14 @@ static void stm32_receive_chars(struct uart_port *port) unsigned long c; u32 sr; char flag; + static int last_res = RX_BUF_L; if (port->irq_wake) pm_wakeup_event(tport->tty->dev, 0); - while ((sr = readl_relaxed(port->membase + ofs->isr)) & USART_SR_RXNE) { + while (stm32_pending_rx(port, &sr, &last_res, threaded)) { sr |= USART_SR_DUMMY_RX; - c = readl_relaxed(port->membase + ofs->rdr); + c = stm32_get_char(port, &sr, &last_res); flag = TTY_NORMAL; port->icount.rx++; @@ -110,6 +157,124 @@ static void stm32_receive_chars(struct uart_port *port) spin_lock(&port->lock); } +static void stm32_tx_dma_complete(void *arg) +{ + struct uart_port *port = arg; + struct stm32_port *stm32port = to_stm32_port(port); + struct stm32_usart_offsets *ofs = &stm32port->info->ofs; + unsigned int isr; + int ret; + + ret = readl_relaxed_poll_timeout_atomic(port->membase + ofs->isr, + isr, + (isr & USART_SR_TC), + 10, 100000); + + if (ret) + dev_err(port->dev, "terminal count not set\n"); + + if (ofs->icr == UNDEF_REG) + stm32_clr_bits(port, ofs->isr, USART_SR_TC); + else + stm32_set_bits(port, ofs->icr, USART_CR_TC); + + stm32_clr_bits(port, ofs->cr3, USART_CR3_DMAT); + stm32port->tx_dma_busy = false; + + /* Let's see if we have pending data to send */ + stm32_transmit_chars(port); +} + +static void stm32_transmit_chars_pio(struct uart_port *port) +{ + struct stm32_port *stm32_port = to_stm32_port(port); + struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; + struct circ_buf *xmit = &port->state->xmit; + unsigned int isr; + int ret; + + if (stm32_port->tx_dma_busy) { + stm32_clr_bits(port, ofs->cr3, USART_CR3_DMAT); + stm32_port->tx_dma_busy = false; + } + + ret = readl_relaxed_poll_timeout_atomic(port->membase + ofs->isr, + isr, + (isr & USART_SR_TXE), + 10, 100); + + if (ret) + dev_err(port->dev, "tx empty not set\n"); + + stm32_set_bits(port, ofs->cr1, USART_CR1_TXEIE); + + writel_relaxed(xmit->buf[xmit->tail], port->membase + ofs->tdr); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; +} + +static void stm32_transmit_chars_dma(struct uart_port *port) +{ + struct stm32_port *stm32port = to_stm32_port(port); + struct stm32_usart_offsets *ofs = &stm32port->info->ofs; + struct circ_buf *xmit = &port->state->xmit; + struct dma_async_tx_descriptor *desc = NULL; + dma_cookie_t cookie; + unsigned int count, i; + + if (stm32port->tx_dma_busy) + return; + + stm32port->tx_dma_busy = true; + + count = uart_circ_chars_pending(xmit); + + if (count > TX_BUF_L) + count = TX_BUF_L; + + if (xmit->tail < xmit->head) { + memcpy(&stm32port->tx_buf[0], &xmit->buf[xmit->tail], count); + } else { + size_t one = UART_XMIT_SIZE - xmit->tail; + size_t two; + + if (one > count) + one = count; + two = count - one; + + memcpy(&stm32port->tx_buf[0], &xmit->buf[xmit->tail], one); + if (two) + memcpy(&stm32port->tx_buf[one], &xmit->buf[0], two); + } + + desc = dmaengine_prep_slave_single(stm32port->tx_ch, + stm32port->tx_dma_buf, + count, + DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT); + + if (!desc) { + for (i = count; i > 0; i--) + stm32_transmit_chars_pio(port); + return; + } + + desc->callback = stm32_tx_dma_complete; + desc->callback_param = port; + + /* Push current DMA TX transaction in the pending queue */ + cookie = dmaengine_submit(desc); + + /* Issue pending DMA TX requests */ + dma_async_issue_pending(stm32port->tx_ch); + + stm32_clr_bits(port, ofs->isr, USART_SR_TC); + stm32_set_bits(port, ofs->cr3, USART_CR3_DMAT); + + xmit->tail = (xmit->tail + count) & (UART_XMIT_SIZE - 1); + port->icount.tx += count; +} + static void stm32_transmit_chars(struct uart_port *port) { struct stm32_port *stm32_port = to_stm32_port(port); @@ -117,9 +282,13 @@ static void stm32_transmit_chars(struct uart_port *port) struct circ_buf *xmit = &port->state->xmit; if (port->x_char) { + if (stm32_port->tx_dma_busy) + stm32_clr_bits(port, ofs->cr3, USART_CR3_DMAT); writel_relaxed(port->x_char, port->membase + ofs->tdr); port->x_char = 0; port->icount.tx++; + if (stm32_port->tx_dma_busy) + stm32_set_bits(port, ofs->cr3, USART_CR3_DMAT); return; } @@ -133,9 +302,10 @@ static void stm32_transmit_chars(struct uart_port *port) return; } - writel_relaxed(xmit->buf[xmit->tail], port->membase + ofs->tdr); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - port->icount.tx++; + if (stm32_port->tx_ch) + stm32_transmit_chars_dma(port); + else + stm32_transmit_chars_pio(port); if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(port); @@ -151,16 +321,30 @@ static irqreturn_t stm32_interrupt(int irq, void *ptr) struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; u32 sr; - spin_lock(&port->lock); - sr = readl_relaxed(port->membase + ofs->isr); - if (sr & USART_SR_RXNE) - stm32_receive_chars(port); + if ((sr & USART_SR_RXNE) && !(stm32_port->rx_ch)) + stm32_receive_chars(port, false); - if (sr & USART_SR_TXE) + if ((sr & USART_SR_TXE) && !(stm32_port->tx_ch)) stm32_transmit_chars(port); + if (stm32_port->rx_ch) + return IRQ_WAKE_THREAD; + else + return IRQ_HANDLED; +} + +static irqreturn_t stm32_threaded_interrupt(int irq, void *ptr) +{ + struct uart_port *port = ptr; + struct stm32_port *stm32_port = to_stm32_port(port); + + spin_lock(&port->lock); + + if (stm32_port->rx_ch) + stm32_receive_chars(port, true); + spin_unlock(&port->lock); return IRQ_HANDLED; @@ -203,14 +387,12 @@ static void stm32_stop_tx(struct uart_port *port) /* There are probably characters waiting to be transmitted. */ static void stm32_start_tx(struct uart_port *port) { - struct stm32_port *stm32_port = to_stm32_port(port); - struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; struct circ_buf *xmit = &port->state->xmit; if (uart_circ_empty(xmit)) return; - stm32_set_bits(port, ofs->cr1, USART_CR1_TXEIE | USART_CR1_TE); + stm32_transmit_chars(port); } /* Throttle the remote when input buffer is about to overflow. */ @@ -259,7 +441,9 @@ static int stm32_startup(struct uart_port *port) u32 val; int ret; - ret = request_irq(port->irq, stm32_interrupt, 0, name, port); + ret = request_threaded_irq(port->irq, stm32_interrupt, + stm32_threaded_interrupt, + IRQF_NO_SUSPEND, name, port); if (ret) return ret; @@ -376,6 +560,9 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios, if ((termios->c_cflag & CREAD) == 0) port->ignore_status_mask |= USART_SR_DUMMY_RX; + if (stm32_port->rx_ch) + cr3 |= USART_CR3_DMAR; + writel_relaxed(cr3, port->membase + ofs->cr3); writel_relaxed(cr2, port->membase + ofs->cr2); writel_relaxed(cr1, port->membase + ofs->cr1); @@ -523,6 +710,129 @@ static const struct of_device_id stm32_match[] = { MODULE_DEVICE_TABLE(of, stm32_match); #endif +static int stm32_of_dma_rx_probe(struct stm32_port *stm32port, + struct platform_device *pdev) +{ + struct stm32_usart_offsets *ofs = &stm32port->info->ofs; + struct uart_port *port = &stm32port->port; + struct device *dev = &pdev->dev; + struct dma_slave_config config; + struct dma_async_tx_descriptor *desc = NULL; + dma_cookie_t cookie; + int ret; + + /* Request DMA RX channel */ + stm32port->rx_ch = dma_request_slave_channel(dev, "rx"); + if (!stm32port->rx_ch) { + dev_info(dev, "rx dma alloc failed\n"); + return -ENODEV; + } + stm32port->rx_buf = dma_alloc_coherent(&pdev->dev, RX_BUF_L, + &stm32port->rx_dma_buf, + GFP_KERNEL); + if (!stm32port->rx_buf) { + ret = -ENOMEM; + goto alloc_err; + } + + /* Configure DMA channel */ + memset(&config, 0, sizeof(config)); + config.src_addr = (dma_addr_t)port->membase + ofs->rdr; + config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + + ret = dmaengine_slave_config(stm32port->rx_ch, &config); + if (ret < 0) { + dev_err(dev, "rx dma channel config failed\n"); + ret = -ENODEV; + goto config_err; + } + + /* Prepare a DMA cyclic transaction */ + desc = dmaengine_prep_dma_cyclic(stm32port->rx_ch, + stm32port->rx_dma_buf, + RX_BUF_L, RX_BUF_P, DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT); + if (!desc) { + dev_err(dev, "rx dma prep cyclic failed\n"); + ret = -ENODEV; + goto config_err; + } + + /* No callback as dma buffer is drained on usart interrupt */ + desc->callback = NULL; + desc->callback_param = NULL; + + /* Push current DMA transaction in the pending queue */ + cookie = dmaengine_submit(desc); + + /* Issue pending DMA requests */ + dma_async_issue_pending(stm32port->rx_ch); + + return 0; + +config_err: + dma_free_coherent(&pdev->dev, + RX_BUF_L, stm32port->rx_buf, + stm32port->rx_dma_buf); + +alloc_err: + dma_release_channel(stm32port->rx_ch); + stm32port->rx_ch = NULL; + + return ret; +} + +static int stm32_of_dma_tx_probe(struct stm32_port *stm32port, + struct platform_device *pdev) +{ + struct stm32_usart_offsets *ofs = &stm32port->info->ofs; + struct uart_port *port = &stm32port->port; + struct device *dev = &pdev->dev; + struct dma_slave_config config; + int ret; + + stm32port->tx_dma_busy = false; + + /* Request DMA TX channel */ + stm32port->tx_ch = dma_request_slave_channel(dev, "tx"); + if (!stm32port->tx_ch) { + dev_info(dev, "tx dma alloc failed\n"); + return -ENODEV; + } + stm32port->tx_buf = dma_alloc_coherent(&pdev->dev, TX_BUF_L, + &stm32port->tx_dma_buf, + GFP_KERNEL); + if (!stm32port->tx_buf) { + ret = -ENOMEM; + goto alloc_err; + } + + /* Configure DMA channel */ + memset(&config, 0, sizeof(config)); + config.dst_addr = (dma_addr_t)port->membase + ofs->tdr; + config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + + ret = dmaengine_slave_config(stm32port->tx_ch, &config); + if (ret < 0) { + dev_err(dev, "tx dma channel config failed\n"); + ret = -ENODEV; + goto config_err; + } + + return 0; + +config_err: + dma_free_coherent(&pdev->dev, + TX_BUF_L, stm32port->tx_buf, + stm32port->tx_dma_buf); + +alloc_err: + dma_release_channel(stm32port->tx_ch); + stm32port->tx_ch = NULL; + + return ret; +} + static int stm32_serial_probe(struct platform_device *pdev) { const struct of_device_id *match; @@ -547,6 +857,14 @@ static int stm32_serial_probe(struct platform_device *pdev) if (ret) return ret; + ret = stm32_of_dma_rx_probe(stm32port, pdev); + if (ret) + dev_info(&pdev->dev, "interrupt mode used for rx (no dma)\n"); + + ret = stm32_of_dma_tx_probe(stm32port, pdev); + if (ret) + dev_info(&pdev->dev, "interrupt mode used for tx (no dma)\n"); + platform_set_drvdata(pdev, &stm32port->port); return 0; @@ -556,6 +874,27 @@ static int stm32_serial_remove(struct platform_device *pdev) { struct uart_port *port = platform_get_drvdata(pdev); struct stm32_port *stm32_port = to_stm32_port(port); + struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; + + stm32_clr_bits(port, ofs->cr3, USART_CR3_DMAR); + + if (stm32_port->rx_ch) + dma_release_channel(stm32_port->rx_ch); + + if (stm32_port->rx_dma_buf) + dma_free_coherent(&pdev->dev, + RX_BUF_L, stm32_port->rx_buf, + stm32_port->rx_dma_buf); + + stm32_clr_bits(port, ofs->cr3, USART_CR3_DMAT); + + if (stm32_port->tx_ch) + dma_release_channel(stm32_port->tx_ch); + + if (stm32_port->tx_dma_buf) + dma_free_coherent(&pdev->dev, + TX_BUF_L, stm32_port->tx_buf, + stm32_port->tx_dma_buf); clk_disable_unprepare(stm32_port->clk); diff --git a/drivers/tty/serial/stm32-usart.h b/drivers/tty/serial/stm32-usart.h index 75f8388c91df..41d974923102 100644 --- a/drivers/tty/serial/stm32-usart.h +++ b/drivers/tty/serial/stm32-usart.h @@ -99,6 +99,9 @@ struct stm32_usart_info stm32f7_info = { /* Dummy bits */ #define USART_SR_DUMMY_RX BIT(16) +/* USART_ICR (F7) */ +#define USART_CR_TC BIT(6) + /* USART_DR */ #define USART_DR_MASK GENMASK(8, 0) @@ -204,10 +207,21 @@ struct stm32_usart_info stm32f7_info = { #define STM32_SERIAL_NAME "ttyS" #define STM32_MAX_PORTS 6 +#define RX_BUF_L 200 /* dma rx buffer length */ +#define RX_BUF_P RX_BUF_L /* dma rx buffer period */ +#define TX_BUF_L 200 /* dma tx buffer length */ + struct stm32_port { struct uart_port port; struct clk *clk; struct stm32_usart_info *info; + struct dma_chan *rx_ch; /* dma rx channel */ + dma_addr_t rx_dma_buf; /* dma rx buffer bus address */ + unsigned char *rx_buf; /* dma rx buffer cpu address */ + struct dma_chan *tx_ch; /* dma tx channel */ + dma_addr_t tx_dma_buf; /* dma tx buffer bus address */ + unsigned char *tx_buf; /* dma tx buffer cpu address */ + bool tx_dma_busy; /* dma tx busy */ bool hw_flow_control; }; -- cgit v1.2.3 From 01d32d71610b0c56ce2c56bec370e275607a96e7 Mon Sep 17 00:00:00 2001 From: Alexandre TORGUE Date: Thu, 15 Sep 2016 18:42:41 +0200 Subject: serial: stm32: fix spin_lock management Signed-off-by: Gerald Baeza Signed-off-by: Alexandre TORGUE Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/stm32-usart.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index 24c4d821472a..3b99d790fb3f 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -321,6 +321,8 @@ static irqreturn_t stm32_interrupt(int irq, void *ptr) struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; u32 sr; + spin_lock(&port->lock); + sr = readl_relaxed(port->membase + ofs->isr); if ((sr & USART_SR_RXNE) && !(stm32_port->rx_ch)) @@ -329,6 +331,8 @@ static irqreturn_t stm32_interrupt(int irq, void *ptr) if ((sr & USART_SR_TXE) && !(stm32_port->tx_ch)) stm32_transmit_chars(port); + spin_unlock(&port->lock); + if (stm32_port->rx_ch) return IRQ_WAKE_THREAD; else -- cgit v1.2.3 From 87f1f809c9b90903e6a9ee0a8356a2303fd4270d Mon Sep 17 00:00:00 2001 From: Alexandre TORGUE Date: Thu, 15 Sep 2016 18:42:42 +0200 Subject: serial: stm32: fix uart enable management Signed-off-by: Gerald Baeza Signed-off-by: Alexandre TORGUE Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/stm32-usart.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index 3b99d790fb3f..4d3001b77e7e 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -461,9 +461,11 @@ static void stm32_shutdown(struct uart_port *port) { struct stm32_port *stm32_port = to_stm32_port(port); struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; + struct stm32_usart_config *cfg = &stm32_port->info->cfg; u32 val; val = USART_CR1_TXEIE | USART_CR1_RXNEIE | USART_CR1_TE | USART_CR1_RE; + val |= BIT(cfg->uart_enable_bit); stm32_clr_bits(port, ofs->cr1, val); free_irq(port->irq, port); @@ -923,6 +925,7 @@ static void stm32_console_write(struct console *co, const char *s, unsigned cnt) struct uart_port *port = &stm32_ports[co->index].port; struct stm32_port *stm32_port = to_stm32_port(port); struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; + struct stm32_usart_config *cfg = &stm32_port->info->cfg; unsigned long flags; u32 old_cr1, new_cr1; int locked = 1; @@ -935,9 +938,10 @@ static void stm32_console_write(struct console *co, const char *s, unsigned cnt) else spin_lock(&port->lock); - /* Save and disable interrupts */ + /* Save and disable interrupts, enable the transmitter */ old_cr1 = readl_relaxed(port->membase + ofs->cr1); new_cr1 = old_cr1 & ~USART_CR1_IE_MASK; + new_cr1 |= USART_CR1_TE | BIT(cfg->uart_enable_bit); writel_relaxed(new_cr1, port->membase + ofs->cr1); uart_console_write(port, s, cnt, stm32_console_putchar); -- cgit v1.2.3 From a3081893cab48df3facff8144d9506610213241a Mon Sep 17 00:00:00 2001 From: Anirudha Sarangi Date: Wed, 21 Sep 2016 15:01:18 +0100 Subject: serial: xuartps: Do not enable parity error interrupt The patch makes changes not to enable parity error interrupt. With the current implementation, each parity error results in two distinct interrupts (almost always). The first one is normal parity error interrupt with no data in the fifo and the second one is a proper Rx interrupt with the received data in the fifo. By disabling parity error interrupt we still ensure handling of parity errors as for the Rx fifo interrupt the parity error still shows up in the interrupt status register. Considering the fact that the by default INPCK and IGNPAR are not set, this is the optimal implementation for parity error handling. Signed-off-by: Anirudha Sarangi Signed-off-by: Michal Simek [stelford@cadence.com: cherry picked from https://github.com/Xilinx/linux-xlnx commit bf9f610b445e2c9ed33c41e1e0e30b43be4e1f97 with manual conflict resolution] Signed-off-by: Scott Telford Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/xilinx_uartps.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index e0c6a8619b9f..98f7b590ec55 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -130,8 +130,21 @@ MODULE_PARM_DESC(rx_timeout, "Rx timeout, 1-255"); #define CDNS_UART_IXR_RXEMPTY 0x00000002 /* RX FIFO empty interrupt. */ #define CDNS_UART_IXR_MASK 0x00001FFF /* Valid bit mask */ -#define CDNS_UART_RX_IRQS (CDNS_UART_IXR_PARITY | CDNS_UART_IXR_FRAMING | \ - CDNS_UART_IXR_OVERRUN | CDNS_UART_IXR_RXTRIG | \ + /* + * Do not enable parity error interrupt for the following + * reason: When parity error interrupt is enabled, each Rx + * parity error always results in 2 events. The first one + * being parity error interrupt and the second one with a + * proper Rx interrupt with the incoming data. Disabling + * parity error interrupt ensures better handling of parity + * error events. With this change, for a parity error case, we + * get a Rx interrupt with parity error set in ISR register + * and we still handle parity errors in the desired way. + */ + +#define CDNS_UART_RX_IRQS (CDNS_UART_IXR_FRAMING | \ + CDNS_UART_IXR_OVERRUN | \ + CDNS_UART_IXR_RXTRIG | \ CDNS_UART_IXR_TOUT) /* Goes in read_status_mask for break detection as the HW doesn't do it*/ -- cgit v1.2.3 From 36131cdfef5aef7f4a9a36423a7a338bd6f68ad6 Mon Sep 17 00:00:00 2001 From: Alexey Starikovskiy Date: Wed, 21 Sep 2016 12:44:14 +0200 Subject: tty/serial: atmel: fix fractional baud rate computation The problem with previous code was it rounded values in wrong place and produced wrong baud rate in some cases. Signed-off-by: Alexey Starikovskiy [nicolas.ferre@atmel.com: port to newer kernel and add commit log] Signed-off-by: Nicolas Ferre Reviewed-by: Boris Brezillon Reviewed-by: Uwe Kleine-König Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/atmel_serial.c | 10 ++++++---- include/linux/atmel_serial.h | 1 + 2 files changed, 7 insertions(+), 4 deletions(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index 5f550d9feed9..fd8aa1f4ba78 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -2170,13 +2170,15 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios, * accurately. This feature is enabled only when using normal mode. * baudrate = selected clock / (8 * (2 - OVER) * (CD + FP / 8)) * Currently, OVER is always set to 0 so we get - * baudrate = selected clock (16 * (CD + FP / 8)) + * baudrate = selected clock / (16 * (CD + FP / 8)) + * then + * 8 CD + FP = selected clock / (2 * baudrate) */ if (atmel_port->has_frac_baudrate && (mode & ATMEL_US_USMODE) == ATMEL_US_USMODE_NORMAL) { - div = DIV_ROUND_CLOSEST(port->uartclk, baud); - cd = div / 16; - fp = DIV_ROUND_CLOSEST(div % 16, 2); + div = DIV_ROUND_CLOSEST(port->uartclk, baud * 2); + cd = div >> 3; + fp = div & ATMEL_US_FP_MASK; } else { cd = uart_get_divisor(port, baud); } diff --git a/include/linux/atmel_serial.h b/include/linux/atmel_serial.h index f8e452aa48d7..bd2560502f3c 100644 --- a/include/linux/atmel_serial.h +++ b/include/linux/atmel_serial.h @@ -119,6 +119,7 @@ #define ATMEL_US_BRGR 0x20 /* Baud Rate Generator Register */ #define ATMEL_US_CD GENMASK(15, 0) /* Clock Divider */ #define ATMEL_US_FP_OFFSET 16 /* Fractional Part */ +#define ATMEL_US_FP_MASK 0x7 #define ATMEL_US_RTOR 0x24 /* Receiver Time-out Register for USART */ #define ATMEL_UA_RTOR 0x28 /* Receiver Time-out Register for UART */ -- cgit v1.2.3 From 8e5481d98bbf1de0ff06a3e488b668572d578e61 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 23 Sep 2016 21:38:51 +0200 Subject: serial: stm32: use mapbase instead of membase for DMA Building this driver with a 64-bit dma_addr_t type results in a compiler warning: drivers/tty/serial/stm32-usart.c: In function 'stm32_of_dma_rx_probe': drivers/tty/serial/stm32-usart.c:746:20: error: cast from pointer to integer of different size [-Werror=pointer-to-int-cast] drivers/tty/serial/stm32-usart.c: In function 'stm32_of_dma_tx_probe': drivers/tty/serial/stm32-usart.c:818:20: error: cast from pointer to integer of different size [-Werror=pointer-to-int-cast] While the type conversion here is harmless, this hints at a different problem: we pass an __iomem pointer into a DMA engine, which expects a phys_addr_t. This happens to work because stm32 has no MMU and ioremap() is an identity mapping here, but it's still an incorrect API use. Using dma_addr_t is doubly wrong here, because that would be the result of dma_map_single() rather than the physical address. Using the mapbase instead fixes multiple issues: - the warning is gone - we don't go through ioremap in error - the cast is gone, making it use the correct resource_size_t/phys_addr_t type in the process. Fixes: 3489187204eb ("serial: stm32: adding dma support") Signed-off-by: Arnd Bergmann Reviewed-by: Gerald Baeza Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/stm32-usart.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index 4d3001b77e7e..2adb678a863b 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -743,7 +743,7 @@ static int stm32_of_dma_rx_probe(struct stm32_port *stm32port, /* Configure DMA channel */ memset(&config, 0, sizeof(config)); - config.src_addr = (dma_addr_t)port->membase + ofs->rdr; + config.src_addr = port->mapbase + ofs->rdr; config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; ret = dmaengine_slave_config(stm32port->rx_ch, &config); @@ -815,7 +815,7 @@ static int stm32_of_dma_tx_probe(struct stm32_port *stm32port, /* Configure DMA channel */ memset(&config, 0, sizeof(config)); - config.dst_addr = (dma_addr_t)port->membase + ofs->tdr; + config.dst_addr = port->mapbase + ofs->tdr; config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; ret = dmaengine_slave_config(stm32port->tx_ch, &config); -- cgit v1.2.3 From c8dbdc842d30618e4f7e315e3b0e6c43de7915f3 Mon Sep 17 00:00:00 2001 From: Anirudha Sarangi Date: Thu, 22 Sep 2016 16:58:14 +0100 Subject: serial: xuartps: Rewrite the interrupt handling logic The existing interrupt handling logic has following issues. - Upon a parity error with default configuration, the control never comes out of the ISR thereby hanging Linux. - The error handling logic around framing and parity error are buggy. There are chances that the errors will never be captured. This patch ensures that the status registers are cleared on all cases so that a hang situation never arises. Signed-off-by: Anirudha Sarangi Signed-off-by: Michal Simek [stelford@cadence.com: cherry picked from https://github.com/Xilinx/linux-xlnx commit ac297e20d399850d7a8e373b6eccf2e183c15165 with manual conflict resolution] Signed-off-by: Scott Telford Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/xilinx_uartps.c | 219 +++++++++++++++++++------------------ 1 file changed, 114 insertions(+), 105 deletions(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index 98f7b590ec55..511999755f5c 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -198,58 +198,43 @@ struct cdns_platform_data { #define to_cdns_uart(_nb) container_of(_nb, struct cdns_uart, \ clk_rate_change_nb); -static void cdns_uart_handle_rx(struct uart_port *port, unsigned int isrstatus) +/** + * cdns_uart_handle_rx - Handle the received bytes along with Rx errors. + * @dev_id: Id of the UART port + * @isrstatus: The interrupt status register value as read + * Return: None + */ +static void cdns_uart_handle_rx(void *dev_id, unsigned int isrstatus) { + struct uart_port *port = (struct uart_port *)dev_id; struct cdns_uart *cdns_uart = port->private_data; - bool is_rxbs_support; + unsigned int data; unsigned int rxbs_status = 0; unsigned int status_mask; + unsigned int framerrprocessed = 0; + char status = TTY_NORMAL; + bool is_rxbs_support; is_rxbs_support = cdns_uart->quirks & CDNS_UART_RXBS_SUPPORT; - /* - * There is no hardware break detection, so we interpret framing - * error with all-zeros data as a break sequence. Most of the time, - * there's another non-zero byte at the end of the sequence. - */ - if (!is_rxbs_support && (isrstatus & CDNS_UART_IXR_FRAMING)) { - while (!(readl(port->membase + CDNS_UART_SR) & - CDNS_UART_SR_RXEMPTY)) { - if (!readl(port->membase + CDNS_UART_FIFO)) { - port->read_status_mask |= CDNS_UART_IXR_BRK; - isrstatus &= ~CDNS_UART_IXR_FRAMING; - } - } - writel(CDNS_UART_IXR_FRAMING, port->membase + CDNS_UART_ISR); - } - - /* drop byte with parity error if IGNPAR specified */ - if (isrstatus & port->ignore_status_mask & CDNS_UART_IXR_PARITY) - isrstatus &= ~(CDNS_UART_IXR_RXTRIG | CDNS_UART_IXR_TOUT); - - isrstatus &= port->read_status_mask; - isrstatus &= ~port->ignore_status_mask; - status_mask = port->read_status_mask; - status_mask &= ~port->ignore_status_mask; - - if (!(isrstatus & (CDNS_UART_IXR_TOUT | CDNS_UART_IXR_RXTRIG))) - return; - - while (!(readl(port->membase + CDNS_UART_SR) & CDNS_UART_SR_RXEMPTY)) { - u32 data; - char status = TTY_NORMAL; - + while ((readl(port->membase + CDNS_UART_SR) & + CDNS_UART_SR_RXEMPTY) != CDNS_UART_SR_RXEMPTY) { if (is_rxbs_support) rxbs_status = readl(port->membase + CDNS_UART_RXBS); - data = readl(port->membase + CDNS_UART_FIFO); - - /* Non-NULL byte after BREAK is garbage (99%) */ - if (data && (port->read_status_mask & CDNS_UART_IXR_BRK)) { - port->read_status_mask &= ~CDNS_UART_IXR_BRK; - port->icount.brk++; - if (uart_handle_break(port)) + port->icount.rx++; + /* + * There is no hardware break detection in Zynq, so we interpret + * framing error with all-zeros data as a break sequence. + * Most of the time, there's another non-zero byte at the + * end of the sequence. + */ + if (!is_rxbs_support && (isrstatus & CDNS_UART_IXR_FRAMING)) { + if (!data) { + port->read_status_mask |= CDNS_UART_IXR_BRK; + framerrprocessed = 1; continue; + } } if (is_rxbs_support && (rxbs_status & CDNS_UART_RXBS_BRK)) { port->icount.brk++; @@ -258,74 +243,101 @@ static void cdns_uart_handle_rx(struct uart_port *port, unsigned int isrstatus) continue; } - if (uart_handle_sysrq_char(port, data)) - continue; + isrstatus &= port->read_status_mask; + isrstatus &= ~port->ignore_status_mask; + status_mask = port->read_status_mask; + status_mask &= ~port->ignore_status_mask; + + if ((isrstatus & CDNS_UART_IXR_TOUT) || + (isrstatus & CDNS_UART_IXR_RXTRIG)) { + if (data && + (port->read_status_mask & CDNS_UART_IXR_BRK)) { + port->read_status_mask &= ~CDNS_UART_IXR_BRK; + port->icount.brk++; + if (uart_handle_break(port)) + continue; + } - port->icount.rx++; + if (uart_handle_sysrq_char(port, data)) + continue; - if (is_rxbs_support) { - if ((rxbs_status & CDNS_UART_RXBS_PARITY) - && (status_mask & CDNS_UART_IXR_PARITY)) { - port->icount.parity++; - status = TTY_PARITY; + if (is_rxbs_support) { + if ((rxbs_status & CDNS_UART_RXBS_PARITY) + && (status_mask & CDNS_UART_IXR_PARITY)) { + port->icount.parity++; + status = TTY_PARITY; + } + if ((rxbs_status & CDNS_UART_RXBS_FRAMING) + && (status_mask & CDNS_UART_IXR_PARITY)) { + port->icount.frame++; + status = TTY_FRAME; + } + } else { + if (isrstatus & CDNS_UART_IXR_PARITY) { + port->icount.parity++; + status = TTY_PARITY; + } + if ((isrstatus & CDNS_UART_IXR_FRAMING) && + !framerrprocessed) { + port->icount.frame++; + status = TTY_FRAME; + } } - if ((rxbs_status & CDNS_UART_RXBS_FRAMING) - && (status_mask & CDNS_UART_IXR_PARITY)) { - port->icount.frame++; - status = TTY_FRAME; - } - } else { - if (isrstatus & CDNS_UART_IXR_PARITY) { - port->icount.parity++; - status = TTY_PARITY; - } - if (isrstatus & CDNS_UART_IXR_FRAMING) { - port->icount.frame++; - status = TTY_FRAME; + if (isrstatus & CDNS_UART_IXR_OVERRUN) { + port->icount.overrun++; + tty_insert_flip_char(&port->state->port, 0, + TTY_OVERRUN); } + tty_insert_flip_char(&port->state->port, data, status); } - if (isrstatus & CDNS_UART_IXR_OVERRUN) - port->icount.overrun++; - - uart_insert_char(port, isrstatus, CDNS_UART_IXR_OVERRUN, - data, status); } + spin_unlock(&port->lock); tty_flip_buffer_push(&port->state->port); + spin_lock(&port->lock); } -static void cdns_uart_handle_tx(struct uart_port *port) +/** + * cdns_uart_handle_tx - Handle the bytes to be Txed. + * @dev_id: Id of the UART port + * Return: None + */ +static void cdns_uart_handle_tx(void *dev_id) { + struct uart_port *port = (struct uart_port *)dev_id; unsigned int numbytes; if (uart_circ_empty(&port->state->xmit)) { writel(CDNS_UART_IXR_TXEMPTY, port->membase + CDNS_UART_IDR); - return; - } - - numbytes = port->fifosize; - while (numbytes && !uart_circ_empty(&port->state->xmit) && - !(readl(port->membase + CDNS_UART_SR) & CDNS_UART_SR_TXFULL)) { - /* - * Get the data from the UART circular buffer - * and write it to the cdns_uart's TX_FIFO - * register. - */ - writel(port->state->xmit.buf[port->state->xmit.tail], - port->membase + CDNS_UART_FIFO); - port->icount.tx++; - - /* - * Adjust the tail of the UART buffer and wrap - * the buffer if it reaches limit. - */ - port->state->xmit.tail = - (port->state->xmit.tail + 1) & (UART_XMIT_SIZE - 1); + } else { + numbytes = port->fifosize; + while (numbytes && !uart_circ_empty(&port->state->xmit) && + !(readl(port->membase + CDNS_UART_SR) & CDNS_UART_SR_TXFULL)) { + /* + * Get the data from the UART circular buffer + * and write it to the cdns_uart's TX_FIFO + * register. + */ + writel( + port->state->xmit.buf[port->state->xmit. + tail], port->membase + CDNS_UART_FIFO); + + port->icount.tx++; + + /* + * Adjust the tail of the UART buffer and wrap + * the buffer if it reaches limit. + */ + port->state->xmit.tail = + (port->state->xmit.tail + 1) & + (UART_XMIT_SIZE - 1); + + numbytes--; + } - numbytes--; + if (uart_circ_chars_pending( + &port->state->xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); } - - if (uart_circ_chars_pending(&port->state->xmit) < WAKEUP_CHARS) - uart_write_wakeup(port); } /** @@ -338,27 +350,24 @@ static void cdns_uart_handle_tx(struct uart_port *port) static irqreturn_t cdns_uart_isr(int irq, void *dev_id) { struct uart_port *port = (struct uart_port *)dev_id; - unsigned long flags; unsigned int isrstatus; - spin_lock_irqsave(&port->lock, flags); + spin_lock(&port->lock); /* Read the interrupt status register to determine which - * interrupt(s) is/are active. + * interrupt(s) is/are active and clear them. */ isrstatus = readl(port->membase + CDNS_UART_ISR); - - if (isrstatus & CDNS_UART_RX_IRQS) - cdns_uart_handle_rx(port, isrstatus); - - if ((isrstatus & CDNS_UART_IXR_TXEMPTY) == CDNS_UART_IXR_TXEMPTY) - cdns_uart_handle_tx(port); - writel(isrstatus, port->membase + CDNS_UART_ISR); - /* be sure to release the lock and tty before leaving */ - spin_unlock_irqrestore(&port->lock, flags); + if (isrstatus & CDNS_UART_IXR_TXEMPTY) { + cdns_uart_handle_tx(dev_id); + isrstatus &= ~CDNS_UART_IXR_TXEMPTY; + } + if (isrstatus & CDNS_UART_IXR_MASK) + cdns_uart_handle_rx(dev_id, isrstatus); + spin_unlock(&port->lock); return IRQ_HANDLED; } -- cgit v1.2.3 From 212d249b6acbe00ac3617938433ce785c2d68e88 Mon Sep 17 00:00:00 2001 From: Nava kishore Manne Date: Thu, 22 Sep 2016 16:58:15 +0100 Subject: serial: xuartps: Removed unwanted checks while reading the error conditions This patch Remove the unwated checks while reading the parity,framing, overrun and Break detection errors. Signed-off-by: Nava kishore Manne Signed-off-by: Michal Simek [stelford@cadence.com: cherry picked from https://github.com/Xilinx/linux-xlnx commit b1cf74970df5470ffbc8e7876a9edf5e3498ef94] Signed-off-by: Scott Telford Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/xilinx_uartps.c | 72 ++++++++++++++++++-------------------- 1 file changed, 35 insertions(+), 37 deletions(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index 511999755f5c..39f41bfd7c22 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -248,48 +248,46 @@ static void cdns_uart_handle_rx(void *dev_id, unsigned int isrstatus) status_mask = port->read_status_mask; status_mask &= ~port->ignore_status_mask; - if ((isrstatus & CDNS_UART_IXR_TOUT) || - (isrstatus & CDNS_UART_IXR_RXTRIG)) { - if (data && - (port->read_status_mask & CDNS_UART_IXR_BRK)) { - port->read_status_mask &= ~CDNS_UART_IXR_BRK; - port->icount.brk++; - if (uart_handle_break(port)) - continue; - } - - if (uart_handle_sysrq_char(port, data)) + if (data && + (port->read_status_mask & CDNS_UART_IXR_BRK)) { + port->read_status_mask &= ~CDNS_UART_IXR_BRK; + port->icount.brk++; + if (uart_handle_break(port)) continue; + } - if (is_rxbs_support) { - if ((rxbs_status & CDNS_UART_RXBS_PARITY) - && (status_mask & CDNS_UART_IXR_PARITY)) { - port->icount.parity++; - status = TTY_PARITY; - } - if ((rxbs_status & CDNS_UART_RXBS_FRAMING) - && (status_mask & CDNS_UART_IXR_PARITY)) { - port->icount.frame++; - status = TTY_FRAME; - } - } else { - if (isrstatus & CDNS_UART_IXR_PARITY) { - port->icount.parity++; - status = TTY_PARITY; - } - if ((isrstatus & CDNS_UART_IXR_FRAMING) && - !framerrprocessed) { - port->icount.frame++; - status = TTY_FRAME; - } + if (uart_handle_sysrq_char(port, data)) + continue; + + if (is_rxbs_support) { + if ((rxbs_status & CDNS_UART_RXBS_PARITY) + && (status_mask & CDNS_UART_IXR_PARITY)) { + port->icount.parity++; + status = TTY_PARITY; + } + if ((rxbs_status & CDNS_UART_RXBS_FRAMING) + && (status_mask & CDNS_UART_IXR_PARITY)) { + port->icount.frame++; + status = TTY_FRAME; } - if (isrstatus & CDNS_UART_IXR_OVERRUN) { - port->icount.overrun++; - tty_insert_flip_char(&port->state->port, 0, - TTY_OVERRUN); + } else { + if (isrstatus & CDNS_UART_IXR_PARITY) { + port->icount.parity++; + status = TTY_PARITY; } - tty_insert_flip_char(&port->state->port, data, status); + if ((isrstatus & CDNS_UART_IXR_FRAMING) && + !framerrprocessed) { + port->icount.frame++; + status = TTY_FRAME; + } + } + if (isrstatus & CDNS_UART_IXR_OVERRUN) { + port->icount.overrun++; + tty_insert_flip_char(&port->state->port, 0, + TTY_OVERRUN); } + tty_insert_flip_char(&port->state->port, data, status); + isrstatus = 0; } spin_unlock(&port->lock); tty_flip_buffer_push(&port->state->port); -- cgit v1.2.3 From c41251b17563234371a9b376ed4914efa4bc079b Mon Sep 17 00:00:00 2001 From: Scott Telford Date: Thu, 22 Sep 2016 16:58:16 +0100 Subject: serial: xuartps: Add some register initialisation to cdns_early_console_setup() Add initialisation of control register and baud rate to cdns_early_console_setup(), required when running kernel standalone without a boot loader. Baud rate is only initialised when specified in earlycon command-line option, otherwise it is assumed this has been set by a boot loader. Updated Documentation/kernel-parameters.txt accordingly. Signed-off-by: Scott Telford Signed-off-by: Greg Kroah-Hartman --- Documentation/kernel-parameters.txt | 11 ++++++----- drivers/tty/serial/xilinx_uartps.c | 27 ++++++++++++++++++++++++++- 2 files changed, 32 insertions(+), 6 deletions(-) (limited to 'drivers/tty') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index a4f4d693e2c1..70b51f824a60 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1045,11 +1045,12 @@ bytes respectively. Such letter suffixes can also be entirely omitted. determined by the stdout-path property in device tree's chosen node. - cdns, - Start an early, polled-mode console on a cadence serial - port at the specified address. The cadence serial port - must already be setup and configured. Options are not - yet supported. + cdns,[,options] + Start an early, polled-mode console on a Cadence + (xuartps) serial port at the specified address. Only + supported option is baud rate. If baud rate is not + specified, the serial port must already be setup and + configured. uart[8250],io,[,options] uart[8250],mmio,[,options] diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index 39f41bfd7c22..f37edaa5ac75 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -1165,9 +1165,34 @@ static void __init cdns_early_write(struct console *con, const char *s, static int __init cdns_early_console_setup(struct earlycon_device *device, const char *opt) { - if (!device->port.membase) + struct uart_port *port = &device->port; + + if (!port->membase) return -ENODEV; + /* initialise control register */ + writel(CDNS_UART_CR_TX_EN|CDNS_UART_CR_TXRST|CDNS_UART_CR_RXRST, + port->membase + CDNS_UART_CR); + + /* only set baud if specified on command line - otherwise + * assume it has been initialized by a boot loader. + */ + if (device->baud) { + u32 cd = 0, bdiv = 0; + u32 mr; + int div8; + + cdns_uart_calc_baud_divs(port->uartclk, device->baud, + &bdiv, &cd, &div8); + mr = CDNS_UART_MR_PARITY_NONE; + if (div8) + mr |= CDNS_UART_MR_CLKSEL; + + writel(mr, port->membase + CDNS_UART_MR); + writel(cd, port->membase + CDNS_UART_BAUDGEN); + writel(bdiv, port->membase + CDNS_UART_BAUDDIV); + } + device->con->write = cdns_early_write; return 0; -- cgit v1.2.3 From b97055bcf17551196a498e624b544c3e9c804ef0 Mon Sep 17 00:00:00 2001 From: Baoyou Xie Date: Mon, 26 Sep 2016 19:58:56 +0800 Subject: serial: stm32: mark symbols static where possible We get 2 warnings when building kernel with W=1: drivers/tty/serial/stm32-usart.c:63:5: warning: no previous prototype for 'stm32_pending_rx' [-Wmissing-prototypes] drivers/tty/serial/stm32-usart.c:88:15: warning: no previous prototype for 'stm32_get_char' [-Wmissing-prototypes] In fact, these two functions are only used in the file in which they are declared and don't need a declaration, but can be made static. So this patch marks these functions with 'static'. Signed-off-by: Baoyou Xie Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/stm32-usart.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index 2adb678a863b..033856287ca2 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -60,8 +60,8 @@ static void stm32_clr_bits(struct uart_port *port, u32 reg, u32 bits) writel_relaxed(val, port->membase + reg); } -int stm32_pending_rx(struct uart_port *port, u32 *sr, int *last_res, - bool threaded) +static int stm32_pending_rx(struct uart_port *port, u32 *sr, int *last_res, + bool threaded) { struct stm32_port *stm32_port = to_stm32_port(port); struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; @@ -85,7 +85,8 @@ int stm32_pending_rx(struct uart_port *port, u32 *sr, int *last_res, return 0; } -unsigned long stm32_get_char(struct uart_port *port, u32 *sr, int *last_res) +static unsigned long +stm32_get_char(struct uart_port *port, u32 *sr, int *last_res) { struct stm32_port *stm32_port = to_stm32_port(port); struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; -- cgit v1.2.3 From 4b75f80003617fe35771a9e27022e8fbd6a41875 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Mon, 26 Sep 2016 15:55:31 +0200 Subject: serial: imx: Fix DCD reading The USR2_DCDIN bit is tested for in register usr1. As the name suggests the usr2 register should be used instead. This fixes reading the Carrier detect status. Signed-off-by: Sascha Hauer Fixes: 90ebc4838666 ("serial: imx: repair and complete handshaking") Acked-by: Uwe Kleine-König Cc: # 4.5+ Reviewed-by: Fabio Estevam Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/imx.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index a1d8174a098b..a70356dad1b7 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -763,12 +763,13 @@ static unsigned int imx_get_hwmctrl(struct imx_port *sport) { unsigned int tmp = TIOCM_DSR; unsigned usr1 = readl(sport->port.membase + USR1); + unsigned usr2 = readl(sport->port.membase + USR2); if (usr1 & USR1_RTSS) tmp |= TIOCM_CTS; /* in DCE mode DCDIN is always 0 */ - if (!(usr1 & USR2_DCDIN)) + if (!(usr2 & USR2_DCDIN)) tmp |= TIOCM_CAR; if (sport->dte_mode) -- cgit v1.2.3 From 9f12cea96f47f98d612a0a0b84f950a0163731bf Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Mon, 26 Sep 2016 16:16:18 +0100 Subject: drivers/tty: Explicitly pass current to show_stack As noted in commit: 81539169f283329f ("x86/dumpstack: Remove NULL task pointer convention") ... having a NULL task parameter imply current leads to subtle bugs in stack walking code (so far seen on both 86 and arm64), makes callsites harder to read, and is unnecessary as all callers have access to current. As a step towards removing the problematic NULL-implies-current idiom entirely, have the sysrq code explicitly pass current to show_stack. Signed-off-by: Mark Rutland Cc: Ingo Molnar Cc: Jiri Slaby Cc: Josh Poimboeuf Cc: linux-kernel@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/tty/sysrq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/tty') diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c index 52bbd27e93ae..5b1e0edacb84 100644 --- a/drivers/tty/sysrq.c +++ b/drivers/tty/sysrq.c @@ -224,7 +224,7 @@ static void showacpu(void *dummy) spin_lock_irqsave(&show_lock, flags); pr_info("CPU%d:\n", smp_processor_id()); - show_stack(NULL, NULL); + show_stack(current, NULL); spin_unlock_irqrestore(&show_lock, flags); } -- cgit v1.2.3 From ffea043965e4634617485b4d313d6c0099d817f0 Mon Sep 17 00:00:00 2001 From: Thor Thayer Date: Thu, 22 Sep 2016 14:56:15 -0500 Subject: serial: 8250: of: Load TX FIFO Threshold from DT Initialize the tx_loadsz parameter from passed in devicetree tx-threshold parameter. The tx_loadsz is calculated as the number of bytes to fill FIFO when tx-threshold is triggered. Signed-off-by: Thor Thayer Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_of.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/8250/8250_of.c b/drivers/tty/serial/8250/8250_of.c index 38963d7bcf84..7a8b5fc81a19 100644 --- a/drivers/tty/serial/8250/8250_of.c +++ b/drivers/tty/serial/8250/8250_of.c @@ -195,6 +195,7 @@ static int of_platform_serial_probe(struct platform_device *ofdev) switch (port_type) { case PORT_8250 ... PORT_MAX_8250: { + u32 tx_threshold; struct uart_8250_port port8250; memset(&port8250, 0, sizeof(port8250)); port8250.port = port; @@ -202,6 +203,12 @@ static int of_platform_serial_probe(struct platform_device *ofdev) if (port.fifosize) port8250.capabilities = UART_CAP_FIFO; + /* Check for TX FIFO threshold & set tx_loadsz */ + if ((of_property_read_u32(ofdev->dev.of_node, "tx-threshold", + &tx_threshold) == 0) && + (tx_threshold < port.fifosize)) + port8250.tx_loadsz = port.fifosize - tx_threshold; + if (of_property_read_bool(ofdev->dev.of_node, "auto-flow-control")) port8250.capabilities |= UART_CAP_AFE; -- cgit v1.2.3 From 8e5470c9839caff94fe334e67ff7e7ace587282a Mon Sep 17 00:00:00 2001 From: Thor Thayer Date: Thu, 22 Sep 2016 14:56:16 -0500 Subject: serial: 8250: Set Altera 16550 TX FIFO Threshold The Altera 16550 soft IP UART requires 2 additional registers for TX FIFO threshold support. These 2 registers enable the TX FIFO Low Watermark and set the TX FIFO Low Watermark. Set the TX FIFO threshold to the FIFO size - tx_loadsz. Signed-off-by: Thor Thayer Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_port.c | 43 +++++++++++++++++++++++++++++++++++++ include/uapi/linux/serial_reg.h | 8 +++++++ 2 files changed, 51 insertions(+) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index 28d29fac1973..1bfb6fdbaa20 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -1870,6 +1870,30 @@ static int exar_handle_irq(struct uart_port *port) return ret; } +/* + * Newer 16550 compatible parts such as the SC16C650 & Altera 16550 Soft IP + * have a programmable TX threshold that triggers the THRE interrupt in + * the IIR register. In this case, the THRE interrupt indicates the FIFO + * has space available. Load it up with tx_loadsz bytes. + */ +static int serial8250_tx_threshold_handle_irq(struct uart_port *port) +{ + unsigned long flags; + unsigned int iir = serial_port_in(port, UART_IIR); + + /* TX Threshold IRQ triggered so load up FIFO */ + if ((iir & UART_IIR_ID) == UART_IIR_THRI) { + struct uart_8250_port *up = up_to_u8250p(port); + + spin_lock_irqsave(&port->lock, flags); + serial8250_tx_chars(up); + spin_unlock_irqrestore(&port->lock, flags); + } + + iir = serial_port_in(port, UART_IIR); + return serial8250_handle_irq(port, iir); +} + static unsigned int serial8250_tx_empty(struct uart_port *port) { struct uart_8250_port *up = up_to_u8250p(port); @@ -2159,6 +2183,25 @@ int serial8250_do_startup(struct uart_port *port) serial_port_out(port, UART_LCR, 0); } + /* + * For the Altera 16550 variants, set TX threshold trigger level. + */ + if (((port->type == PORT_ALTR_16550_F32) || + (port->type == PORT_ALTR_16550_F64) || + (port->type == PORT_ALTR_16550_F128)) && (port->fifosize > 1)) { + /* Bounds checking of TX threshold (valid 0 to fifosize-2) */ + if ((up->tx_loadsz < 2) || (up->tx_loadsz > port->fifosize)) { + pr_err("ttyS%d TX FIFO Threshold errors, skipping\n", + serial_index(port)); + } else { + serial_port_out(port, UART_ALTR_AFR, + UART_ALTR_EN_TXFIFO_LW); + serial_port_out(port, UART_ALTR_TX_LOW, + port->fifosize - up->tx_loadsz); + port->handle_irq = serial8250_tx_threshold_handle_irq; + } + } + if (port->irq) { unsigned char iir1; /* diff --git a/include/uapi/linux/serial_reg.h b/include/uapi/linux/serial_reg.h index 1e5ac4e776da..b4c04842a8c0 100644 --- a/include/uapi/linux/serial_reg.h +++ b/include/uapi/linux/serial_reg.h @@ -376,5 +376,13 @@ #define UART_EXAR_TXTRG 0x0a /* Tx FIFO trigger level write-only */ #define UART_EXAR_RXTRG 0x0b /* Rx FIFO trigger level write-only */ +/* + * These are definitions for the Altera ALTR_16550_F32/F64/F128 + * Normalized from 0x100 to 0x40 because of shift by 2 (32 bit regs). + */ +#define UART_ALTR_AFR 0x40 /* Additional Features Register */ +#define UART_ALTR_EN_TXFIFO_LW 0x01 /* Enable the TX FIFO Low Watermark */ +#define UART_ALTR_TX_LOW 0x41 /* Tx FIFO Low Watermark */ + #endif /* _LINUX_SERIAL_REG_H */ -- cgit v1.2.3 From 35aa33cf0b35af6f2f1d8fb81eea8cf723c65239 Mon Sep 17 00:00:00 2001 From: Kefeng Wang Date: Sat, 24 Sep 2016 17:14:24 +0800 Subject: tty: amba-pl011: Don't complain on -EPROBE_DEFER when no irq Don't complain on -EPROBE_DEFER when attempting to get the irq. the driver probe will be retried later. Cc: Russell King Cc: Greg Kroah-Hartman Signed-off-by: Kefeng Wang Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/amba-pl011.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 2d9ffab16ffe..e2c33b9528d8 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -2582,7 +2582,8 @@ static int sbsa_uart_probe(struct platform_device *pdev) ret = platform_get_irq(pdev, 0); if (ret < 0) { - dev_err(&pdev->dev, "cannot obtain irq\n"); + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, "cannot obtain irq\n"); return ret; } uap->port.irq = ret; -- cgit v1.2.3 From 094a32626f904f92beda174154daca3fe4455890 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 28 Sep 2016 08:12:27 +0200 Subject: Revert "drivers/tty: Explicitly pass current to show_stack" This reverts commit 9f12cea96f47f98d612a0a0b84f950a0163731bf. Mark writes: Unfortunately, this patch will result in erroneous stack traces on some architectures. Sorry about this; I should have verified this more thoroughly before sending the series out. Please drop the patch at your earliest convenience. Cc: Mark Rutland Signed-off-by: Greg Kroah-Hartman --- drivers/tty/sysrq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/tty') diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c index 5b1e0edacb84..52bbd27e93ae 100644 --- a/drivers/tty/sysrq.c +++ b/drivers/tty/sysrq.c @@ -224,7 +224,7 @@ static void showacpu(void *dummy) spin_lock_irqsave(&show_lock, flags); pr_info("CPU%d:\n", smp_processor_id()); - show_stack(current, NULL); + show_stack(NULL, NULL); spin_unlock_irqrestore(&show_lock, flags); } -- cgit v1.2.3 From d503187b6cc4e41c21c02e695e0e7b5acdd066de Mon Sep 17 00:00:00 2001 From: Leif Lindholm Date: Tue, 27 Sep 2016 23:54:12 +0300 Subject: of/serial: move earlycon early_param handling to serial We have multiple "earlycon" early_param handlers - merge the DT one into the main earlycon one. It's a cleanup that also will be useful to defer setting up DT console until ACPI/DT decision is made. Rename the exported function to avoid clashing with the function from arch/microblaze/kernel/prom.c Signed-off-by: Leif Lindholm Signed-off-by: Aleksey Makarov Acked-by: Rob Herring Acked-by: Greg Kroah-Hartman Reviewed-by: Peter Hurley Tested-by: Kefeng Wang Tested-by: Christopher Covington Signed-off-by: Greg Kroah-Hartman --- drivers/of/fdt.c | 11 +---------- drivers/tty/serial/earlycon.c | 2 +- include/linux/of_fdt.h | 3 +++ 3 files changed, 5 insertions(+), 11 deletions(-) (limited to 'drivers/tty') diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 085c6389afd1..c89d5d231a0e 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -924,7 +924,7 @@ static inline void early_init_dt_check_for_initrd(unsigned long node) #ifdef CONFIG_SERIAL_EARLYCON -static int __init early_init_dt_scan_chosen_serial(void) +int __init early_init_dt_scan_chosen_stdout(void) { int offset; const char *p, *q, *options = NULL; @@ -968,15 +968,6 @@ static int __init early_init_dt_scan_chosen_serial(void) } return -ENODEV; } - -static int __init setup_of_earlycon(char *buf) -{ - if (buf) - return 0; - - return early_init_dt_scan_chosen_serial(); -} -early_param("earlycon", setup_of_earlycon); #endif /** diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c index 3940280afa9c..836cad222fcc 100644 --- a/drivers/tty/serial/earlycon.c +++ b/drivers/tty/serial/earlycon.c @@ -208,7 +208,7 @@ static int __init param_setup_earlycon(char *buf) * don't generate a warning from parse_early_params() in that case */ if (!buf || !buf[0]) - return 0; + return early_init_dt_scan_chosen_stdout(); err = setup_earlycon(buf); if (err == -ENOENT || err == -EALREADY) diff --git a/include/linux/of_fdt.h b/include/linux/of_fdt.h index 26c3302ae58f..4341f32516d8 100644 --- a/include/linux/of_fdt.h +++ b/include/linux/of_fdt.h @@ -14,6 +14,7 @@ #include #include +#include /* Definitions used by the flattened device tree */ #define OF_DT_HEADER 0xd00dfeed /* marker */ @@ -66,6 +67,7 @@ extern int early_init_dt_scan_chosen(unsigned long node, const char *uname, int depth, void *data); extern int early_init_dt_scan_memory(unsigned long node, const char *uname, int depth, void *data); +extern int early_init_dt_scan_chosen_stdout(void); extern void early_init_fdt_scan_reserved_mem(void); extern void early_init_fdt_reserve_self(void); extern void early_init_dt_add_memory_arch(u64 base, u64 size); @@ -94,6 +96,7 @@ extern void early_get_first_memblock_info(void *, phys_addr_t *); extern u64 of_flat_dt_translate_address(unsigned long node); extern void of_fdt_limit_memory(int limit); #else /* CONFIG_OF_FLATTREE */ +static inline int early_init_dt_scan_chosen_stdout(void) { return -ENODEV; } static inline void early_init_fdt_scan_reserved_mem(void) {} static inline void early_init_fdt_reserve_self(void) {} static inline const char *of_flat_dt_get_machine_name(void) { return NULL; } -- cgit v1.2.3 From ad1696f6f09daacfdf2bf04bc83cd8f48d80e34a Mon Sep 17 00:00:00 2001 From: Aleksey Makarov Date: Tue, 27 Sep 2016 23:54:13 +0300 Subject: ACPI: parse SPCR and enable matching console 'ARM Server Base Boot Requiremets' [1] mentions SPCR (Serial Port Console Redirection Table) [2] as a mandatory ACPI table that specifies the configuration of serial console. Defer initialization of DT earlycon until ACPI/DT decision is made. Parse the ACPI SPCR table, setup earlycon if required, enable specified console. Thanks to Peter Hurley for explaining how this should work. [1] http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.den0044a/index.html [2] https://msdn.microsoft.com/en-us/library/windows/hardware/dn639132(v=vs.85).aspx Signed-off-by: Aleksey Makarov Acked-by: Rafael J. Wysocki Reviewed-by: Peter Hurley Tested-by: Kefeng Wang Tested-by: Christopher Covington Signed-off-by: Greg Kroah-Hartman --- drivers/acpi/Kconfig | 3 ++ drivers/acpi/Makefile | 1 + drivers/acpi/spcr.c | 111 ++++++++++++++++++++++++++++++++++++++++++ drivers/tty/serial/earlycon.c | 19 +++++++- include/linux/acpi.h | 6 +++ include/linux/serial_core.h | 9 +++- 6 files changed, 146 insertions(+), 3 deletions(-) create mode 100644 drivers/acpi/spcr.c (limited to 'drivers/tty') diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 445ce28475b3..a984cc7dcba4 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -77,6 +77,9 @@ config ACPI_DEBUGGER_USER endif +config ACPI_SPCR_TABLE + bool + config ACPI_SLEEP bool depends on SUSPEND || HIBERNATION diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 5ae9d85c5159..c469516062c6 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -81,6 +81,7 @@ obj-$(CONFIG_ACPI_EC_DEBUGFS) += ec_sys.o obj-$(CONFIG_ACPI_CUSTOM_METHOD)+= custom_method.o obj-$(CONFIG_ACPI_BGRT) += bgrt.o obj-$(CONFIG_ACPI_CPPC_LIB) += cppc_acpi.o +obj-$(CONFIG_ACPI_SPCR_TABLE) += spcr.o obj-$(CONFIG_ACPI_DEBUGGER_USER) += acpi_dbg.o # processor has its own "processor." module_param namespace diff --git a/drivers/acpi/spcr.c b/drivers/acpi/spcr.c new file mode 100644 index 000000000000..e8d7bc7d4da8 --- /dev/null +++ b/drivers/acpi/spcr.c @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2012, Intel Corporation + * Copyright (c) 2015, Red Hat, Inc. + * Copyright (c) 2015, 2016 Linaro Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#define pr_fmt(fmt) "ACPI: SPCR: " fmt + +#include +#include +#include +#include + +/** + * parse_spcr() - parse ACPI SPCR table and add preferred console + * + * @earlycon: set up earlycon for the console specified by the table + * + * For the architectures with support for ACPI, CONFIG_ACPI_SPCR_TABLE may be + * defined to parse ACPI SPCR table. As a result of the parsing preferred + * console is registered and if @earlycon is true, earlycon is set up. + * + * When CONFIG_ACPI_SPCR_TABLE is defined, this function should be called + * from arch inintialization code as soon as the DT/ACPI decision is made. + * + */ +int __init parse_spcr(bool earlycon) +{ + static char opts[64]; + struct acpi_table_spcr *table; + acpi_size table_size; + acpi_status status; + char *uart; + char *iotype; + int baud_rate; + int err; + + if (acpi_disabled) + return -ENODEV; + + status = acpi_get_table_with_size(ACPI_SIG_SPCR, 0, + (struct acpi_table_header **)&table, + &table_size); + + if (ACPI_FAILURE(status)) + return -ENOENT; + + if (table->header.revision < 2) { + err = -ENOENT; + pr_err("wrong table version\n"); + goto done; + } + + iotype = table->serial_port.space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY ? + "mmio" : "io"; + + switch (table->interface_type) { + case ACPI_DBG2_ARM_SBSA_32BIT: + iotype = "mmio32"; + /* fall through */ + case ACPI_DBG2_ARM_PL011: + case ACPI_DBG2_ARM_SBSA_GENERIC: + case ACPI_DBG2_BCM2835: + uart = "pl011"; + break; + case ACPI_DBG2_16550_COMPATIBLE: + case ACPI_DBG2_16550_SUBSET: + uart = "uart"; + break; + default: + err = -ENOENT; + goto done; + } + + switch (table->baud_rate) { + case 3: + baud_rate = 9600; + break; + case 4: + baud_rate = 19200; + break; + case 6: + baud_rate = 57600; + break; + case 7: + baud_rate = 115200; + break; + default: + err = -ENOENT; + goto done; + } + + snprintf(opts, sizeof(opts), "%s,%s,0x%llx,%d", uart, iotype, + table->serial_port.address, baud_rate); + + pr_info("console: %s\n", opts); + + if (earlycon) + setup_earlycon(opts); + + err = add_preferred_console(uart, 0, opts + strlen(uart) + 1); + +done: + early_acpi_os_unmap_memory((void __iomem *)table, table_size); + return err; +} diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c index 836cad222fcc..c3651540e1ba 100644 --- a/drivers/tty/serial/earlycon.c +++ b/drivers/tty/serial/earlycon.c @@ -21,6 +21,7 @@ #include #include #include +#include #ifdef CONFIG_FIX_EARLYCON_MEM #include @@ -198,6 +199,14 @@ int __init setup_earlycon(char *buf) return -ENOENT; } +/* + * When CONFIG_ACPI_SPCR_TABLE is defined, "earlycon" without parameters in + * command line does not start DT earlycon immediately, instead it defers + * starting it until DT/ACPI decision is made. At that time if ACPI is enabled + * call parse_spcr(), else call early_init_dt_scan_chosen_stdout() + */ +bool earlycon_init_is_deferred __initdata; + /* early_param wrapper for setup_earlycon() */ static int __init param_setup_earlycon(char *buf) { @@ -207,8 +216,14 @@ static int __init param_setup_earlycon(char *buf) * Just 'earlycon' is a valid param for devicetree earlycons; * don't generate a warning from parse_early_params() in that case */ - if (!buf || !buf[0]) - return early_init_dt_scan_chosen_stdout(); + if (!buf || !buf[0]) { + if (IS_ENABLED(CONFIG_ACPI_SPCR_TABLE)) { + earlycon_init_is_deferred = true; + return 0; + } else { + return early_init_dt_scan_chosen_stdout(); + } + } err = setup_earlycon(buf); if (err == -ENOENT || err == -EALREADY) diff --git a/include/linux/acpi.h b/include/linux/acpi.h index c5eaf2f80a4c..2353827731d2 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -1074,4 +1074,10 @@ void acpi_table_upgrade(void); static inline void acpi_table_upgrade(void) { } #endif +#ifdef CONFIG_ACPI_SPCR_TABLE +int parse_spcr(bool earlycon); +#else +static inline int parse_spcr(bool earlycon) { return 0; } +#endif + #endif /*_LINUX_ACPI_H*/ diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 378d80a8dd43..344201437017 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -367,11 +367,18 @@ extern const struct earlycon_id __earlycon_table_end[]; #define EARLYCON_DECLARE(_name, fn) OF_EARLYCON_DECLARE(_name, "", fn) -extern int setup_earlycon(char *buf); extern int of_setup_earlycon(const struct earlycon_id *match, unsigned long node, const char *options); +#ifdef CONFIG_SERIAL_EARLYCON +extern bool earlycon_init_is_deferred __initdata; +int setup_earlycon(char *buf); +#else +static const bool earlycon_init_is_deferred; +static inline int setup_earlycon(char *buf) { return 0; } +#endif + struct uart_port *uart_get_console(struct uart_port *ports, int nr, struct console *c); int uart_parse_earlycon(char *p, unsigned char *iotype, resource_size_t *addr, -- cgit v1.2.3 From 8b8f347d3a4859d22567f3b8e5bb4a69b1089739 Mon Sep 17 00:00:00 2001 From: Aleksey Makarov Date: Tue, 27 Sep 2016 23:54:15 +0300 Subject: serial: pl011: add console matching function This patch adds function pl011_console_match() that implements method match of struct console. It allows to match consoles against data specified in a string, for example taken from command line or compiled by ACPI SPCR table handler. Signed-off-by: Aleksey Makarov Reviewed-by: Peter Hurley Acked-by: Russell King Tested-by: Christopher Covington Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/amba-pl011.c | 55 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index e2c33b9528d8..cd5d1ee3b8b6 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -2315,12 +2315,67 @@ static int __init pl011_console_setup(struct console *co, char *options) return uart_set_options(&uap->port, co, baud, parity, bits, flow); } +/** + * pl011_console_match - non-standard console matching + * @co: registering console + * @name: name from console command line + * @idx: index from console command line + * @options: ptr to option string from console command line + * + * Only attempts to match console command lines of the form: + * console=pl011,mmio|mmio32,[,] + * console=pl011,0x[,] + * This form is used to register an initial earlycon boot console and + * replace it with the amba_console at pl011 driver init. + * + * Performs console setup for a match (as required by interface) + * If no are specified, then assume the h/w is already setup. + * + * Returns 0 if console matches; otherwise non-zero to use default matching + */ +static int __init pl011_console_match(struct console *co, char *name, int idx, + char *options) +{ + unsigned char iotype; + unsigned long addr; + int i; + + if (strcmp(name, "pl011") != 0) + return -ENODEV; + + if (uart_parse_earlycon(options, &iotype, &addr, &options)) + return -ENODEV; + + if (iotype != UPIO_MEM && iotype != UPIO_MEM32) + return -ENODEV; + + /* try to match the port specified on the command line */ + for (i = 0; i < ARRAY_SIZE(amba_ports); i++) { + struct uart_port *port; + + if (!amba_ports[i]) + continue; + + port = &amba_ports[i]->port; + + if (port->mapbase != addr) + continue; + + co->index = i; + port->cons = co; + return pl011_console_setup(co, options); + } + + return -ENODEV; +} + static struct uart_driver amba_reg; static struct console amba_console = { .name = "ttyAMA", .write = pl011_console_write, .device = uart_console_device, .setup = pl011_console_setup, + .match = pl011_console_match, .flags = CON_PRINTBUFFER, .index = -1, .data = &amba_reg, -- cgit v1.2.3 From 08bf21590041550e5ffd4d33e6a58548d0d09142 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 30 Sep 2016 07:46:35 +0200 Subject: Revert "serial: pl011: add console matching function" This reverts commit 8b8f347d3a4859d22567f3b8e5bb4a69b1089739 as it causes build errors in linux-next Reported-by: Stephen Rothwell Cc: Aleksey Makarov Cc: Peter Hurley Cc: Russell King Cc: Christopher Covington Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/amba-pl011.c | 55 ----------------------------------------- 1 file changed, 55 deletions(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index cd5d1ee3b8b6..e2c33b9528d8 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -2315,67 +2315,12 @@ static int __init pl011_console_setup(struct console *co, char *options) return uart_set_options(&uap->port, co, baud, parity, bits, flow); } -/** - * pl011_console_match - non-standard console matching - * @co: registering console - * @name: name from console command line - * @idx: index from console command line - * @options: ptr to option string from console command line - * - * Only attempts to match console command lines of the form: - * console=pl011,mmio|mmio32,[,] - * console=pl011,0x[,] - * This form is used to register an initial earlycon boot console and - * replace it with the amba_console at pl011 driver init. - * - * Performs console setup for a match (as required by interface) - * If no are specified, then assume the h/w is already setup. - * - * Returns 0 if console matches; otherwise non-zero to use default matching - */ -static int __init pl011_console_match(struct console *co, char *name, int idx, - char *options) -{ - unsigned char iotype; - unsigned long addr; - int i; - - if (strcmp(name, "pl011") != 0) - return -ENODEV; - - if (uart_parse_earlycon(options, &iotype, &addr, &options)) - return -ENODEV; - - if (iotype != UPIO_MEM && iotype != UPIO_MEM32) - return -ENODEV; - - /* try to match the port specified on the command line */ - for (i = 0; i < ARRAY_SIZE(amba_ports); i++) { - struct uart_port *port; - - if (!amba_ports[i]) - continue; - - port = &amba_ports[i]->port; - - if (port->mapbase != addr) - continue; - - co->index = i; - port->cons = co; - return pl011_console_setup(co, options); - } - - return -ENODEV; -} - static struct uart_driver amba_reg; static struct console amba_console = { .name = "ttyAMA", .write = pl011_console_write, .device = uart_console_device, .setup = pl011_console_setup, - .match = pl011_console_match, .flags = CON_PRINTBUFFER, .index = -1, .data = &amba_reg, -- cgit v1.2.3