diff options
53 files changed, 2902 insertions, 738 deletions
diff --git a/Documentation/devicetree/bindings/serial/8250.txt b/Documentation/devicetree/bindings/serial/8250.txt index dad3b2ec66d4..aeb6db4e35c3 100644 --- a/Documentation/devicetree/bindings/serial/8250.txt +++ b/Documentation/devicetree/bindings/serial/8250.txt @@ -24,6 +24,7 @@ Required properties: - "ti,da830-uart" - "aspeed,ast2400-vuart" - "aspeed,ast2500-vuart" + - "nuvoton,npcm750-uart" - "serial" if the port type is unknown. - reg : offset and length of the register set for the device. - interrupts : should contain uart interrupt. diff --git a/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt b/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt index cf504d0380ae..ad962f4ec3aa 100644 --- a/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt +++ b/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt @@ -43,6 +43,8 @@ Required properties: - "renesas,hscif-r8a7796" for R8A7796 (R-Car M3-W) HSCIF compatible UART. - "renesas,scif-r8a77970" for R8A77970 (R-Car V3M) SCIF compatible UART. - "renesas,hscif-r8a77970" for R8A77970 (R-Car V3M) HSCIF compatible UART. + - "renesas,scif-r8a77980" for R8A77980 (R-Car V3H) SCIF compatible UART. + - "renesas,hscif-r8a77980" for R8A77980 (R-Car V3H) HSCIF compatible UART. - "renesas,scif-r8a77995" for R8A77995 (R-Car D3) SCIF compatible UART. - "renesas,hscif-r8a77995" for R8A77995 (R-Car D3) HSCIF compatible UART. - "renesas,scifa-sh73a0" for SH73A0 (SH-Mobile AG5) SCIFA compatible UART. diff --git a/Documentation/devicetree/bindings/serial/st,stm32-usart.txt b/Documentation/devicetree/bindings/serial/st,stm32-usart.txt index d150b04a6229..9d3efed55deb 100644 --- a/Documentation/devicetree/bindings/serial/st,stm32-usart.txt +++ b/Documentation/devicetree/bindings/serial/st,stm32-usart.txt @@ -15,6 +15,8 @@ Required properties: Optional properties: - pinctrl: The reference on the pins configuration - st,hw-flow-ctrl: bool flag to enable hardware flow control. +- rs485-rts-delay, rs485-rx-during-tx, rs485-rts-active-low, + linux,rs485-enabled-at-boot-time: see rs485.txt. - dmas: phandle(s) to DMA controller node(s). Refer to stm32-dma.txt - dma-names: "rx" and/or "tx" diff --git a/arch/arm/boot/dts/stih407-b2120.dts b/arch/arm/boot/dts/stih407-b2120.dts index c8ad905d0309..a7a0f76e9cbc 100644 --- a/arch/arm/boot/dts/stih407-b2120.dts +++ b/arch/arm/boot/dts/stih407-b2120.dts @@ -14,7 +14,7 @@ compatible = "st,stih407-b2120", "st,stih407"; chosen { - bootargs = "console=ttyAS0,115200 clk_ignore_unused"; + bootargs = "clk_ignore_unused"; linux,stdout-path = &sbc_serial0; }; @@ -24,7 +24,7 @@ }; aliases { - ttyAS0 = &sbc_serial0; + serial0 = &sbc_serial0; ethernet0 = ðernet0; }; diff --git a/arch/arm/boot/dts/stih410-b2120.dts b/arch/arm/boot/dts/stih410-b2120.dts index 9830be577433..6c6b4cc37e97 100644 --- a/arch/arm/boot/dts/stih410-b2120.dts +++ b/arch/arm/boot/dts/stih410-b2120.dts @@ -14,7 +14,7 @@ compatible = "st,stih410-b2120", "st,stih410"; chosen { - bootargs = "console=ttyAS0,115200 clk_ignore_unused"; + bootargs = "clk_ignore_unused"; linux,stdout-path = &sbc_serial0; }; @@ -24,7 +24,7 @@ }; aliases { - ttyAS0 = &sbc_serial0; + serial0 = &sbc_serial0; ethernet0 = ðernet0; }; diff --git a/arch/arm/boot/dts/stih410-b2260.dts b/arch/arm/boot/dts/stih410-b2260.dts index c663b70c43a7..50d36758391c 100644 --- a/arch/arm/boot/dts/stih410-b2260.dts +++ b/arch/arm/boot/dts/stih410-b2260.dts @@ -15,7 +15,7 @@ compatible = "st,stih410-b2260", "st,stih410"; chosen { - bootargs = "console=ttyAS1,115200 clk_ignore_unused"; + bootargs = "clk_ignore_unused"; linux,stdout-path = &uart1; }; @@ -25,7 +25,7 @@ }; aliases { - ttyAS1 = &uart1; + serial1 = &uart1; ethernet0 = ðernet0; }; diff --git a/arch/arm/boot/dts/stih418-b2199.dts b/arch/arm/boot/dts/stih418-b2199.dts index 4e6d915c85ff..7f5f3252bfc7 100644 --- a/arch/arm/boot/dts/stih418-b2199.dts +++ b/arch/arm/boot/dts/stih418-b2199.dts @@ -14,7 +14,7 @@ compatible = "st,stih418-b2199", "st,stih418"; chosen { - bootargs = "console=ttyAS0,115200 clk_ignore_unused"; + bootargs = "clk_ignore_unused"; linux,stdout-path = &sbc_serial0; }; @@ -24,7 +24,7 @@ }; aliases { - ttyAS0 = &sbc_serial0; + serial0 = &sbc_serial0; ethernet0 = ðernet0; }; diff --git a/drivers/parport/parport_serial.c b/drivers/parport/parport_serial.c index e15b4845f7c6..087e847b1da2 100644 --- a/drivers/parport/parport_serial.c +++ b/drivers/parport/parport_serial.c @@ -156,9 +156,6 @@ static struct parport_pc_pci cards[] = { /* sunix_2s1p */ { 1, { { 3, -1 }, } }, }; -#define PCI_VENDOR_ID_SUNIX 0x1fd4 -#define PCI_DEVICE_ID_SUNIX_1999 0x1999 - static struct pci_device_id parport_serial_pci_tbl[] = { /* PCI cards */ { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_110L, diff --git a/drivers/tty/hvc/Kconfig b/drivers/tty/hvc/Kconfig index 3bade5ad3d71..4293c172e120 100644 --- a/drivers/tty/hvc/Kconfig +++ b/drivers/tty/hvc/Kconfig @@ -88,6 +88,16 @@ config HVC_DCC driver. This console is used through a JTAG only on ARM. If you don't have a JTAG then you probably don't want this option. +config HVC_RISCV_SBI + bool "RISC-V SBI console support" + depends on RISCV + select HVC_DRIVER + help + This enables support for console output via RISC-V SBI calls, which + is normally used only during boot to output printk. + + If you don't know what do to here, say Y. + config HVCS tristate "IBM Hypervisor Virtual Console Server support" depends on PPC_PSERIES && HVC_CONSOLE diff --git a/drivers/tty/hvc/Makefile b/drivers/tty/hvc/Makefile index 7da1934d34e8..98880e357941 100644 --- a/drivers/tty/hvc/Makefile +++ b/drivers/tty/hvc/Makefile @@ -9,4 +9,5 @@ obj-$(CONFIG_HVC_IRQ) += hvc_irq.o obj-$(CONFIG_HVC_XEN) += hvc_xen.o obj-$(CONFIG_HVC_IUCV) += hvc_iucv.o obj-$(CONFIG_HVC_UDBG) += hvc_udbg.o +obj-$(CONFIG_HVC_RISCV_SBI) += hvc_riscv_sbi.o obj-$(CONFIG_HVCS) += hvcs.o diff --git a/drivers/tty/hvc/hvc_riscv_sbi.c b/drivers/tty/hvc/hvc_riscv_sbi.c new file mode 100644 index 000000000000..75155bde2b88 --- /dev/null +++ b/drivers/tty/hvc/hvc_riscv_sbi.c @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2008 David Gibson, IBM Corporation + * Copyright (C) 2012 Regents of the University of California + * Copyright (C) 2017 SiFive + */ + +#include <linux/console.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/moduleparam.h> +#include <linux/types.h> + +#include <asm/sbi.h> + +#include "hvc_console.h" + +static int hvc_sbi_tty_put(uint32_t vtermno, const char *buf, int count) +{ + int i; + + for (i = 0; i < count; i++) + sbi_console_putchar(buf[i]); + + return i; +} + +static int hvc_sbi_tty_get(uint32_t vtermno, char *buf, int count) +{ + int i, c; + + for (i = 0; i < count; i++) { + c = sbi_console_getchar(); + if (c < 0) + break; + buf[i] = c; + } + + return i; +} + +static const struct hv_ops hvc_sbi_ops = { + .get_chars = hvc_sbi_tty_get, + .put_chars = hvc_sbi_tty_put, +}; + +static int __init hvc_sbi_init(void) +{ + return PTR_ERR_OR_ZERO(hvc_alloc(0, 0, &hvc_sbi_ops, 16)); +} +device_initcall(hvc_sbi_init); + +static int __init hvc_sbi_console_init(void) +{ + hvc_instantiate(0, 0, &hvc_sbi_ops); + add_preferred_console("hvc", 0, NULL); + + return 0; +} +console_initcall(hvc_sbi_console_init); diff --git a/drivers/tty/serdev/core.c b/drivers/tty/serdev/core.c index f439c72b9e3c..df93b727e984 100644 --- a/drivers/tty/serdev/core.c +++ b/drivers/tty/serdev/core.c @@ -350,7 +350,7 @@ static struct bus_type serdev_bus_type = { }; /** - * serdev_controller_alloc() - Allocate a new serdev device + * serdev_device_alloc() - Allocate a new serdev device * @ctrl: associated controller * * Caller is responsible for either calling serdev_device_add() to add the diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index cd1b94a0f451..6fcdb90f616a 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -9,6 +9,7 @@ * LCR is written whilst busy. If it is, then a busy detect interrupt is * raised, the LCR needs to be rewritten and the uart status register read. */ +#include <linux/delay.h> #include <linux/device.h> #include <linux/io.h> #include <linux/module.h> @@ -119,10 +120,27 @@ static void dw8250_check_lcr(struct uart_port *p, int value) */ } +/* Returns once the transmitter is empty or we run out of retries */ +static void dw8250_tx_wait_empty(struct uart_port *p, int tries) +{ + unsigned int lsr; + + while (tries--) { + lsr = readb (p->membase + (UART_LSR << p->regshift)); + if (lsr & UART_LSR_TEMT) + break; + udelay (10); + } +} + static void dw8250_serial_out(struct uart_port *p, int offset, int value) { struct dw8250_data *d = p->private_data; + /* Allow the TX to drain before we reconfigure */ + if (offset == UART_LCR) + dw8250_tx_wait_empty(p, 1000); + writeb(value, p->membase + (offset << p->regshift)); if (offset == UART_LCR && !d->uart_16550_compatible) @@ -339,17 +357,11 @@ static void dw8250_quirks(struct uart_port *p, struct dw8250_data *data) p->serial_in = dw8250_serial_in32be; p->serial_out = dw8250_serial_out32be; } - } else if (has_acpi_companion(p->dev)) { - const struct acpi_device_id *id; - - id = acpi_match_device(p->dev->driver->acpi_match_table, - p->dev); - if (id && !strcmp(id->id, "APMC0D08")) { - p->iotype = UPIO_MEM32; - p->regshift = 2; - p->serial_in = dw8250_serial_in32; - data->uart_16550_compatible = true; - } + } else if (acpi_dev_present("APMC0D08", NULL, -1)) { + p->iotype = UPIO_MEM32; + p->regshift = 2; + p->serial_in = dw8250_serial_in32; + data->uart_16550_compatible = true; } /* Platforms with iDMA */ diff --git a/drivers/tty/serial/8250/8250_men_mcb.c b/drivers/tty/serial/8250/8250_men_mcb.c index 308977807994..127017cc41d9 100644 --- a/drivers/tty/serial/8250/8250_men_mcb.c +++ b/drivers/tty/serial/8250/8250_men_mcb.c @@ -1,12 +1,19 @@ #include <linux/device.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/io.h> #include <linux/mcb.h> #include <linux/serial.h> #include <linux/serial_core.h> #include <linux/serial_8250.h> #include <uapi/linux/serial_core.h> +#define MEN_UART_ID_Z025 0x19 +#define MEN_UART_ID_Z057 0x39 +#define MEN_UART_ID_Z125 0x7d + +#define MEN_UART_MEM_SIZE 0x10 + struct serial_8250_men_mcb_data { struct uart_8250_port uart; int line; @@ -18,7 +25,7 @@ struct serial_8250_men_mcb_data { * parameter in order to really set the correct baudrate, and * do so if possible without user interaction */ -static u32 men_z125_lookup_uartclk(struct mcb_device *mdev) +static u32 men_lookup_uartclk(struct mcb_device *mdev) { /* use default value if board is not available below */ u32 clkval = 1041666; @@ -28,10 +35,12 @@ static u32 men_z125_lookup_uartclk(struct mcb_device *mdev) mdev->bus->name); if (strncmp(mdev->bus->name, "F075", 4) == 0) clkval = 1041666; - else if (strncmp(mdev->bus->name, "F216", 4) == 0) + else if (strncmp(mdev->bus->name, "F216", 4) == 0) clkval = 1843200; else if (strncmp(mdev->bus->name, "G215", 4) == 0) clkval = 1843200; + else if (strncmp(mdev->bus->name, "F210", 4) == 0) + clkval = 115200; else dev_info(&mdev->dev, "board not detected, using default uartclk\n"); @@ -41,62 +50,108 @@ static u32 men_z125_lookup_uartclk(struct mcb_device *mdev) return clkval; } +static unsigned int get_num_ports(struct mcb_device *mdev, + void __iomem *membase) +{ + switch (mdev->id) { + case MEN_UART_ID_Z125: + return 1U; + case MEN_UART_ID_Z025: + return readb(membase) >> 4; + case MEN_UART_ID_Z057: + return 4U; + default: + dev_err(&mdev->dev, "no supported device!\n"); + return -ENODEV; + } +} + static int serial_8250_men_mcb_probe(struct mcb_device *mdev, const struct mcb_device_id *id) { struct serial_8250_men_mcb_data *data; struct resource *mem; - - data = devm_kzalloc(&mdev->dev, - sizeof(struct serial_8250_men_mcb_data), - GFP_KERNEL); - if (!data) - return -ENOMEM; - - mcb_set_drvdata(mdev, data); - data->uart.port.dev = mdev->dma_dev; - spin_lock_init(&data->uart.port.lock); - - data->uart.port.type = PORT_16550; - data->uart.port.flags = UPF_SKIP_TEST | UPF_SHARE_IRQ | UPF_FIXED_TYPE; - data->uart.port.iotype = UPIO_MEM; - data->uart.port.uartclk = men_z125_lookup_uartclk(mdev); - data->uart.port.regshift = 0; - data->uart.port.fifosize = 60; + unsigned int num_ports; + unsigned int i; + void __iomem *membase; mem = mcb_get_resource(mdev, IORESOURCE_MEM); if (mem == NULL) return -ENXIO; + membase = devm_ioremap_resource(&mdev->dev, mem); + if (IS_ERR(membase)) + return PTR_ERR_OR_ZERO(membase); - data->uart.port.irq = mcb_get_irq(mdev); + num_ports = get_num_ports(mdev, membase); - data->uart.port.membase = devm_ioremap_resource(&mdev->dev, mem); - if (IS_ERR(data->uart.port.membase)) - return PTR_ERR_OR_ZERO(data->uart.port.membase); + dev_dbg(&mdev->dev, "found a 16z%03u with %u ports\n", + mdev->id, num_ports); - data->uart.port.mapbase = (unsigned long) mem->start; - data->uart.port.iobase = data->uart.port.mapbase; + if (num_ports == 0 || num_ports > 4) { + dev_err(&mdev->dev, "unexpected number of ports: %u\n", + num_ports); + return -ENODEV; + } - /* ok, register the port */ - data->line = serial8250_register_8250_port(&data->uart); - if (data->line < 0) - return data->line; + data = devm_kcalloc(&mdev->dev, num_ports, + sizeof(struct serial_8250_men_mcb_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; - dev_info(&mdev->dev, "found 16Z125 UART: ttyS%d\n", data->line); + mcb_set_drvdata(mdev, data); + + for (i = 0; i < num_ports; i++) { + data[i].uart.port.dev = mdev->dma_dev; + spin_lock_init(&data[i].uart.port.lock); + + data[i].uart.port.type = PORT_16550; + data[i].uart.port.flags = UPF_SKIP_TEST | UPF_SHARE_IRQ + | UPF_FIXED_TYPE; + data[i].uart.port.iotype = UPIO_MEM; + data[i].uart.port.uartclk = men_lookup_uartclk(mdev); + data[i].uart.port.regshift = 0; + data[i].uart.port.irq = mcb_get_irq(mdev); + data[i].uart.port.membase = membase; + data[i].uart.port.fifosize = 60; + data[i].uart.port.mapbase = (unsigned long) mem->start + + i * MEN_UART_MEM_SIZE; + data[i].uart.port.iobase = data[i].uart.port.mapbase; + + /* ok, register the port */ + data[i].line = serial8250_register_8250_port(&data[i].uart); + if (data[i].line < 0) { + dev_err(&mdev->dev, "unable to register UART port\n"); + return data[i].line; + } + dev_info(&mdev->dev, "found MCB UART: ttyS%d\n", data[i].line); + } return 0; } static void serial_8250_men_mcb_remove(struct mcb_device *mdev) { + unsigned int num_ports, i; struct serial_8250_men_mcb_data *data = mcb_get_drvdata(mdev); - if (data) - serial8250_unregister_port(data->line); + if (!data) + return; + + num_ports = get_num_ports(mdev, data[0].uart.port.membase); + if (num_ports < 0 || num_ports > 4) { + dev_err(&mdev->dev, "error retrieving number of ports!\n"); + return; + } + + for (i = 0; i < num_ports; i++) + serial8250_unregister_port(data[i].line); } static const struct mcb_device_id serial_8250_men_mcb_ids[] = { - { .device = 0x7d }, + { .device = MEN_UART_ID_Z025 }, + { .device = MEN_UART_ID_Z057 }, + { .device = MEN_UART_ID_Z125 }, { } }; MODULE_DEVICE_TABLE(mcb, serial_8250_men_mcb_ids); @@ -113,6 +168,8 @@ static struct mcb_driver mcb_driver = { module_mcb_driver(mcb_driver); MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("MEN 16z125 8250 UART driver"); +MODULE_DESCRIPTION("MEN 8250 UART driver"); MODULE_AUTHOR("Michael Moese <michael.moese@men.de"); MODULE_ALIAS("mcb:16z125"); +MODULE_ALIAS("mcb:16z025"); +MODULE_ALIAS("mcb:16z057"); diff --git a/drivers/tty/serial/8250/8250_of.c b/drivers/tty/serial/8250/8250_of.c index 160b8906d9b9..9835b1c1cbe1 100644 --- a/drivers/tty/serial/8250/8250_of.c +++ b/drivers/tty/serial/8250/8250_of.c @@ -316,6 +316,7 @@ static const struct of_device_id of_platform_serial_table[] = { { .compatible = "mrvl,mmp-uart", .data = (void *)PORT_XSCALE, }, { .compatible = "ti,da830-uart", .data = (void *)PORT_DA830, }, + { .compatible = "nuvoton,npcm750-uart", .data = (void *)PORT_NPCM, }, { /* end of list */ }, }; MODULE_DEVICE_TABLE(of, of_platform_serial_table); diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c index 57f6eba47f44..624b501fd253 100644 --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -114,6 +114,7 @@ struct omap8250_priv { struct uart_8250_dma omap8250_dma; spinlock_t rx_dma_lock; bool rx_dma_broken; + bool throttled; }; #ifdef CONFIG_SERIAL_8250_DMA @@ -692,6 +693,7 @@ static void omap_8250_shutdown(struct uart_port *port) static void omap_8250_throttle(struct uart_port *port) { + struct omap8250_priv *priv = port->private_data; struct uart_8250_port *up = up_to_u8250p(port); unsigned long flags; @@ -700,6 +702,7 @@ static void omap_8250_throttle(struct uart_port *port) spin_lock_irqsave(&port->lock, flags); up->ier &= ~(UART_IER_RLSI | UART_IER_RDI); serial_out(up, UART_IER, up->ier); + priv->throttled = true; spin_unlock_irqrestore(&port->lock, flags); pm_runtime_mark_last_busy(port->dev); @@ -738,12 +741,16 @@ static int omap_8250_rs485_config(struct uart_port *port, static void omap_8250_unthrottle(struct uart_port *port) { + struct omap8250_priv *priv = port->private_data; struct uart_8250_port *up = up_to_u8250p(port); unsigned long flags; pm_runtime_get_sync(port->dev); spin_lock_irqsave(&port->lock, flags); + priv->throttled = false; + if (up->dma) + up->dma->rx_dma(up); up->ier |= UART_IER_RLSI | UART_IER_RDI; serial_out(up, UART_IER, up->ier); spin_unlock_irqrestore(&port->lock, flags); @@ -788,6 +795,7 @@ unlock: static void __dma_rx_complete(void *param) { struct uart_8250_port *p = param; + struct omap8250_priv *priv = p->port.private_data; struct uart_8250_dma *dma = p->dma; struct dma_tx_state state; unsigned long flags; @@ -805,7 +813,8 @@ static void __dma_rx_complete(void *param) return; } __dma_rx_do_complete(p); - omap_8250_rx_dma(p); + if (!priv->throttled) + omap_8250_rx_dma(p); spin_unlock_irqrestore(&p->port.lock, flags); } diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index a93f77ab3da0..3296a05cda2d 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -1685,9 +1685,6 @@ pci_wch_ch38x_setup(struct serial_private *priv, #define PCI_DEVICE_ID_BROADCOM_TRUMANAGE 0x160a #define PCI_DEVICE_ID_AMCC_ADDIDATA_APCI7800 0x818e -#define PCI_VENDOR_ID_SUNIX 0x1fd4 -#define PCI_DEVICE_ID_SUNIX_1999 0x1999 - #define PCIE_VENDOR_ID_WCH 0x1c00 #define PCIE_DEVICE_ID_WCH_CH382_2S1P 0x3250 #define PCIE_DEVICE_ID_WCH_CH384_4S 0x3470 diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index 1328c7e70108..95833cbc4338 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -47,6 +47,10 @@ #define UART_EXAR_SLEEP 0x8b /* Sleep mode */ #define UART_EXAR_DVID 0x8d /* Device identification */ +/* Nuvoton NPCM timeout register */ +#define UART_NPCM_TOR 7 +#define UART_NPCM_TOIE BIT(7) /* Timeout Interrupt Enable */ + /* * Debugging. */ @@ -293,6 +297,15 @@ static const struct serial8250_config uart_config[] = { UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT, .flags = UART_CAP_FIFO, }, + [PORT_NPCM] = { + .name = "Nuvoton 16550", + .fifo_size = 16, + .tx_loadsz = 16, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10 | + UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT, + .rxtrig_bytes = {1, 4, 8, 14}, + .flags = UART_CAP_FIFO, + }, }; /* Uart divisor latch read */ @@ -1854,7 +1867,8 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir) status = serial_port_in(port, UART_LSR); - if (status & (UART_LSR_DR | UART_LSR_BI)) { + if (status & (UART_LSR_DR | UART_LSR_BI) && + iir & UART_IIR_RDI) { if (!up->dma || handle_rx_dma(up, iir)) status = serial8250_rx_chars(up, status); } @@ -2140,6 +2154,15 @@ int serial8250_do_startup(struct uart_port *port) UART_DA830_PWREMU_MGMT_FREE); } + if (port->type == PORT_NPCM) { + /* + * Nuvoton calls the scratch register 'UART_TOR' (timeout + * register). Enable it, and set TIOC (timeout interrupt + * comparator) to be 0x20 for correct operation. + */ + serial_port_out(port, UART_NPCM_TOR, UART_NPCM_TOIE | 0x20); + } + #ifdef CONFIG_SERIAL_8250_RSA /* * If this is an RSA port, see if we can kick it up to the @@ -2462,6 +2485,15 @@ static unsigned int xr17v35x_get_divisor(struct uart_8250_port *up, return quot_16 >> 4; } +/* Nuvoton NPCM UARTs have a custom divisor calculation */ +static unsigned int npcm_get_divisor(struct uart_8250_port *up, + unsigned int baud) +{ + struct uart_port *port = &up->port; + + return DIV_ROUND_CLOSEST(port->uartclk, 16 * baud + 2) - 2; +} + static unsigned int serial8250_get_divisor(struct uart_8250_port *up, unsigned int baud, unsigned int *frac) @@ -2482,6 +2514,8 @@ static unsigned int serial8250_get_divisor(struct uart_8250_port *up, quot = 0x8002; else if (up->port.type == PORT_XR17V35X) quot = xr17v35x_get_divisor(up, baud, frac); + else if (up->port.type == PORT_NPCM) + quot = npcm_get_divisor(up, baud); else quot = uart_get_divisor(port, baud); diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig index 16b1496e6105..f005eaf8bc57 100644 --- a/drivers/tty/serial/8250/Kconfig +++ b/drivers/tty/serial/8250/Kconfig @@ -157,11 +157,12 @@ config SERIAL_8250_CS If unsure, say N. config SERIAL_8250_MEN_MCB - tristate "MEN Z125 UART device support" + tristate "MEN MCB UART device support" depends on MCB && SERIAL_8250 help This enables support for FPGA based UARTs found on many MEN - boards. This driver enables support for the Z125 UARTs. + boards. This driver enables support for the 16z025, 16z057 + and 16z125 UARTs. To compile this driver as a module, chose M here: the module will be called 8250_men_mcb. diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 736720a8d84f..0f058df0b070 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -989,6 +989,21 @@ config SERIAL_MSM_CONSOLE select SERIAL_CORE_CONSOLE select SERIAL_EARLYCON +config SERIAL_QCOM_GENI + tristate "QCOM on-chip GENI based serial port support" + depends on ARCH_QCOM || COMPILE_TEST + depends on QCOM_GENI_SE + select SERIAL_CORE + +config SERIAL_QCOM_GENI_CONSOLE + bool "QCOM GENI Serial Console support" + depends on SERIAL_QCOM_GENI=y + select SERIAL_CORE_CONSOLE + select SERIAL_EARLYCON + help + Serial console driver for Qualcomm Technologies Inc's GENI based + QUP hardware. + config SERIAL_VT8500 bool "VIA VT8500 on-chip serial port support" depends on ARCH_VT8500 diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index b30ee2e5d518..daac675612df 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -58,6 +58,7 @@ obj-$(CONFIG_SERIAL_SGI_IOC3) += ioc3_serial.o obj-$(CONFIG_SERIAL_ATMEL) += atmel_serial.o obj-$(CONFIG_SERIAL_UARTLITE) += uartlite.o obj-$(CONFIG_SERIAL_MSM) += msm_serial.o +obj-$(CONFIG_SERIAL_QCOM_GENI) += qcom_geni_serial.o obj-$(CONFIG_SERIAL_NETX) += netx-serial.o obj-$(CONFIG_SERIAL_KS8695) += serial_ks8695.o obj-$(CONFIG_SERIAL_OMAP) += omap-serial.o diff --git a/drivers/tty/serial/altera_uart.c b/drivers/tty/serial/altera_uart.c index b88b05f8e81e..0e487ce091ac 100644 --- a/drivers/tty/serial/altera_uart.c +++ b/drivers/tty/serial/altera_uart.c @@ -109,6 +109,20 @@ static unsigned int altera_uart_get_mctrl(struct uart_port *port) return sigs; } +static void altera_uart_update_ctrl_reg(struct altera_uart *pp) +{ + unsigned short imr = pp->imr; + + /* + * If the device doesn't have an irq, ensure that the irq bits are + * masked out to keep the irq line inactive. + */ + if (!pp->port.irq) + imr &= ALTERA_UART_CONTROL_TRBK_MSK | ALTERA_UART_CONTROL_RTS_MSK; + + altera_uart_writel(&pp->port, imr, ALTERA_UART_CONTROL_REG); +} + static void altera_uart_set_mctrl(struct uart_port *port, unsigned int sigs) { struct altera_uart *pp = container_of(port, struct altera_uart, port); @@ -118,7 +132,7 @@ static void altera_uart_set_mctrl(struct uart_port *port, unsigned int sigs) pp->imr |= ALTERA_UART_CONTROL_RTS_MSK; else pp->imr &= ~ALTERA_UART_CONTROL_RTS_MSK; - altera_uart_writel(port, pp->imr, ALTERA_UART_CONTROL_REG); + altera_uart_update_ctrl_reg(pp); } static void altera_uart_start_tx(struct uart_port *port) @@ -126,7 +140,7 @@ static void altera_uart_start_tx(struct uart_port *port) struct altera_uart *pp = container_of(port, struct altera_uart, port); pp->imr |= ALTERA_UART_CONTROL_TRDY_MSK; - altera_uart_writel(port, pp->imr, ALTERA_UART_CONTROL_REG); + altera_uart_update_ctrl_reg(pp); } static void altera_uart_stop_tx(struct uart_port *port) @@ -134,7 +148,7 @@ static void altera_uart_stop_tx(struct uart_port *port) struct altera_uart *pp = container_of(port, struct altera_uart, port); pp->imr &= ~ALTERA_UART_CONTROL_TRDY_MSK; - altera_uart_writel(port, pp->imr, ALTERA_UART_CONTROL_REG); + altera_uart_update_ctrl_reg(pp); } static void altera_uart_stop_rx(struct uart_port *port) @@ -142,7 +156,7 @@ static void altera_uart_stop_rx(struct uart_port *port) struct altera_uart *pp = container_of(port, struct altera_uart, port); pp->imr &= ~ALTERA_UART_CONTROL_RRDY_MSK; - altera_uart_writel(port, pp->imr, ALTERA_UART_CONTROL_REG); + altera_uart_update_ctrl_reg(pp); } static void altera_uart_break_ctl(struct uart_port *port, int break_state) @@ -155,7 +169,7 @@ static void altera_uart_break_ctl(struct uart_port *port, int break_state) pp->imr |= ALTERA_UART_CONTROL_TRBK_MSK; else pp->imr &= ~ALTERA_UART_CONTROL_TRBK_MSK; - altera_uart_writel(port, pp->imr, ALTERA_UART_CONTROL_REG); + altera_uart_update_ctrl_reg(pp); spin_unlock_irqrestore(&port->lock, flags); } @@ -262,7 +276,7 @@ static void altera_uart_tx_chars(struct altera_uart *pp) if (xmit->head == xmit->tail) { pp->imr &= ~ALTERA_UART_CONTROL_TRDY_MSK; - altera_uart_writel(port, pp->imr, ALTERA_UART_CONTROL_REG); + altera_uart_update_ctrl_reg(pp); } } @@ -307,27 +321,27 @@ static int altera_uart_startup(struct uart_port *port) { struct altera_uart *pp = container_of(port, struct altera_uart, port); unsigned long flags; - int ret; if (!port->irq) { timer_setup(&pp->tmr, altera_uart_timer, 0); mod_timer(&pp->tmr, jiffies + uart_poll_timeout(port)); - return 0; - } - - ret = request_irq(port->irq, altera_uart_interrupt, 0, - DRV_NAME, port); - if (ret) { - pr_err(DRV_NAME ": unable to attach Altera UART %d " - "interrupt vector=%d\n", port->line, port->irq); - return ret; + } else { + int ret; + + ret = request_irq(port->irq, altera_uart_interrupt, 0, + DRV_NAME, port); + if (ret) { + pr_err(DRV_NAME ": unable to attach Altera UART %d " + "interrupt vector=%d\n", port->line, port->irq); + return ret; + } } spin_lock_irqsave(&port->lock, flags); /* Enable RX interrupts now */ pp->imr = ALTERA_UART_CONTROL_RRDY_MSK; - writel(pp->imr, port->membase + ALTERA_UART_CONTROL_REG); + altera_uart_update_ctrl_reg(pp); spin_unlock_irqrestore(&port->lock, flags); @@ -343,7 +357,7 @@ static void altera_uart_shutdown(struct uart_port *port) /* Disable all interrupts now */ pp->imr = 0; - writel(pp->imr, port->membase + ALTERA_UART_CONTROL_REG); + altera_uart_update_ctrl_reg(pp); spin_unlock_irqrestore(&port->lock, flags); @@ -432,7 +446,7 @@ static void altera_uart_console_putc(struct uart_port *port, int c) ALTERA_UART_STATUS_TRDY_MSK)) cpu_relax(); - writel(c, port->membase + ALTERA_UART_TXDATA_REG); + altera_uart_writel(port, c, ALTERA_UART_TXDATA_REG); } static void altera_uart_console_write(struct console *co, const char *s, @@ -502,13 +516,13 @@ static int __init altera_uart_earlycon_setup(struct earlycon_device *dev, return -ENODEV; /* Enable RX interrupts now */ - writel(ALTERA_UART_CONTROL_RRDY_MSK, - port->membase + ALTERA_UART_CONTROL_REG); + altera_uart_writel(port, ALTERA_UART_CONTROL_RRDY_MSK, + ALTERA_UART_CONTROL_REG); if (dev->baud) { unsigned int baudclk = port->uartclk / dev->baud; - writel(baudclk, port->membase + ALTERA_UART_DIVISOR_REG); + altera_uart_writel(port, baudclk, ALTERA_UART_DIVISOR_REG); } dev->con->write = altera_uart_earlycon_write; diff --git a/drivers/tty/serial/arc_uart.c b/drivers/tty/serial/arc_uart.c index 2599f9ecccfe..d904a3a345e7 100644 --- a/drivers/tty/serial/arc_uart.c +++ b/drivers/tty/serial/arc_uart.c @@ -593,6 +593,11 @@ static int arc_serial_probe(struct platform_device *pdev) if (dev_id < 0) dev_id = 0; + if (dev_id >= ARRAY_SIZE(arc_uart_ports)) { + dev_err(&pdev->dev, "serial%d out of range\n", dev_id); + return -EINVAL; + } + uart = &arc_uart_ports[dev_id]; port = &uart->port; diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 8cf112f2efc3..51e47a63d61a 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -2145,6 +2145,10 @@ static int lpuart_probe(struct platform_device *pdev) dev_err(&pdev->dev, "failed to get alias id, errno %d\n", ret); return ret; } + if (ret >= ARRAY_SIZE(lpuart_ports)) { + dev_err(&pdev->dev, "serial%d out of range\n", ret); + return -EINVAL; + } sport->port.line = ret; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); sport->port.membase = devm_ioremap_resource(&pdev->dev, res); diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index a33c685af990..91f3a1a5cb7f 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -71,12 +71,12 @@ #define UCR1_IDEN (1<<12) /* Idle condition interrupt */ #define UCR1_ICD_REG(x) (((x) & 3) << 10) /* idle condition detect */ #define UCR1_RRDYEN (1<<9) /* Recv ready interrupt enable */ -#define UCR1_RDMAEN (1<<8) /* Recv ready DMA enable */ +#define UCR1_RXDMAEN (1<<8) /* Recv ready DMA enable */ #define UCR1_IREN (1<<7) /* Infrared interface enable */ #define UCR1_TXMPTYEN (1<<6) /* Transimitter empty interrupt enable */ #define UCR1_RTSDEN (1<<5) /* RTS delta interrupt enable */ #define UCR1_SNDBRK (1<<4) /* Send break */ -#define UCR1_TDMAEN (1<<3) /* Transmitter ready DMA enable */ +#define UCR1_TXDMAEN (1<<3) /* Transmitter ready DMA enable */ #define IMX1_UCR1_UARTCLKEN (1<<2) /* UART clock enabled, i.mx1 only */ #define UCR1_ATDMAEN (1<<2) /* Aging DMA Timer Enable */ #define UCR1_DOZE (1<<1) /* Doze */ @@ -204,8 +204,14 @@ struct imx_port { struct mctrl_gpios *gpios; + /* shadow registers */ + unsigned int ucr1; + unsigned int ucr2; + unsigned int ucr3; + unsigned int ucr4; + unsigned int ufcr; + /* DMA fields */ - unsigned int dma_is_inited:1; unsigned int dma_is_enabled:1; unsigned int dma_is_rxing:1; unsigned int dma_is_txing:1; @@ -274,27 +280,81 @@ static const struct of_device_id imx_uart_dt_ids[] = { }; MODULE_DEVICE_TABLE(of, imx_uart_dt_ids); -static inline unsigned uts_reg(struct imx_port *sport) +static void imx_uart_writel(struct imx_port *sport, u32 val, u32 offset) +{ + switch (offset) { + case UCR1: + sport->ucr1 = val; + break; + case UCR2: + sport->ucr2 = val; + break; + case UCR3: + sport->ucr3 = val; + break; + case UCR4: + sport->ucr4 = val; + break; + case UFCR: + sport->ufcr = val; + break; + default: + break; + } + writel(val, sport->port.membase + offset); +} + +static u32 imx_uart_readl(struct imx_port *sport, u32 offset) +{ + switch (offset) { + case UCR1: + return sport->ucr1; + break; + case UCR2: + /* + * UCR2_SRST is the only bit in the cached registers that might + * differ from the value that was last written. As it only + * clears after being set, reread conditionally. + */ + if (sport->ucr2 & UCR2_SRST) + sport->ucr2 = readl(sport->port.membase + offset); + return sport->ucr2; + break; + case UCR3: + return sport->ucr3; + break; + case UCR4: + return sport->ucr4; + break; + case UFCR: + return sport->ufcr; + break; + default: + return readl(sport->port.membase + offset); + } +} + +static inline unsigned imx_uart_uts_reg(struct imx_port *sport) { return sport->devdata->uts_reg; } -static inline int is_imx1_uart(struct imx_port *sport) +static inline int imx_uart_is_imx1(struct imx_port *sport) { return sport->devdata->devtype == IMX1_UART; } -static inline int is_imx21_uart(struct imx_port *sport) +static inline int imx_uart_is_imx21(struct imx_port *sport) { return sport->devdata->devtype == IMX21_UART; } -static inline int is_imx53_uart(struct imx_port *sport) +static inline int imx_uart_is_imx53(struct imx_port *sport) { return sport->devdata->devtype == IMX53_UART; } -static inline int is_imx6q_uart(struct imx_port *sport) +static inline int imx_uart_is_imx6q(struct imx_port *sport) { return sport->devdata->devtype == IMX6Q_UART; } @@ -302,26 +362,26 @@ static inline int is_imx6q_uart(struct imx_port *sport) * Save and restore functions for UCR1, UCR2 and UCR3 registers */ #if defined(CONFIG_SERIAL_IMX_CONSOLE) -static void imx_port_ucrs_save(struct uart_port *port, +static void imx_uart_ucrs_save(struct imx_port *sport, struct imx_port_ucrs *ucr) { /* save control registers */ - ucr->ucr1 = readl(port->membase + UCR1); - ucr->ucr2 = readl(port->membase + UCR2); - ucr->ucr3 = readl(port->membase + UCR3); + ucr->ucr1 = imx_uart_readl(sport, UCR1); + ucr->ucr2 = imx_uart_readl(sport, UCR2); + ucr->ucr3 = imx_uart_readl(sport, UCR3); } -static void imx_port_ucrs_restore(struct uart_port *port, +static void imx_uart_ucrs_restore(struct imx_port *sport, struct imx_port_ucrs *ucr) { /* restore control registers */ - writel(ucr->ucr1, port->membase + UCR1); - writel(ucr->ucr2, port->membase + UCR2); - writel(ucr->ucr3, port->membase + UCR3); + imx_uart_writel(sport, ucr->ucr1, UCR1); + imx_uart_writel(sport, ucr->ucr2, UCR2); + imx_uart_writel(sport, ucr->ucr3, UCR3); } #endif -static void imx_port_rts_active(struct imx_port *sport, unsigned long *ucr2) +static void imx_uart_rts_active(struct imx_port *sport, u32 *ucr2) { *ucr2 &= ~(UCR2_CTSC | UCR2_CTS); @@ -329,7 +389,7 @@ static void imx_port_rts_active(struct imx_port *sport, unsigned long *ucr2) mctrl_gpio_set(sport->gpios, sport->port.mctrl); } -static void imx_port_rts_inactive(struct imx_port *sport, unsigned long *ucr2) +static void imx_uart_rts_inactive(struct imx_port *sport, u32 *ucr2) { *ucr2 &= ~UCR2_CTSC; *ucr2 |= UCR2_CTS; @@ -338,75 +398,91 @@ static void imx_port_rts_inactive(struct imx_port *sport, unsigned long *ucr2) mctrl_gpio_set(sport->gpios, sport->port.mctrl); } -static void imx_port_rts_auto(struct imx_port *sport, unsigned long *ucr2) +static void imx_uart_rts_auto(struct imx_port *sport, u32 *ucr2) { *ucr2 |= UCR2_CTSC; } -/* - * interrupts disabled on entry - */ -static void imx_stop_tx(struct uart_port *port) +/* called with port.lock taken and irqs off */ +static void imx_uart_start_rx(struct uart_port *port) { struct imx_port *sport = (struct imx_port *)port; - unsigned long temp; + unsigned int ucr1, ucr2; + + ucr1 = imx_uart_readl(sport, UCR1); + ucr2 = imx_uart_readl(sport, UCR2); + + ucr2 |= UCR2_RXEN; + + if (sport->dma_is_enabled) { + ucr1 |= UCR1_RXDMAEN | UCR1_ATDMAEN; + } else { + ucr1 |= UCR1_RRDYEN; + ucr2 |= UCR2_ATEN; + } + + /* Write UCR2 first as it includes RXEN */ + imx_uart_writel(sport, ucr2, UCR2); + imx_uart_writel(sport, ucr1, UCR1); +} + +/* called with port.lock taken and irqs off */ +static void imx_uart_stop_tx(struct uart_port *port) +{ + struct imx_port *sport = (struct imx_port *)port; + u32 ucr1; /* * We are maybe in the SMP context, so if the DMA TX thread is running * on other cpu, we have to wait for it to finish. */ - if (sport->dma_is_enabled && sport->dma_is_txing) + if (sport->dma_is_txing) return; - temp = readl(port->membase + UCR1); - writel(temp & ~UCR1_TXMPTYEN, port->membase + UCR1); + ucr1 = imx_uart_readl(sport, UCR1); + imx_uart_writel(sport, ucr1 & ~UCR1_TXMPTYEN, UCR1); /* in rs485 mode disable transmitter if shifter is empty */ if (port->rs485.flags & SER_RS485_ENABLED && - readl(port->membase + USR2) & USR2_TXDC) { - temp = readl(port->membase + UCR2); + imx_uart_readl(sport, USR2) & USR2_TXDC) { + u32 ucr2 = imx_uart_readl(sport, UCR2), ucr4; if (port->rs485.flags & SER_RS485_RTS_AFTER_SEND) - imx_port_rts_active(sport, &temp); + imx_uart_rts_active(sport, &ucr2); else - imx_port_rts_inactive(sport, &temp); - temp |= UCR2_RXEN; - writel(temp, port->membase + UCR2); + imx_uart_rts_inactive(sport, &ucr2); + imx_uart_writel(sport, ucr2, UCR2); + + imx_uart_start_rx(port); - temp = readl(port->membase + UCR4); - temp &= ~UCR4_TCEN; - writel(temp, port->membase + UCR4); + ucr4 = imx_uart_readl(sport, UCR4); + ucr4 &= ~UCR4_TCEN; + imx_uart_writel(sport, ucr4, UCR4); } } -/* - * interrupts disabled on entry - */ -static void imx_stop_rx(struct uart_port *port) +/* called with port.lock taken and irqs off */ +static void imx_uart_stop_rx(struct uart_port *port) { struct imx_port *sport = (struct imx_port *)port; - unsigned long temp; + u32 ucr1, ucr2; - if (sport->dma_is_enabled && sport->dma_is_rxing) { - if (sport->port.suspended) { - dmaengine_terminate_all(sport->dma_chan_rx); - sport->dma_is_rxing = 0; - } else { - return; - } - } + ucr1 = imx_uart_readl(sport, UCR1); + ucr2 = imx_uart_readl(sport, UCR2); - temp = readl(sport->port.membase + UCR2); - writel(temp & ~UCR2_RXEN, sport->port.membase + UCR2); + if (sport->dma_is_enabled) { + ucr1 &= ~(UCR1_RXDMAEN | UCR1_ATDMAEN); + } else { + ucr1 &= ~UCR1_RRDYEN; + ucr2 &= ~UCR2_ATEN; + } + imx_uart_writel(sport, ucr1, UCR1); - /* disable the `Receiver Ready Interrrupt` */ - temp = readl(sport->port.membase + UCR1); - writel(temp & ~UCR1_RRDYEN, sport->port.membase + UCR1); + ucr2 &= ~UCR2_RXEN; + imx_uart_writel(sport, ucr2, UCR2); } -/* - * Set the modem control timer to fire immediately. - */ -static void imx_enable_ms(struct uart_port *port) +/* called with port.lock taken and irqs off */ +static void imx_uart_enable_ms(struct uart_port *port) { struct imx_port *sport = (struct imx_port *)port; @@ -415,49 +491,50 @@ static void imx_enable_ms(struct uart_port *port) mctrl_gpio_enable_ms(sport->gpios); } -static void imx_dma_tx(struct imx_port *sport); -static inline void imx_transmit_buffer(struct imx_port *sport) +static void imx_uart_dma_tx(struct imx_port *sport); + +/* called with port.lock taken and irqs off */ +static inline void imx_uart_transmit_buffer(struct imx_port *sport) { struct circ_buf *xmit = &sport->port.state->xmit; - unsigned long temp; if (sport->port.x_char) { /* Send next char */ - writel(sport->port.x_char, sport->port.membase + URTX0); + imx_uart_writel(sport, sport->port.x_char, URTX0); sport->port.icount.tx++; sport->port.x_char = 0; return; } if (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)) { - imx_stop_tx(&sport->port); + imx_uart_stop_tx(&sport->port); return; } if (sport->dma_is_enabled) { + u32 ucr1; /* * We've just sent a X-char Ensure the TX DMA is enabled * and the TX IRQ is disabled. **/ - temp = readl(sport->port.membase + UCR1); - temp &= ~UCR1_TXMPTYEN; + ucr1 = imx_uart_readl(sport, UCR1); + ucr1 &= ~UCR1_TXMPTYEN; if (sport->dma_is_txing) { - temp |= UCR1_TDMAEN; - writel(temp, sport->port.membase + UCR1); + ucr1 |= UCR1_TXDMAEN; + imx_uart_writel(sport, ucr1, UCR1); } else { - writel(temp, sport->port.membase + UCR1); - imx_dma_tx(sport); + imx_uart_writel(sport, ucr1, UCR1); + imx_uart_dma_tx(sport); } - } - if (sport->dma_is_txing) return; + } while (!uart_circ_empty(xmit) && - !(readl(sport->port.membase + uts_reg(sport)) & UTS_TXFULL)) { + !(imx_uart_readl(sport, imx_uart_uts_reg(sport)) & UTS_TXFULL)) { /* send xmit->buf[xmit->tail] * out the port here */ - writel(xmit->buf[xmit->tail], sport->port.membase + URTX0); + imx_uart_writel(sport, xmit->buf[xmit->tail], URTX0); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); sport->port.icount.tx++; } @@ -466,24 +543,24 @@ static inline void imx_transmit_buffer(struct imx_port *sport) uart_write_wakeup(&sport->port); if (uart_circ_empty(xmit)) - imx_stop_tx(&sport->port); + imx_uart_stop_tx(&sport->port); } -static void dma_tx_callback(void *data) +static void imx_uart_dma_tx_callback(void *data) { struct imx_port *sport = data; struct scatterlist *sgl = &sport->tx_sgl[0]; struct circ_buf *xmit = &sport->port.state->xmit; unsigned long flags; - unsigned long temp; + u32 ucr1; spin_lock_irqsave(&sport->port.lock, flags); dma_unmap_sg(sport->port.dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE); - temp = readl(sport->port.membase + UCR1); - temp &= ~UCR1_TDMAEN; - writel(temp, sport->port.membase + UCR1); + ucr1 = imx_uart_readl(sport, UCR1); + ucr1 &= ~UCR1_TXDMAEN; + imx_uart_writel(sport, ucr1, UCR1); /* update the stat */ xmit->tail = (xmit->tail + sport->tx_bytes) & (UART_XMIT_SIZE - 1); @@ -497,24 +574,34 @@ static void dma_tx_callback(void *data) uart_write_wakeup(&sport->port); if (!uart_circ_empty(xmit) && !uart_tx_stopped(&sport->port)) - imx_dma_tx(sport); + imx_uart_dma_tx(sport); + else if (sport->port.rs485.flags & SER_RS485_ENABLED) { + u32 ucr4 = imx_uart_readl(sport, UCR4); + ucr4 |= UCR4_TCEN; + imx_uart_writel(sport, ucr4, UCR4); + } spin_unlock_irqrestore(&sport->port.lock, flags); } -static void imx_dma_tx(struct imx_port *sport) +/* called with port.lock taken and irqs off */ +static void imx_uart_dma_tx(struct imx_port *sport) { struct circ_buf *xmit = &sport->port.state->xmit; struct scatterlist *sgl = sport->tx_sgl; struct dma_async_tx_descriptor *desc; struct dma_chan *chan = sport->dma_chan_tx; struct device *dev = sport->port.dev; - unsigned long temp; + u32 ucr1, ucr4; int ret; if (sport->dma_is_txing) return; + ucr4 = imx_uart_readl(sport, UCR4); + ucr4 &= ~UCR4_TCEN; + imx_uart_writel(sport, ucr4, UCR4); + sport->tx_bytes = uart_circ_chars_pending(xmit); if (xmit->tail < xmit->head) { @@ -541,15 +628,15 @@ static void imx_dma_tx(struct imx_port *sport) dev_err(dev, "We cannot prepare for the TX slave dma!\n"); return; } - desc->callback = dma_tx_callback; + desc->callback = imx_uart_dma_tx_callback; desc->callback_param = sport; dev_dbg(dev, "TX: prepare to send %lu bytes by DMA.\n", uart_circ_chars_pending(xmit)); - temp = readl(sport->port.membase + UCR1); - temp |= UCR1_TDMAEN; - writel(temp, sport->port.membase + UCR1); + ucr1 = imx_uart_readl(sport, UCR1); + ucr1 |= UCR1_TXDMAEN; + imx_uart_writel(sport, ucr1, UCR1); /* fire it */ sport->dma_is_txing = 1; @@ -558,99 +645,110 @@ static void imx_dma_tx(struct imx_port *sport) return; } -/* - * interrupts disabled on entry - */ -static void imx_start_tx(struct uart_port *port) +/* called with port.lock taken and irqs off */ +static void imx_uart_start_tx(struct uart_port *port) { struct imx_port *sport = (struct imx_port *)port; - unsigned long temp; + u32 ucr1; + + if (!sport->port.x_char && uart_circ_empty(&port->state->xmit)) + return; if (port->rs485.flags & SER_RS485_ENABLED) { - temp = readl(port->membase + UCR2); + u32 ucr2; + + ucr2 = imx_uart_readl(sport, UCR2); if (port->rs485.flags & SER_RS485_RTS_ON_SEND) - imx_port_rts_active(sport, &temp); + imx_uart_rts_active(sport, &ucr2); else - imx_port_rts_inactive(sport, &temp); + imx_uart_rts_inactive(sport, &ucr2); + imx_uart_writel(sport, ucr2, UCR2); + if (!(port->rs485.flags & SER_RS485_RX_DURING_TX)) - temp &= ~UCR2_RXEN; - writel(temp, port->membase + UCR2); + imx_uart_stop_rx(port); - /* enable transmitter and shifter empty irq */ - temp = readl(port->membase + UCR4); - temp |= UCR4_TCEN; - writel(temp, port->membase + UCR4); + /* + * Enable transmitter and shifter empty irq only if DMA is off. + * In the DMA case this is done in the tx-callback. + */ + if (!sport->dma_is_enabled) { + u32 ucr4 = imx_uart_readl(sport, UCR4); + ucr4 |= UCR4_TCEN; + imx_uart_writel(sport, ucr4, UCR4); + } } if (!sport->dma_is_enabled) { - temp = readl(sport->port.membase + UCR1); - writel(temp | UCR1_TXMPTYEN, sport->port.membase + UCR1); + ucr1 = imx_uart_readl(sport, UCR1); + imx_uart_writel(sport, ucr1 | UCR1_TXMPTYEN, UCR1); } if (sport->dma_is_enabled) { if (sport->port.x_char) { /* We have X-char to send, so enable TX IRQ and * disable TX DMA to let TX interrupt to send X-char */ - temp = readl(sport->port.membase + UCR1); - temp &= ~UCR1_TDMAEN; - temp |= UCR1_TXMPTYEN; - writel(temp, sport->port.membase + UCR1); + ucr1 = imx_uart_readl(sport, UCR1); + ucr1 &= ~UCR1_TXDMAEN; + ucr1 |= UCR1_TXMPTYEN; + imx_uart_writel(sport, ucr1, UCR1); return; } if (!uart_circ_empty(&port->state->xmit) && !uart_tx_stopped(port)) - imx_dma_tx(sport); + imx_uart_dma_tx(sport); return; } } -static irqreturn_t imx_rtsint(int irq, void *dev_id) +static irqreturn_t imx_uart_rtsint(int irq, void *dev_id) { struct imx_port *sport = dev_id; - unsigned int val; + u32 usr1; unsigned long flags; spin_lock_irqsave(&sport->port.lock, flags); - writel(USR1_RTSD, sport->port.membase + USR1); - val = readl(sport->port.membase + USR1) & USR1_RTSS; - uart_handle_cts_change(&sport->port, !!val); + imx_uart_writel(sport, USR1_RTSD, USR1); + usr1 = imx_uart_readl(sport, USR1) & USR1_RTSS; + uart_handle_cts_change(&sport->port, !!usr1); wake_up_interruptible(&sport->port.state->port.delta_msr_wait); spin_unlock_irqrestore(&sport->port.lock, flags); return IRQ_HANDLED; } -static irqreturn_t imx_txint(int irq, void *dev_id) +static irqreturn_t imx_uart_txint(int irq, void *dev_id) { struct imx_port *sport = dev_id; unsigned long flags; spin_lock_irqsave(&sport->port.lock, flags); - imx_transmit_buffer(sport); + imx_uart_transmit_buffer(sport); spin_unlock_irqrestore(&sport->port.lock, flags); return IRQ_HANDLED; } -static irqreturn_t imx_rxint(int irq, void *dev_id) +static irqreturn_t imx_uart_rxint(int irq, void *dev_id) { struct imx_port *sport = dev_id; unsigned int rx, flg, ignored = 0; struct tty_port *port = &sport->port.state->port; - unsigned long flags, temp; + unsigned long flags; spin_lock_irqsave(&sport->port.lock, flags); - while (readl(sport->port.membase + USR2) & USR2_RDR) { + while (imx_uart_readl(sport, USR2) & USR2_RDR) { + u32 usr2; + flg = TTY_NORMAL; sport->port.icount.rx++; - rx = readl(sport->port.membase + URXD0); + rx = imx_uart_readl(sport, URXD0); - temp = readl(sport->port.membase + USR2); - if (temp & USR2_BRCD) { - writel(USR2_BRCD, sport->port.membase + USR2); + usr2 = imx_uart_readl(sport, USR2); + if (usr2 & USR2_BRCD) { + imx_uart_writel(sport, USR2_BRCD, USR2); if (uart_handle_break(&sport->port)) continue; } @@ -703,16 +801,16 @@ out: return IRQ_HANDLED; } -static void clear_rx_errors(struct imx_port *sport); +static void imx_uart_clear_rx_errors(struct imx_port *sport); /* * We have a modem side uart, so the meanings of RTS and CTS are inverted. */ -static unsigned int imx_get_hwmctrl(struct imx_port *sport) +static unsigned int imx_uart_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); + unsigned usr1 = imx_uart_readl(sport, USR1); + unsigned usr2 = imx_uart_readl(sport, USR2); if (usr1 & USR1_RTSS) tmp |= TIOCM_CTS; @@ -722,7 +820,7 @@ static unsigned int imx_get_hwmctrl(struct imx_port *sport) tmp |= TIOCM_CAR; if (sport->dte_mode) - if (!(readl(sport->port.membase + USR2) & USR2_RIIN)) + if (!(imx_uart_readl(sport, USR2) & USR2_RIIN)) tmp |= TIOCM_RI; return tmp; @@ -731,11 +829,11 @@ static unsigned int imx_get_hwmctrl(struct imx_port *sport) /* * Handle any change of modem status signal since we were last called. */ -static void imx_mctrl_check(struct imx_port *sport) +static void imx_uart_mctrl_check(struct imx_port *sport) { unsigned int status, changed; - status = imx_get_hwmctrl(sport); + status = imx_uart_get_hwmctrl(sport); changed = status ^ sport->old_status; if (changed == 0) @@ -755,55 +853,79 @@ static void imx_mctrl_check(struct imx_port *sport) wake_up_interruptible(&sport->port.state->port.delta_msr_wait); } -static irqreturn_t imx_int(int irq, void *dev_id) +static irqreturn_t imx_uart_int(int irq, void *dev_id) { struct imx_port *sport = dev_id; - unsigned int sts; - unsigned int sts2; + unsigned int usr1, usr2, ucr1, ucr2, ucr3, ucr4; irqreturn_t ret = IRQ_NONE; - sts = readl(sport->port.membase + USR1); - sts2 = readl(sport->port.membase + USR2); + usr1 = imx_uart_readl(sport, USR1); + usr2 = imx_uart_readl(sport, USR2); + ucr1 = imx_uart_readl(sport, UCR1); + ucr2 = imx_uart_readl(sport, UCR2); + ucr3 = imx_uart_readl(sport, UCR3); + ucr4 = imx_uart_readl(sport, UCR4); - if (!sport->dma_is_enabled && (sts & (USR1_RRDY | USR1_AGTIM))) { - imx_rxint(irq, dev_id); + /* + * Even if a condition is true that can trigger an irq only handle it if + * the respective irq source is enabled. This prevents some undesired + * actions, for example if a character that sits in the RX FIFO and that + * should be fetched via DMA is tried to be fetched using PIO. Or the + * receiver is currently off and so reading from URXD0 results in an + * exception. So just mask the (raw) status bits for disabled irqs. + */ + if ((ucr1 & UCR1_RRDYEN) == 0) + usr1 &= ~USR1_RRDY; + if ((ucr2 & UCR2_ATEN) == 0) + usr1 &= ~USR1_AGTIM; + if ((ucr1 & UCR1_TXMPTYEN) == 0) + usr1 &= ~USR1_TRDY; + if ((ucr4 & UCR4_TCEN) == 0) + usr2 &= ~USR2_TXDC; + if ((ucr3 & UCR3_DTRDEN) == 0) + usr1 &= ~USR1_DTRD; + if ((ucr1 & UCR1_RTSDEN) == 0) + usr1 &= ~USR1_RTSD; + if ((ucr3 & UCR3_AWAKEN) == 0) + usr1 &= ~USR1_AWAKE; + if ((ucr4 & UCR4_OREN) == 0) + usr2 &= ~USR2_ORE; + + if (usr1 & (USR1_RRDY | USR1_AGTIM)) { + imx_uart_rxint(irq, dev_id); ret = IRQ_HANDLED; } - if ((sts & USR1_TRDY && - readl(sport->port.membase + UCR1) & UCR1_TXMPTYEN) || - (sts2 & USR2_TXDC && - readl(sport->port.membase + UCR4) & UCR4_TCEN)) { - imx_txint(irq, dev_id); + if ((usr1 & USR1_TRDY) || (usr2 & USR2_TXDC)) { + imx_uart_txint(irq, dev_id); ret = IRQ_HANDLED; } - if (sts & USR1_DTRD) { + if (usr1 & USR1_DTRD) { unsigned long flags; - if (sts & USR1_DTRD) - writel(USR1_DTRD, sport->port.membase + USR1); + imx_uart_writel(sport, USR1_DTRD, USR1); spin_lock_irqsave(&sport->port.lock, flags); - imx_mctrl_check(sport); + imx_uart_mctrl_check(sport); spin_unlock_irqrestore(&sport->port.lock, flags); ret = IRQ_HANDLED; } - if (sts & USR1_RTSD) { - imx_rtsint(irq, dev_id); + if (usr1 & USR1_RTSD) { + imx_uart_rtsint(irq, dev_id); ret = IRQ_HANDLED; } - if (sts & USR1_AWAKE) { - writel(USR1_AWAKE, sport->port.membase + USR1); + if (usr1 & USR1_AWAKE) { + imx_uart_writel(sport, USR1_AWAKE, USR1); ret = IRQ_HANDLED; } - if (sts2 & USR2_ORE) { + if (usr2 & USR2_ORE) { sport->port.icount.overrun++; - writel(USR2_ORE, sport->port.membase + USR2); + imx_uart_writel(sport, USR2_ORE, USR2); ret = IRQ_HANDLED; } @@ -813,52 +935,56 @@ static irqreturn_t imx_int(int irq, void *dev_id) /* * Return TIOCSER_TEMT when transmitter is not busy. */ -static unsigned int imx_tx_empty(struct uart_port *port) +static unsigned int imx_uart_tx_empty(struct uart_port *port) { struct imx_port *sport = (struct imx_port *)port; unsigned int ret; - ret = (readl(sport->port.membase + USR2) & USR2_TXDC) ? TIOCSER_TEMT : 0; + ret = (imx_uart_readl(sport, USR2) & USR2_TXDC) ? TIOCSER_TEMT : 0; /* If the TX DMA is working, return 0. */ - if (sport->dma_is_enabled && sport->dma_is_txing) + if (sport->dma_is_txing) ret = 0; return ret; } -static unsigned int imx_get_mctrl(struct uart_port *port) +/* called with port.lock taken and irqs off */ +static unsigned int imx_uart_get_mctrl(struct uart_port *port) { struct imx_port *sport = (struct imx_port *)port; - unsigned int ret = imx_get_hwmctrl(sport); + unsigned int ret = imx_uart_get_hwmctrl(sport); mctrl_gpio_get(sport->gpios, &ret); return ret; } -static void imx_set_mctrl(struct uart_port *port, unsigned int mctrl) +/* called with port.lock taken and irqs off */ +static void imx_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) { struct imx_port *sport = (struct imx_port *)port; - unsigned long temp; + u32 ucr3, uts; if (!(port->rs485.flags & SER_RS485_ENABLED)) { - temp = readl(sport->port.membase + UCR2); - temp &= ~(UCR2_CTS | UCR2_CTSC); + u32 ucr2; + + ucr2 = imx_uart_readl(sport, UCR2); + ucr2 &= ~(UCR2_CTS | UCR2_CTSC); if (mctrl & TIOCM_RTS) - temp |= UCR2_CTS | UCR2_CTSC; - writel(temp, sport->port.membase + UCR2); + ucr2 |= UCR2_CTS | UCR2_CTSC; + imx_uart_writel(sport, ucr2, UCR2); } - temp = readl(sport->port.membase + UCR3) & ~UCR3_DSR; + ucr3 = imx_uart_readl(sport, UCR3) & ~UCR3_DSR; if (!(mctrl & TIOCM_DTR)) - temp |= UCR3_DSR; - writel(temp, sport->port.membase + UCR3); + ucr3 |= UCR3_DSR; + imx_uart_writel(sport, ucr3, UCR3); - temp = readl(sport->port.membase + uts_reg(sport)) & ~UTS_LOOP; + uts = imx_uart_readl(sport, imx_uart_uts_reg(sport)) & ~UTS_LOOP; if (mctrl & TIOCM_LOOP) - temp |= UTS_LOOP; - writel(temp, sport->port.membase + uts_reg(sport)); + uts |= UTS_LOOP; + imx_uart_writel(sport, uts, imx_uart_uts_reg(sport)); mctrl_gpio_set(sport->gpios, mctrl); } @@ -866,19 +992,20 @@ static void imx_set_mctrl(struct uart_port *port, unsigned int mctrl) /* * Interrupts always disabled. */ -static void imx_break_ctl(struct uart_port *port, int break_state) +static void imx_uart_break_ctl(struct uart_port *port, int break_state) { struct imx_port *sport = (struct imx_port *)port; - unsigned long flags, temp; + unsigned long flags; + u32 ucr1; spin_lock_irqsave(&sport->port.lock, flags); - temp = readl(sport->port.membase + UCR1) & ~UCR1_SNDBRK; + ucr1 = imx_uart_readl(sport, UCR1) & ~UCR1_SNDBRK; if (break_state != 0) - temp |= UCR1_SNDBRK; + ucr1 |= UCR1_SNDBRK; - writel(temp, sport->port.membase + UCR1); + imx_uart_writel(sport, ucr1, UCR1); spin_unlock_irqrestore(&sport->port.lock, flags); } @@ -887,14 +1014,14 @@ static void imx_break_ctl(struct uart_port *port, int break_state) * This is our per-port timeout handler, for checking the * modem status signals. */ -static void imx_timeout(struct timer_list *t) +static void imx_uart_timeout(struct timer_list *t) { struct imx_port *sport = from_timer(sport, t, timer); unsigned long flags; if (sport->port.state) { spin_lock_irqsave(&sport->port.lock, flags); - imx_mctrl_check(sport); + imx_uart_mctrl_check(sport); spin_unlock_irqrestore(&sport->port.lock, flags); mod_timer(&sport->timer, jiffies + MCTRL_TIMEOUT); @@ -911,7 +1038,7 @@ static void imx_timeout(struct timer_list *t) * Condition [2] is triggered when a character has been sitting in the FIFO * for at least 8 byte durations. */ -static void dma_rx_callback(void *data) +static void imx_uart_dma_rx_callback(void *data) { struct imx_port *sport = data; struct dma_chan *chan = sport->dma_chan_rx; @@ -927,8 +1054,7 @@ static void dma_rx_callback(void *data) status = dmaengine_tx_status(chan, (dma_cookie_t)0, &state); if (status == DMA_ERROR) { - dev_err(sport->port.dev, "DMA transaction error.\n"); - clear_rx_errors(sport); + imx_uart_clear_rx_errors(sport); return; } @@ -988,7 +1114,7 @@ static void dma_rx_callback(void *data) /* RX DMA buffer periods */ #define RX_DMA_PERIODS 4 -static int start_rx_dma(struct imx_port *sport) +static int imx_uart_start_rx_dma(struct imx_port *sport) { struct scatterlist *sgl = &sport->rx_sgl; struct dma_chan *chan = sport->dma_chan_rx; @@ -1016,7 +1142,7 @@ static int start_rx_dma(struct imx_port *sport) dev_err(dev, "We cannot prepare for the RX slave dma!\n"); return -EINVAL; } - desc->callback = dma_rx_callback; + desc->callback = imx_uart_dma_rx_callback; desc->callback_param = sport; dev_dbg(dev, "RX: prepare for the DMA.\n"); @@ -1026,27 +1152,35 @@ static int start_rx_dma(struct imx_port *sport) return 0; } -static void clear_rx_errors(struct imx_port *sport) +static void imx_uart_clear_rx_errors(struct imx_port *sport) { - unsigned int status_usr1, status_usr2; + struct tty_port *port = &sport->port.state->port; + u32 usr1, usr2; - status_usr1 = readl(sport->port.membase + USR1); - status_usr2 = readl(sport->port.membase + USR2); + usr1 = imx_uart_readl(sport, USR1); + usr2 = imx_uart_readl(sport, USR2); - if (status_usr2 & USR2_BRCD) { + if (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); + imx_uart_writel(sport, USR2_BRCD, USR2); + uart_handle_break(&sport->port); + if (tty_insert_flip_char(port, 0, TTY_BREAK) == 0) + sport->port.icount.buf_overrun++; + tty_flip_buffer_push(port); + } else { + dev_err(sport->port.dev, "DMA transaction error.\n"); + if (usr1 & USR1_FRAMERR) { + sport->port.icount.frame++; + imx_uart_writel(sport, USR1_FRAMERR, USR1); + } else if (usr1 & USR1_PARITYERR) { + sport->port.icount.parity++; + imx_uart_writel(sport, USR1_PARITYERR, USR1); + } } - if (status_usr2 & USR2_ORE) { + if (usr2 & USR2_ORE) { sport->port.icount.overrun++; - writel(USR2_ORE, sport->port.membase + USR2); + imx_uart_writel(sport, USR2_ORE, USR2); } } @@ -1056,15 +1190,15 @@ static void clear_rx_errors(struct imx_port *sport) #define TXTL_DMA 8 /* DMA burst setting */ #define RXTL_DMA 9 /* DMA burst setting */ -static void imx_setup_ufcr(struct imx_port *sport, - unsigned char txwl, unsigned char rxwl) +static void imx_uart_setup_ufcr(struct imx_port *sport, + unsigned char txwl, unsigned char rxwl) { unsigned int val; /* set receiver / transmitter trigger level */ - val = readl(sport->port.membase + UFCR) & (UFCR_RFDIV | UFCR_DCEDTE); + val = imx_uart_readl(sport, UFCR) & (UFCR_RFDIV | UFCR_DCEDTE); val |= txwl << UFCR_TXTL_SHF | rxwl; - writel(val, sport->port.membase + UFCR); + imx_uart_writel(sport, val, UFCR); } static void imx_uart_dma_exit(struct imx_port *sport) @@ -1083,8 +1217,6 @@ static void imx_uart_dma_exit(struct imx_port *sport) dma_release_channel(sport->dma_chan_tx); sport->dma_chan_tx = NULL; } - - sport->dma_is_inited = 0; } static int imx_uart_dma_init(struct imx_port *sport) @@ -1137,43 +1269,41 @@ static int imx_uart_dma_init(struct imx_port *sport) goto err; } - sport->dma_is_inited = 1; - return 0; err: imx_uart_dma_exit(sport); return ret; } -static void imx_enable_dma(struct imx_port *sport) +static void imx_uart_enable_dma(struct imx_port *sport) { - unsigned long temp; + u32 ucr1; - /* set UCR1 */ - temp = readl(sport->port.membase + UCR1); - temp |= UCR1_RDMAEN | UCR1_TDMAEN | UCR1_ATDMAEN; - writel(temp, sport->port.membase + UCR1); + imx_uart_setup_ufcr(sport, TXTL_DMA, RXTL_DMA); - imx_setup_ufcr(sport, TXTL_DMA, RXTL_DMA); + /* set UCR1 */ + ucr1 = imx_uart_readl(sport, UCR1); + ucr1 |= UCR1_RXDMAEN | UCR1_TXDMAEN | UCR1_ATDMAEN; + imx_uart_writel(sport, ucr1, UCR1); sport->dma_is_enabled = 1; } -static void imx_disable_dma(struct imx_port *sport) +static void imx_uart_disable_dma(struct imx_port *sport) { - unsigned long temp; + u32 ucr1, ucr2; /* clear UCR1 */ - temp = readl(sport->port.membase + UCR1); - temp &= ~(UCR1_RDMAEN | UCR1_TDMAEN | UCR1_ATDMAEN); - writel(temp, sport->port.membase + UCR1); + ucr1 = imx_uart_readl(sport, UCR1); + ucr1 &= ~(UCR1_RXDMAEN | UCR1_TXDMAEN | UCR1_ATDMAEN); + imx_uart_writel(sport, ucr1, UCR1); /* clear UCR2 */ - temp = readl(sport->port.membase + UCR2); - temp &= ~(UCR2_CTSC | UCR2_CTS | UCR2_ATEN); - writel(temp, sport->port.membase + UCR2); + ucr2 = imx_uart_readl(sport, UCR2); + ucr2 &= ~(UCR2_CTSC | UCR2_CTS | UCR2_ATEN); + imx_uart_writel(sport, ucr2, UCR2); - imx_setup_ufcr(sport, TXTL_DEFAULT, RXTL_DEFAULT); + imx_uart_setup_ufcr(sport, TXTL_DEFAULT, RXTL_DEFAULT); sport->dma_is_enabled = 0; } @@ -1181,11 +1311,13 @@ static void imx_disable_dma(struct imx_port *sport) /* half the RX buffer size */ #define CTSTL 16 -static int imx_startup(struct uart_port *port) +static int imx_uart_startup(struct uart_port *port) { struct imx_port *sport = (struct imx_port *)port; int retval, i; - unsigned long flags, temp; + unsigned long flags; + int dma_is_inited = 0; + u32 ucr1, ucr2, ucr4; retval = clk_prepare_enable(sport->clk_per); if (retval) @@ -1196,104 +1328,106 @@ static int imx_startup(struct uart_port *port) return retval; } - imx_setup_ufcr(sport, TXTL_DEFAULT, RXTL_DEFAULT); + imx_uart_setup_ufcr(sport, TXTL_DEFAULT, RXTL_DEFAULT); /* disable the DREN bit (Data Ready interrupt enable) before * requesting IRQs */ - temp = readl(sport->port.membase + UCR4); + ucr4 = imx_uart_readl(sport, UCR4); /* set the trigger level for CTS */ - temp &= ~(UCR4_CTSTL_MASK << UCR4_CTSTL_SHF); - temp |= CTSTL << UCR4_CTSTL_SHF; + ucr4 &= ~(UCR4_CTSTL_MASK << UCR4_CTSTL_SHF); + ucr4 |= CTSTL << UCR4_CTSTL_SHF; - writel(temp & ~UCR4_DREN, sport->port.membase + UCR4); + imx_uart_writel(sport, ucr4 & ~UCR4_DREN, UCR4); /* Can we enable the DMA support? */ - if (!uart_console(port) && !sport->dma_is_inited) - imx_uart_dma_init(sport); + if (!uart_console(port) && imx_uart_dma_init(sport) == 0) + dma_is_inited = 1; spin_lock_irqsave(&sport->port.lock, flags); /* Reset fifo's and state machines */ i = 100; - temp = readl(sport->port.membase + UCR2); - temp &= ~UCR2_SRST; - writel(temp, sport->port.membase + UCR2); + ucr2 = imx_uart_readl(sport, UCR2); + ucr2 &= ~UCR2_SRST; + imx_uart_writel(sport, ucr2, UCR2); - while (!(readl(sport->port.membase + UCR2) & UCR2_SRST) && (--i > 0)) + while (!(imx_uart_readl(sport, UCR2) & UCR2_SRST) && (--i > 0)) udelay(1); /* * Finally, clear and enable interrupts */ - writel(USR1_RTSD | USR1_DTRD, sport->port.membase + USR1); - writel(USR2_ORE, sport->port.membase + USR2); - - if (sport->dma_is_inited && !sport->dma_is_enabled) - imx_enable_dma(sport); + imx_uart_writel(sport, USR1_RTSD | USR1_DTRD, USR1); + imx_uart_writel(sport, USR2_ORE, USR2); - temp = readl(sport->port.membase + UCR1) & ~UCR1_RRDYEN; - if (!sport->dma_is_enabled) - temp |= UCR1_RRDYEN; - temp |= UCR1_UARTEN; + ucr1 = imx_uart_readl(sport, UCR1) & ~UCR1_RRDYEN; + ucr1 |= UCR1_UARTEN; if (sport->have_rtscts) - temp |= UCR1_RTSDEN; + ucr1 |= UCR1_RTSDEN; - writel(temp, sport->port.membase + UCR1); + imx_uart_writel(sport, ucr1, UCR1); - temp = readl(sport->port.membase + UCR4) & ~UCR4_OREN; + ucr4 = imx_uart_readl(sport, UCR4) & ~UCR4_OREN; if (!sport->dma_is_enabled) - temp |= UCR4_OREN; - writel(temp, sport->port.membase + UCR4); + ucr4 |= UCR4_OREN; + imx_uart_writel(sport, ucr4, UCR4); - temp = readl(sport->port.membase + UCR2) & ~UCR2_ATEN; - temp |= (UCR2_RXEN | UCR2_TXEN); + ucr2 = imx_uart_readl(sport, UCR2) & ~UCR2_ATEN; + ucr2 |= (UCR2_RXEN | UCR2_TXEN); if (!sport->have_rtscts) - temp |= UCR2_IRTS; + ucr2 |= UCR2_IRTS; /* * make sure the edge sensitive RTS-irq is disabled, * we're using RTSD instead. */ - if (!is_imx1_uart(sport)) - temp &= ~UCR2_RTSEN; - writel(temp, sport->port.membase + UCR2); + if (!imx_uart_is_imx1(sport)) + ucr2 &= ~UCR2_RTSEN; + imx_uart_writel(sport, ucr2, UCR2); + + if (!imx_uart_is_imx1(sport)) { + u32 ucr3; - if (!is_imx1_uart(sport)) { - temp = readl(sport->port.membase + UCR3); + ucr3 = imx_uart_readl(sport, UCR3); - temp |= UCR3_DTRDEN | UCR3_RI | UCR3_DCD; + ucr3 |= UCR3_DTRDEN | UCR3_RI | UCR3_DCD; if (sport->dte_mode) /* disable broken interrupts */ - temp &= ~(UCR3_RI | UCR3_DCD); + ucr3 &= ~(UCR3_RI | UCR3_DCD); - writel(temp, sport->port.membase + UCR3); + imx_uart_writel(sport, ucr3, UCR3); } /* * Enable modem status interrupts */ - imx_enable_ms(&sport->port); + imx_uart_enable_ms(&sport->port); - /* - * Start RX DMA immediately instead of waiting for RX FIFO interrupts. - * In our iMX53 the average delay for the first reception dropped from - * approximately 35000 microseconds to 1000 microseconds. - */ - if (sport->dma_is_enabled) - start_rx_dma(sport); + if (dma_is_inited) { + imx_uart_enable_dma(sport); + imx_uart_start_rx_dma(sport); + } else { + ucr1 = imx_uart_readl(sport, UCR1); + ucr1 |= UCR1_RRDYEN; + imx_uart_writel(sport, ucr1, UCR1); + + ucr2 = imx_uart_readl(sport, UCR2); + ucr2 |= UCR2_ATEN; + imx_uart_writel(sport, ucr2, UCR2); + } spin_unlock_irqrestore(&sport->port.lock, flags); return 0; } -static void imx_shutdown(struct uart_port *port) +static void imx_uart_shutdown(struct uart_port *port) { struct imx_port *sport = (struct imx_port *)port; - unsigned long temp; unsigned long flags; + u32 ucr1, ucr2; if (sport->dma_is_enabled) { sport->dma_is_rxing = 0; @@ -1302,9 +1436,9 @@ static void imx_shutdown(struct uart_port *port) dmaengine_terminate_sync(sport->dma_chan_rx); spin_lock_irqsave(&sport->port.lock, flags); - imx_stop_tx(port); - imx_stop_rx(port); - imx_disable_dma(sport); + imx_uart_stop_tx(port); + imx_uart_stop_rx(port); + imx_uart_disable_dma(sport); spin_unlock_irqrestore(&sport->port.lock, flags); imx_uart_dma_exit(sport); } @@ -1312,9 +1446,9 @@ static void imx_shutdown(struct uart_port *port) mctrl_gpio_disable_ms(sport->gpios); spin_lock_irqsave(&sport->port.lock, flags); - temp = readl(sport->port.membase + UCR2); - temp &= ~(UCR2_TXEN); - writel(temp, sport->port.membase + UCR2); + ucr2 = imx_uart_readl(sport, UCR2); + ucr2 &= ~(UCR2_TXEN | UCR2_ATEN); + imx_uart_writel(sport, ucr2, UCR2); spin_unlock_irqrestore(&sport->port.lock, flags); /* @@ -1327,21 +1461,22 @@ static void imx_shutdown(struct uart_port *port) */ spin_lock_irqsave(&sport->port.lock, flags); - temp = readl(sport->port.membase + UCR1); - temp &= ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN); + ucr1 = imx_uart_readl(sport, UCR1); + ucr1 &= ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN | UCR1_RXDMAEN | UCR1_ATDMAEN); - writel(temp, sport->port.membase + UCR1); + imx_uart_writel(sport, ucr1, UCR1); spin_unlock_irqrestore(&sport->port.lock, flags); clk_disable_unprepare(sport->clk_per); clk_disable_unprepare(sport->clk_ipg); } -static void imx_flush_buffer(struct uart_port *port) +/* called with port.lock taken and irqs off */ +static void imx_uart_flush_buffer(struct uart_port *port) { struct imx_port *sport = (struct imx_port *)port; struct scatterlist *sgl = &sport->tx_sgl[0]; - unsigned long temp; + u32 ucr2; int i = 100, ubir, ubmr, uts; if (!sport->dma_chan_tx) @@ -1350,11 +1485,13 @@ static void imx_flush_buffer(struct uart_port *port) sport->tx_bytes = 0; dmaengine_terminate_all(sport->dma_chan_tx); if (sport->dma_is_txing) { + u32 ucr1; + dma_unmap_sg(sport->port.dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE); - temp = readl(sport->port.membase + UCR1); - temp &= ~UCR1_TDMAEN; - writel(temp, sport->port.membase + UCR1); + ucr1 = imx_uart_readl(sport, UCR1); + ucr1 &= ~UCR1_TXDMAEN; + imx_uart_writel(sport, ucr1, UCR1); sport->dma_is_txing = 0; } @@ -1369,33 +1506,33 @@ static void imx_flush_buffer(struct uart_port *port) * UTXD. UBRC is read only, so only save/restore the other three * registers. */ - ubir = readl(sport->port.membase + UBIR); - ubmr = readl(sport->port.membase + UBMR); - uts = readl(sport->port.membase + IMX21_UTS); + ubir = imx_uart_readl(sport, UBIR); + ubmr = imx_uart_readl(sport, UBMR); + uts = imx_uart_readl(sport, IMX21_UTS); - temp = readl(sport->port.membase + UCR2); - temp &= ~UCR2_SRST; - writel(temp, sport->port.membase + UCR2); + ucr2 = imx_uart_readl(sport, UCR2); + ucr2 &= ~UCR2_SRST; + imx_uart_writel(sport, ucr2, UCR2); - while (!(readl(sport->port.membase + UCR2) & UCR2_SRST) && (--i > 0)) + while (!(imx_uart_readl(sport, UCR2) & UCR2_SRST) && (--i > 0)) udelay(1); /* Restore the registers */ - writel(ubir, sport->port.membase + UBIR); - writel(ubmr, sport->port.membase + UBMR); - writel(uts, sport->port.membase + IMX21_UTS); + imx_uart_writel(sport, ubir, UBIR); + imx_uart_writel(sport, ubmr, UBMR); + imx_uart_writel(sport, uts, IMX21_UTS); } static void -imx_set_termios(struct uart_port *port, struct ktermios *termios, - struct ktermios *old) +imx_uart_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) { struct imx_port *sport = (struct imx_port *)port; unsigned long flags; - unsigned long ucr2, old_ucr1, old_ucr2; + u32 ucr2, old_ucr1, old_ucr2, ufcr; unsigned int baud, quot; unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8; - unsigned long div, ufcr; + unsigned long div; unsigned long num, denom; uint64_t tdiv64; @@ -1426,11 +1563,11 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios, */ if (port->rs485.flags & SER_RS485_RTS_AFTER_SEND) - imx_port_rts_active(sport, &ucr2); + imx_uart_rts_active(sport, &ucr2); else - imx_port_rts_inactive(sport, &ucr2); + imx_uart_rts_inactive(sport, &ucr2); } else { - imx_port_rts_auto(sport, &ucr2); + imx_uart_rts_auto(sport, &ucr2); } } else { termios->c_cflag &= ~CRTSCTS; @@ -1438,9 +1575,9 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios, } else if (port->rs485.flags & SER_RS485_ENABLED) { /* disable transmitter */ if (port->rs485.flags & SER_RS485_RTS_AFTER_SEND) - imx_port_rts_active(sport, &ucr2); + imx_uart_rts_active(sport, &ucr2); else - imx_port_rts_inactive(sport, &ucr2); + imx_uart_rts_inactive(sport, &ucr2); } @@ -1495,17 +1632,18 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios, /* * disable interrupts and drain transmitter */ - old_ucr1 = readl(sport->port.membase + UCR1); - writel(old_ucr1 & ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN), - sport->port.membase + UCR1); - - while (!(readl(sport->port.membase + USR2) & USR2_TXDC)) + old_ucr1 = imx_uart_readl(sport, UCR1); + imx_uart_writel(sport, + old_ucr1 & ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN), + UCR1); + old_ucr2 = imx_uart_readl(sport, UCR2); + imx_uart_writel(sport, old_ucr2 & ~UCR2_ATEN, UCR2); + + while (!(imx_uart_readl(sport, USR2) & USR2_TXDC)) barrier(); /* then, disable everything */ - old_ucr2 = readl(sport->port.membase + UCR2); - writel(old_ucr2 & ~(UCR2_TXEN | UCR2_RXEN), - sport->port.membase + UCR2); + imx_uart_writel(sport, old_ucr2 & ~(UCR2_TXEN | UCR2_RXEN | UCR2_ATEN), UCR2); old_ucr2 &= (UCR2_TXEN | UCR2_RXEN | UCR2_ATEN); /* custom-baudrate handling */ @@ -1531,29 +1669,29 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios, num -= 1; denom -= 1; - ufcr = readl(sport->port.membase + UFCR); + ufcr = imx_uart_readl(sport, UFCR); ufcr = (ufcr & (~UFCR_RFDIV)) | UFCR_RFDIV_REG(div); - writel(ufcr, sport->port.membase + UFCR); + imx_uart_writel(sport, ufcr, UFCR); - writel(num, sport->port.membase + UBIR); - writel(denom, sport->port.membase + UBMR); + imx_uart_writel(sport, num, UBIR); + imx_uart_writel(sport, denom, UBMR); - if (!is_imx1_uart(sport)) - writel(sport->port.uartclk / div / 1000, - sport->port.membase + IMX21_ONEMS); + if (!imx_uart_is_imx1(sport)) + imx_uart_writel(sport, sport->port.uartclk / div / 1000, + IMX21_ONEMS); - writel(old_ucr1, sport->port.membase + UCR1); + imx_uart_writel(sport, old_ucr1, UCR1); /* set the parity, stop bits and data size */ - writel(ucr2 | old_ucr2, sport->port.membase + UCR2); + imx_uart_writel(sport, ucr2 | old_ucr2, UCR2); if (UART_ENABLE_MS(&sport->port, termios->c_cflag)) - imx_enable_ms(&sport->port); + imx_uart_enable_ms(&sport->port); spin_unlock_irqrestore(&sport->port.lock, flags); } -static const char *imx_type(struct uart_port *port) +static const char *imx_uart_type(struct uart_port *port) { struct imx_port *sport = (struct imx_port *)port; @@ -1563,7 +1701,7 @@ static const char *imx_type(struct uart_port *port) /* * Configure/autoconfigure the port. */ -static void imx_config_port(struct uart_port *port, int flags) +static void imx_uart_config_port(struct uart_port *port, int flags) { struct imx_port *sport = (struct imx_port *)port; @@ -1577,7 +1715,7 @@ static void imx_config_port(struct uart_port *port, int flags) * even then only between PORT_IMX and PORT_UNKNOWN */ static int -imx_verify_port(struct uart_port *port, struct serial_struct *ser) +imx_uart_verify_port(struct uart_port *port, struct serial_struct *ser) { struct imx_port *sport = (struct imx_port *)port; int ret = 0; @@ -1601,11 +1739,11 @@ imx_verify_port(struct uart_port *port, struct serial_struct *ser) #if defined(CONFIG_CONSOLE_POLL) -static int imx_poll_init(struct uart_port *port) +static int imx_uart_poll_init(struct uart_port *port) { struct imx_port *sport = (struct imx_port *)port; unsigned long flags; - unsigned long temp; + u32 ucr1, ucr2; int retval; retval = clk_prepare_enable(sport->clk_ipg); @@ -1615,58 +1753,76 @@ static int imx_poll_init(struct uart_port *port) if (retval) clk_disable_unprepare(sport->clk_ipg); - imx_setup_ufcr(sport, TXTL_DEFAULT, RXTL_DEFAULT); + imx_uart_setup_ufcr(sport, TXTL_DEFAULT, RXTL_DEFAULT); spin_lock_irqsave(&sport->port.lock, flags); - temp = readl(sport->port.membase + UCR1); - if (is_imx1_uart(sport)) - temp |= IMX1_UCR1_UARTCLKEN; - temp |= UCR1_UARTEN | UCR1_RRDYEN; - temp &= ~(UCR1_TXMPTYEN | UCR1_RTSDEN); - writel(temp, sport->port.membase + UCR1); + /* + * Be careful about the order of enabling bits here. First enable the + * receiver (UARTEN + RXEN) and only then the corresponding irqs. + * This prevents that a character that already sits in the RX fifo is + * triggering an irq but the try to fetch it from there results in an + * exception because UARTEN or RXEN is still off. + */ + ucr1 = imx_uart_readl(sport, UCR1); + ucr2 = imx_uart_readl(sport, UCR2); - temp = readl(sport->port.membase + UCR2); - temp |= UCR2_RXEN; - writel(temp, sport->port.membase + UCR2); + if (imx_uart_is_imx1(sport)) + ucr1 |= IMX1_UCR1_UARTCLKEN; + + ucr1 |= UCR1_UARTEN; + ucr1 &= ~(UCR1_TXMPTYEN | UCR1_RTSDEN | UCR1_RRDYEN); + + ucr2 |= UCR2_RXEN; + ucr2 &= ~UCR2_ATEN; + + imx_uart_writel(sport, ucr1, UCR1); + imx_uart_writel(sport, ucr2, UCR2); + + /* now enable irqs */ + imx_uart_writel(sport, ucr1 | UCR1_RRDYEN, UCR1); + imx_uart_writel(sport, ucr2 | UCR2_ATEN, UCR2); spin_unlock_irqrestore(&sport->port.lock, flags); return 0; } -static int imx_poll_get_char(struct uart_port *port) +static int imx_uart_poll_get_char(struct uart_port *port) { - if (!(readl_relaxed(port->membase + USR2) & USR2_RDR)) + struct imx_port *sport = (struct imx_port *)port; + if (!(imx_uart_readl(sport, USR2) & USR2_RDR)) return NO_POLL_CHAR; - return readl_relaxed(port->membase + URXD0) & URXD_RX_DATA; + return imx_uart_readl(sport, URXD0) & URXD_RX_DATA; } -static void imx_poll_put_char(struct uart_port *port, unsigned char c) +static void imx_uart_poll_put_char(struct uart_port *port, unsigned char c) { + struct imx_port *sport = (struct imx_port *)port; unsigned int status; /* drain */ do { - status = readl_relaxed(port->membase + USR1); + status = imx_uart_readl(sport, USR1); } while (~status & USR1_TRDY); /* write */ - writel_relaxed(c, port->membase + URTX0); + imx_uart_writel(sport, c, URTX0); /* flush */ do { - status = readl_relaxed(port->membase + USR2); + status = imx_uart_readl(sport, USR2); } while (~status & USR2_TXDC); } #endif -static int imx_rs485_config(struct uart_port *port, - struct serial_rs485 *rs485conf) +/* called with port.lock taken and irqs off or from .probe without locking */ +static int imx_uart_rs485_config(struct uart_port *port, + struct serial_rs485 *rs485conf) { struct imx_port *sport = (struct imx_port *)port; - unsigned long temp; + u32 ucr2; /* unimplemented */ rs485conf->delay_rts_before_send = 0; @@ -1678,70 +1834,67 @@ static int imx_rs485_config(struct uart_port *port, if (rs485conf->flags & SER_RS485_ENABLED) { /* disable transmitter */ - temp = readl(sport->port.membase + UCR2); + ucr2 = imx_uart_readl(sport, UCR2); if (rs485conf->flags & SER_RS485_RTS_AFTER_SEND) - imx_port_rts_active(sport, &temp); + imx_uart_rts_active(sport, &ucr2); else - imx_port_rts_inactive(sport, &temp); - writel(temp, sport->port.membase + UCR2); + imx_uart_rts_inactive(sport, &ucr2); + imx_uart_writel(sport, ucr2, UCR2); } /* Make sure Rx is enabled in case Tx is active with Rx disabled */ if (!(rs485conf->flags & SER_RS485_ENABLED) || - rs485conf->flags & SER_RS485_RX_DURING_TX) { - temp = readl(sport->port.membase + UCR2); - temp |= UCR2_RXEN; - writel(temp, sport->port.membase + UCR2); - } + rs485conf->flags & SER_RS485_RX_DURING_TX) + imx_uart_start_rx(port); port->rs485 = *rs485conf; return 0; } -static const struct uart_ops imx_pops = { - .tx_empty = imx_tx_empty, - .set_mctrl = imx_set_mctrl, - .get_mctrl = imx_get_mctrl, - .stop_tx = imx_stop_tx, - .start_tx = imx_start_tx, - .stop_rx = imx_stop_rx, - .enable_ms = imx_enable_ms, - .break_ctl = imx_break_ctl, - .startup = imx_startup, - .shutdown = imx_shutdown, - .flush_buffer = imx_flush_buffer, - .set_termios = imx_set_termios, - .type = imx_type, - .config_port = imx_config_port, - .verify_port = imx_verify_port, +static const struct uart_ops imx_uart_pops = { + .tx_empty = imx_uart_tx_empty, + .set_mctrl = imx_uart_set_mctrl, + .get_mctrl = imx_uart_get_mctrl, + .stop_tx = imx_uart_stop_tx, + .start_tx = imx_uart_start_tx, + .stop_rx = imx_uart_stop_rx, + .enable_ms = imx_uart_enable_ms, + .break_ctl = imx_uart_break_ctl, + .startup = imx_uart_startup, + .shutdown = imx_uart_shutdown, + .flush_buffer = imx_uart_flush_buffer, + .set_termios = imx_uart_set_termios, + .type = imx_uart_type, + .config_port = imx_uart_config_port, + .verify_port = imx_uart_verify_port, #if defined(CONFIG_CONSOLE_POLL) - .poll_init = imx_poll_init, - .poll_get_char = imx_poll_get_char, - .poll_put_char = imx_poll_put_char, + .poll_init = imx_uart_poll_init, + .poll_get_char = imx_uart_poll_get_char, + .poll_put_char = imx_uart_poll_put_char, #endif }; -static struct imx_port *imx_ports[UART_NR]; +static struct imx_port *imx_uart_ports[UART_NR]; #ifdef CONFIG_SERIAL_IMX_CONSOLE -static void imx_console_putchar(struct uart_port *port, int ch) +static void imx_uart_console_putchar(struct uart_port *port, int ch) { struct imx_port *sport = (struct imx_port *)port; - while (readl(sport->port.membase + uts_reg(sport)) & UTS_TXFULL) + while (imx_uart_readl(sport, imx_uart_uts_reg(sport)) & UTS_TXFULL) barrier(); - writel(ch, sport->port.membase + URTX0); + imx_uart_writel(sport, ch, URTX0); } /* * Interrupts are disabled on entering */ static void -imx_console_write(struct console *co, const char *s, unsigned int count) +imx_uart_console_write(struct console *co, const char *s, unsigned int count) { - struct imx_port *sport = imx_ports[co->index]; + struct imx_port *sport = imx_uart_ports[co->index]; struct imx_port_ucrs old_ucr; unsigned int ucr1; unsigned long flags = 0; @@ -1767,27 +1920,27 @@ imx_console_write(struct console *co, const char *s, unsigned int count) /* * First, save UCR1/2/3 and then disable interrupts */ - imx_port_ucrs_save(&sport->port, &old_ucr); + imx_uart_ucrs_save(sport, &old_ucr); ucr1 = old_ucr.ucr1; - if (is_imx1_uart(sport)) + if (imx_uart_is_imx1(sport)) ucr1 |= IMX1_UCR1_UARTCLKEN; ucr1 |= UCR1_UARTEN; ucr1 &= ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN); - writel(ucr1, sport->port.membase + UCR1); + imx_uart_writel(sport, ucr1, UCR1); - writel(old_ucr.ucr2 | UCR2_TXEN, sport->port.membase + UCR2); + imx_uart_writel(sport, old_ucr.ucr2 | UCR2_TXEN, UCR2); - uart_console_write(&sport->port, s, count, imx_console_putchar); + uart_console_write(&sport->port, s, count, imx_uart_console_putchar); /* * Finally, wait for transmitter to become empty * and restore UCR1/2/3 */ - while (!(readl(sport->port.membase + USR2) & USR2_TXDC)); + while (!(imx_uart_readl(sport, USR2) & USR2_TXDC)); - imx_port_ucrs_restore(&sport->port, &old_ucr); + imx_uart_ucrs_restore(sport, &old_ucr); if (locked) spin_unlock_irqrestore(&sport->port.lock, flags); @@ -1801,17 +1954,17 @@ imx_console_write(struct console *co, const char *s, unsigned int count) * try to determine the current setup. */ static void __init -imx_console_get_options(struct imx_port *sport, int *baud, - int *parity, int *bits) +imx_uart_console_get_options(struct imx_port *sport, int *baud, + int *parity, int *bits) { - if (readl(sport->port.membase + UCR1) & UCR1_UARTEN) { + if (imx_uart_readl(sport, UCR1) & UCR1_UARTEN) { /* ok, the port was enabled */ unsigned int ucr2, ubir, ubmr, uartclk; unsigned int baud_raw; unsigned int ucfr_rfdiv; - ucr2 = readl(sport->port.membase + UCR2); + ucr2 = imx_uart_readl(sport, UCR2); *parity = 'n'; if (ucr2 & UCR2_PREN) { @@ -1826,10 +1979,10 @@ imx_console_get_options(struct imx_port *sport, int *baud, else *bits = 7; - ubir = readl(sport->port.membase + UBIR) & 0xffff; - ubmr = readl(sport->port.membase + UBMR) & 0xffff; + ubir = imx_uart_readl(sport, UBIR) & 0xffff; + ubmr = imx_uart_readl(sport, UBMR) & 0xffff; - ucfr_rfdiv = (readl(sport->port.membase + UFCR) & UFCR_RFDIV) >> 7; + ucfr_rfdiv = (imx_uart_readl(sport, UFCR) & UFCR_RFDIV) >> 7; if (ucfr_rfdiv == 6) ucfr_rfdiv = 7; else @@ -1860,7 +2013,7 @@ imx_console_get_options(struct imx_port *sport, int *baud, } static int __init -imx_console_setup(struct console *co, char *options) +imx_uart_console_setup(struct console *co, char *options) { struct imx_port *sport; int baud = 9600; @@ -1874,9 +2027,9 @@ imx_console_setup(struct console *co, char *options) * if so, search for the first available port that does have * console support. */ - if (co->index == -1 || co->index >= ARRAY_SIZE(imx_ports)) + if (co->index == -1 || co->index >= ARRAY_SIZE(imx_uart_ports)) co->index = 0; - sport = imx_ports[co->index]; + sport = imx_uart_ports[co->index]; if (sport == NULL) return -ENODEV; @@ -1888,9 +2041,9 @@ imx_console_setup(struct console *co, char *options) if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); else - imx_console_get_options(sport, &baud, &parity, &bits); + imx_uart_console_get_options(sport, &baud, &parity, &bits); - imx_setup_ufcr(sport, TXTL_DEFAULT, RXTL_DEFAULT); + imx_uart_setup_ufcr(sport, TXTL_DEFAULT, RXTL_DEFAULT); retval = uart_set_options(&sport->port, co, baud, parity, bits, flow); @@ -1908,34 +2061,36 @@ error_console: return retval; } -static struct uart_driver imx_reg; -static struct console imx_console = { +static struct uart_driver imx_uart_uart_driver; +static struct console imx_uart_console = { .name = DEV_NAME, - .write = imx_console_write, + .write = imx_uart_console_write, .device = uart_console_device, - .setup = imx_console_setup, + .setup = imx_uart_console_setup, .flags = CON_PRINTBUFFER, .index = -1, - .data = &imx_reg, + .data = &imx_uart_uart_driver, }; -#define IMX_CONSOLE &imx_console +#define IMX_CONSOLE &imx_uart_console #ifdef CONFIG_OF -static void imx_console_early_putchar(struct uart_port *port, int ch) +static void imx_uart_console_early_putchar(struct uart_port *port, int ch) { - while (readl_relaxed(port->membase + IMX21_UTS) & UTS_TXFULL) + struct imx_port *sport = (struct imx_port *)port; + + while (imx_uart_readl(sport, IMX21_UTS) & UTS_TXFULL) cpu_relax(); - writel_relaxed(ch, port->membase + URTX0); + imx_uart_writel(sport, ch, URTX0); } -static void imx_console_early_write(struct console *con, const char *s, - unsigned count) +static void imx_uart_console_early_write(struct console *con, const char *s, + unsigned count) { struct earlycon_device *dev = con->data; - uart_console_write(&dev->port, s, count, imx_console_early_putchar); + uart_console_write(&dev->port, s, count, imx_uart_console_early_putchar); } static int __init @@ -1944,7 +2099,7 @@ imx_console_early_setup(struct earlycon_device *dev, const char *opt) if (!dev->port.membase) return -ENODEV; - dev->con->write = imx_console_early_write; + dev->con->write = imx_uart_console_early_write; return 0; } @@ -1956,13 +2111,13 @@ OF_EARLYCON_DECLARE(ec_imx21, "fsl,imx21-uart", imx_console_early_setup); #define IMX_CONSOLE NULL #endif -static struct uart_driver imx_reg = { +static struct uart_driver imx_uart_uart_driver = { .owner = THIS_MODULE, .driver_name = DRIVER_NAME, .dev_name = DEV_NAME, .major = SERIAL_IMX_MAJOR, .minor = MINOR_START, - .nr = ARRAY_SIZE(imx_ports), + .nr = ARRAY_SIZE(imx_uart_ports), .cons = IMX_CONSOLE, }; @@ -1971,8 +2126,8 @@ static struct uart_driver imx_reg = { * This function returns 1 iff pdev isn't a device instatiated by dt, 0 iff it * could successfully get all information from dt or a negative errno. */ -static int serial_imx_probe_dt(struct imx_port *sport, - struct platform_device *pdev) +static int imx_uart_probe_dt(struct imx_port *sport, + struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; int ret; @@ -2002,15 +2157,15 @@ static int serial_imx_probe_dt(struct imx_port *sport, return 0; } #else -static inline int serial_imx_probe_dt(struct imx_port *sport, - struct platform_device *pdev) +static inline int imx_uart_probe_dt(struct imx_port *sport, + struct platform_device *pdev) { return 1; } #endif -static void serial_imx_probe_pdata(struct imx_port *sport, - struct platform_device *pdev) +static void imx_uart_probe_pdata(struct imx_port *sport, + struct platform_device *pdev) { struct imxuart_platform_data *pdata = dev_get_platdata(&pdev->dev); @@ -2024,11 +2179,12 @@ static void serial_imx_probe_pdata(struct imx_port *sport, sport->have_rtscts = 1; } -static int serial_imx_probe(struct platform_device *pdev) +static int imx_uart_probe(struct platform_device *pdev) { struct imx_port *sport; void __iomem *base; - int ret = 0, reg; + int ret = 0; + u32 ucr1; struct resource *res; int txirq, rxirq, rtsirq; @@ -2036,12 +2192,18 @@ static int serial_imx_probe(struct platform_device *pdev) if (!sport) return -ENOMEM; - ret = serial_imx_probe_dt(sport, pdev); + ret = imx_uart_probe_dt(sport, pdev); if (ret > 0) - serial_imx_probe_pdata(sport, pdev); + imx_uart_probe_pdata(sport, pdev); else if (ret < 0) return ret; + if (sport->port.line >= ARRAY_SIZE(imx_uart_ports)) { + dev_err(&pdev->dev, "serial%d out of range\n", + sport->port.line); + return -EINVAL; + } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(base)) @@ -2058,10 +2220,10 @@ static int serial_imx_probe(struct platform_device *pdev) sport->port.iotype = UPIO_MEM; sport->port.irq = rxirq; sport->port.fifosize = 32; - sport->port.ops = &imx_pops; - sport->port.rs485_config = imx_rs485_config; + sport->port.ops = &imx_uart_pops; + sport->port.rs485_config = imx_uart_rs485_config; sport->port.flags = UPF_BOOT_AUTOCONF; - timer_setup(&sport->timer, imx_timeout, 0); + timer_setup(&sport->timer, imx_uart_timeout, 0); sport->gpios = mctrl_gpio_init(&sport->port, 0); if (IS_ERR(sport->gpios)) @@ -2090,49 +2252,56 @@ static int serial_imx_probe(struct platform_device *pdev) return ret; } + /* initialize shadow register values */ + sport->ucr1 = readl(sport->port.membase + UCR1); + sport->ucr2 = readl(sport->port.membase + UCR2); + sport->ucr3 = readl(sport->port.membase + UCR3); + sport->ucr4 = readl(sport->port.membase + UCR4); + sport->ufcr = readl(sport->port.membase + UFCR); + uart_get_rs485_mode(&pdev->dev, &sport->port.rs485); if (sport->port.rs485.flags & SER_RS485_ENABLED && (!sport->have_rtscts && !sport->have_rtsgpio)) dev_err(&pdev->dev, "no RTS control, disabling rs485\n"); - imx_rs485_config(&sport->port, &sport->port.rs485); + imx_uart_rs485_config(&sport->port, &sport->port.rs485); /* Disable interrupts before requesting them */ - reg = readl_relaxed(sport->port.membase + UCR1); - reg &= ~(UCR1_ADEN | UCR1_TRDYEN | UCR1_IDEN | UCR1_RRDYEN | + ucr1 = imx_uart_readl(sport, UCR1); + ucr1 &= ~(UCR1_ADEN | UCR1_TRDYEN | UCR1_IDEN | UCR1_RRDYEN | UCR1_TXMPTYEN | UCR1_RTSDEN); - writel_relaxed(reg, sport->port.membase + UCR1); + imx_uart_writel(sport, ucr1, UCR1); - if (!is_imx1_uart(sport) && sport->dte_mode) { + if (!imx_uart_is_imx1(sport) && sport->dte_mode) { /* * The DCEDTE bit changes the direction of DSR, DCD, DTR and RI * and influences if UCR3_RI and UCR3_DCD changes the level of RI * and DCD (when they are outputs) or enables the respective * irqs. So set this bit early, i.e. before requesting irqs. */ - reg = readl(sport->port.membase + UFCR); - if (!(reg & UFCR_DCEDTE)) - writel(reg | UFCR_DCEDTE, sport->port.membase + UFCR); + u32 ufcr = imx_uart_readl(sport, UFCR); + if (!(ufcr & UFCR_DCEDTE)) + imx_uart_writel(sport, ufcr | UFCR_DCEDTE, UFCR); /* * Disable UCR3_RI and UCR3_DCD irqs. They are also not * enabled later because they cannot be cleared * (confirmed on i.MX25) which makes them unusable. */ - writel(IMX21_UCR3_RXDMUXSEL | UCR3_ADNIMP | UCR3_DSR, - sport->port.membase + UCR3); + imx_uart_writel(sport, + IMX21_UCR3_RXDMUXSEL | UCR3_ADNIMP | UCR3_DSR, + UCR3); } else { - unsigned long ucr3 = UCR3_DSR; - - reg = readl(sport->port.membase + UFCR); - if (reg & UFCR_DCEDTE) - writel(reg & ~UFCR_DCEDTE, sport->port.membase + UFCR); + u32 ucr3 = UCR3_DSR; + u32 ufcr = imx_uart_readl(sport, UFCR); + if (ufcr & UFCR_DCEDTE) + imx_uart_writel(sport, ufcr & ~UFCR_DCEDTE, UFCR); - if (!is_imx1_uart(sport)) + if (!imx_uart_is_imx1(sport)) ucr3 |= IMX21_UCR3_RXDMUXSEL | UCR3_ADNIMP; - writel(ucr3, sport->port.membase + UCR3); + imx_uart_writel(sport, ucr3, UCR3); } clk_disable_unprepare(sport->clk_ipg); @@ -2142,7 +2311,7 @@ static int serial_imx_probe(struct platform_device *pdev) * chips only have one interrupt. */ if (txirq > 0) { - ret = devm_request_irq(&pdev->dev, rxirq, imx_rxint, 0, + ret = devm_request_irq(&pdev->dev, rxirq, imx_uart_rxint, 0, dev_name(&pdev->dev), sport); if (ret) { dev_err(&pdev->dev, "failed to request rx irq: %d\n", @@ -2150,7 +2319,7 @@ static int serial_imx_probe(struct platform_device *pdev) return ret; } - ret = devm_request_irq(&pdev->dev, txirq, imx_txint, 0, + ret = devm_request_irq(&pdev->dev, txirq, imx_uart_txint, 0, dev_name(&pdev->dev), sport); if (ret) { dev_err(&pdev->dev, "failed to request tx irq: %d\n", @@ -2158,7 +2327,7 @@ static int serial_imx_probe(struct platform_device *pdev) return ret; } } else { - ret = devm_request_irq(&pdev->dev, rxirq, imx_int, 0, + ret = devm_request_irq(&pdev->dev, rxirq, imx_uart_int, 0, dev_name(&pdev->dev), sport); if (ret) { dev_err(&pdev->dev, "failed to request irq: %d\n", ret); @@ -2166,90 +2335,90 @@ static int serial_imx_probe(struct platform_device *pdev) } } - imx_ports[sport->port.line] = sport; + imx_uart_ports[sport->port.line] = sport; platform_set_drvdata(pdev, sport); - return uart_add_one_port(&imx_reg, &sport->port); + return uart_add_one_port(&imx_uart_uart_driver, &sport->port); } -static int serial_imx_remove(struct platform_device *pdev) +static int imx_uart_remove(struct platform_device *pdev) { struct imx_port *sport = platform_get_drvdata(pdev); - return uart_remove_one_port(&imx_reg, &sport->port); + return uart_remove_one_port(&imx_uart_uart_driver, &sport->port); } -static void serial_imx_restore_context(struct imx_port *sport) +static void imx_uart_restore_context(struct imx_port *sport) { if (!sport->context_saved) return; - writel(sport->saved_reg[4], sport->port.membase + UFCR); - writel(sport->saved_reg[5], sport->port.membase + UESC); - writel(sport->saved_reg[6], sport->port.membase + UTIM); - writel(sport->saved_reg[7], sport->port.membase + UBIR); - writel(sport->saved_reg[8], sport->port.membase + UBMR); - writel(sport->saved_reg[9], sport->port.membase + IMX21_UTS); - writel(sport->saved_reg[0], sport->port.membase + UCR1); - writel(sport->saved_reg[1] | UCR2_SRST, sport->port.membase + UCR2); - writel(sport->saved_reg[2], sport->port.membase + UCR3); - writel(sport->saved_reg[3], sport->port.membase + UCR4); + imx_uart_writel(sport, sport->saved_reg[4], UFCR); + imx_uart_writel(sport, sport->saved_reg[5], UESC); + imx_uart_writel(sport, sport->saved_reg[6], UTIM); + imx_uart_writel(sport, sport->saved_reg[7], UBIR); + imx_uart_writel(sport, sport->saved_reg[8], UBMR); + imx_uart_writel(sport, sport->saved_reg[9], IMX21_UTS); + imx_uart_writel(sport, sport->saved_reg[0], UCR1); + imx_uart_writel(sport, sport->saved_reg[1] | UCR2_SRST, UCR2); + imx_uart_writel(sport, sport->saved_reg[2], UCR3); + imx_uart_writel(sport, sport->saved_reg[3], UCR4); sport->context_saved = false; } -static void serial_imx_save_context(struct imx_port *sport) +static void imx_uart_save_context(struct imx_port *sport) { /* Save necessary regs */ - sport->saved_reg[0] = readl(sport->port.membase + UCR1); - sport->saved_reg[1] = readl(sport->port.membase + UCR2); - sport->saved_reg[2] = readl(sport->port.membase + UCR3); - sport->saved_reg[3] = readl(sport->port.membase + UCR4); - sport->saved_reg[4] = readl(sport->port.membase + UFCR); - sport->saved_reg[5] = readl(sport->port.membase + UESC); - sport->saved_reg[6] = readl(sport->port.membase + UTIM); - sport->saved_reg[7] = readl(sport->port.membase + UBIR); - sport->saved_reg[8] = readl(sport->port.membase + UBMR); - sport->saved_reg[9] = readl(sport->port.membase + IMX21_UTS); + sport->saved_reg[0] = imx_uart_readl(sport, UCR1); + sport->saved_reg[1] = imx_uart_readl(sport, UCR2); + sport->saved_reg[2] = imx_uart_readl(sport, UCR3); + sport->saved_reg[3] = imx_uart_readl(sport, UCR4); + sport->saved_reg[4] = imx_uart_readl(sport, UFCR); + sport->saved_reg[5] = imx_uart_readl(sport, UESC); + sport->saved_reg[6] = imx_uart_readl(sport, UTIM); + sport->saved_reg[7] = imx_uart_readl(sport, UBIR); + sport->saved_reg[8] = imx_uart_readl(sport, UBMR); + sport->saved_reg[9] = imx_uart_readl(sport, IMX21_UTS); sport->context_saved = true; } -static void serial_imx_enable_wakeup(struct imx_port *sport, bool on) +static void imx_uart_enable_wakeup(struct imx_port *sport, bool on) { - unsigned int val; + u32 ucr3; - val = readl(sport->port.membase + UCR3); + ucr3 = imx_uart_readl(sport, UCR3); if (on) { - writel(USR1_AWAKE, sport->port.membase + USR1); - val |= UCR3_AWAKEN; + imx_uart_writel(sport, USR1_AWAKE, USR1); + ucr3 |= UCR3_AWAKEN; + } else { + ucr3 &= ~UCR3_AWAKEN; } - else - val &= ~UCR3_AWAKEN; - writel(val, sport->port.membase + UCR3); + imx_uart_writel(sport, ucr3, UCR3); if (sport->have_rtscts) { - val = readl(sport->port.membase + UCR1); + u32 ucr1 = imx_uart_readl(sport, UCR1); if (on) - val |= UCR1_RTSDEN; + ucr1 |= UCR1_RTSDEN; else - val &= ~UCR1_RTSDEN; - writel(val, sport->port.membase + UCR1); + ucr1 &= ~UCR1_RTSDEN; + imx_uart_writel(sport, ucr1, UCR1); } } -static int imx_serial_port_suspend_noirq(struct device *dev) +static int imx_uart_suspend_noirq(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct imx_port *sport = platform_get_drvdata(pdev); - serial_imx_save_context(sport); + imx_uart_save_context(sport); clk_disable(sport->clk_ipg); return 0; } -static int imx_serial_port_resume_noirq(struct device *dev) +static int imx_uart_resume_noirq(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct imx_port *sport = platform_get_drvdata(pdev); @@ -2259,18 +2428,18 @@ static int imx_serial_port_resume_noirq(struct device *dev) if (ret) return ret; - serial_imx_restore_context(sport); + imx_uart_restore_context(sport); return 0; } -static int imx_serial_port_suspend(struct device *dev) +static int imx_uart_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct imx_port *sport = platform_get_drvdata(pdev); int ret; - uart_suspend_port(&imx_reg, &sport->port); + uart_suspend_port(&imx_uart_uart_driver, &sport->port); disable_irq(sport->port.irq); ret = clk_prepare_enable(sport->clk_ipg); @@ -2278,20 +2447,20 @@ static int imx_serial_port_suspend(struct device *dev) return ret; /* enable wakeup from i.MX UART */ - serial_imx_enable_wakeup(sport, true); + imx_uart_enable_wakeup(sport, true); return 0; } -static int imx_serial_port_resume(struct device *dev) +static int imx_uart_resume(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct imx_port *sport = platform_get_drvdata(pdev); /* disable wakeup from i.MX UART */ - serial_imx_enable_wakeup(sport, false); + imx_uart_enable_wakeup(sport, false); - uart_resume_port(&imx_reg, &sport->port); + uart_resume_port(&imx_uart_uart_driver, &sport->port); enable_irq(sport->port.irq); clk_disable_unprepare(sport->clk_ipg); @@ -2299,74 +2468,74 @@ static int imx_serial_port_resume(struct device *dev) return 0; } -static int imx_serial_port_freeze(struct device *dev) +static int imx_uart_freeze(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct imx_port *sport = platform_get_drvdata(pdev); - uart_suspend_port(&imx_reg, &sport->port); + uart_suspend_port(&imx_uart_uart_driver, &sport->port); return clk_prepare_enable(sport->clk_ipg); } -static int imx_serial_port_thaw(struct device *dev) +static int imx_uart_thaw(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct imx_port *sport = platform_get_drvdata(pdev); - uart_resume_port(&imx_reg, &sport->port); + uart_resume_port(&imx_uart_uart_driver, &sport->port); clk_disable_unprepare(sport->clk_ipg); return 0; } -static const struct dev_pm_ops imx_serial_port_pm_ops = { - .suspend_noirq = imx_serial_port_suspend_noirq, - .resume_noirq = imx_serial_port_resume_noirq, - .freeze_noirq = imx_serial_port_suspend_noirq, - .restore_noirq = imx_serial_port_resume_noirq, - .suspend = imx_serial_port_suspend, - .resume = imx_serial_port_resume, - .freeze = imx_serial_port_freeze, - .thaw = imx_serial_port_thaw, - .restore = imx_serial_port_thaw, +static const struct dev_pm_ops imx_uart_pm_ops = { + .suspend_noirq = imx_uart_suspend_noirq, + .resume_noirq = imx_uart_resume_noirq, + .freeze_noirq = imx_uart_suspend_noirq, + .restore_noirq = imx_uart_resume_noirq, + .suspend = imx_uart_suspend, + .resume = imx_uart_resume, + .freeze = imx_uart_freeze, + .thaw = imx_uart_thaw, + .restore = imx_uart_thaw, }; -static struct platform_driver serial_imx_driver = { - .probe = serial_imx_probe, - .remove = serial_imx_remove, +static struct platform_driver imx_uart_platform_driver = { + .probe = imx_uart_probe, + .remove = imx_uart_remove, - .id_table = imx_uart_devtype, - .driver = { - .name = "imx-uart", + .id_table = imx_uart_devtype, + .driver = { + .name = "imx-uart", .of_match_table = imx_uart_dt_ids, - .pm = &imx_serial_port_pm_ops, + .pm = &imx_uart_pm_ops, }, }; -static int __init imx_serial_init(void) +static int __init imx_uart_init(void) { - int ret = uart_register_driver(&imx_reg); + int ret = uart_register_driver(&imx_uart_uart_driver); if (ret) return ret; - ret = platform_driver_register(&serial_imx_driver); + ret = platform_driver_register(&imx_uart_platform_driver); if (ret != 0) - uart_unregister_driver(&imx_reg); + uart_unregister_driver(&imx_uart_uart_driver); return ret; } -static void __exit imx_serial_exit(void) +static void __exit imx_uart_exit(void) { - platform_driver_unregister(&serial_imx_driver); - uart_unregister_driver(&imx_reg); + platform_driver_unregister(&imx_uart_platform_driver); + uart_unregister_driver(&imx_uart_uart_driver); } -module_init(imx_serial_init); -module_exit(imx_serial_exit); +module_init(imx_uart_init); +module_exit(imx_uart_exit); MODULE_AUTHOR("Sascha Hauer"); MODULE_DESCRIPTION("IMX generic serial port driver"); diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index 39f635812077..efe55a1a0615 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -1318,7 +1318,7 @@ static int max310x_probe(struct device *dev, struct max310x_devtype *devtype, /* Setup GPIO cotroller */ s->gpio.owner = THIS_MODULE; s->gpio.parent = dev; - s->gpio.label = dev_name(dev); + s->gpio.label = devtype->name; s->gpio.direction_input = max310x_gpio_direction_input; s->gpio.get = max310x_gpio_get; s->gpio.direction_output= max310x_gpio_direction_output; diff --git a/drivers/tty/serial/mvebu-uart.c b/drivers/tty/serial/mvebu-uart.c index a100e98259d7..750e5645dc85 100644 --- a/drivers/tty/serial/mvebu-uart.c +++ b/drivers/tty/serial/mvebu-uart.c @@ -65,7 +65,7 @@ #define STAT_FRM_ERR BIT(2) #define STAT_PAR_ERR BIT(1) #define STAT_OVR_ERR BIT(0) -#define STAT_BRK_ERR (STAT_BRK_DET | STAT_FRM_ERR | STAT_FRM_ERR\ +#define STAT_BRK_ERR (STAT_BRK_DET | STAT_FRM_ERR \ | STAT_PAR_ERR | STAT_OVR_ERR) #define UART_BRDV 0x10 @@ -618,7 +618,7 @@ static void wait_for_xmitr(struct uart_port *port) u32 val; readl_poll_timeout_atomic(port->membase + UART_STAT, val, - (val & STAT_TX_EMP), 1, 10000); + (val & STAT_TX_RDY(port)), 1, 10000); } static void mvebu_uart_console_putchar(struct uart_port *port, int ch) diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c index 079dc47aa142..76aa289652f7 100644 --- a/drivers/tty/serial/mxs-auart.c +++ b/drivers/tty/serial/mxs-auart.c @@ -1663,6 +1663,10 @@ static int mxs_auart_probe(struct platform_device *pdev) s->port.line = pdev->id < 0 ? 0 : pdev->id; else if (ret < 0) return ret; + if (s->port.line >= ARRAY_SIZE(auart_port)) { + dev_err(&pdev->dev, "serial%d out of range\n", s->port.line); + return -EINVAL; + } if (of_id) { pdev->id_entry = of_id->data; @@ -1674,8 +1678,10 @@ static int mxs_auart_probe(struct platform_device *pdev) return ret; r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!r) - return -ENXIO; + if (!r) { + ret = -ENXIO; + goto out_disable_clks; + } s->port.mapbase = r->start; s->port.membase = ioremap(r->start, resource_size(r)); @@ -1690,21 +1696,23 @@ static int mxs_auart_probe(struct platform_device *pdev) s->mctrl_prev = 0; irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; + if (irq < 0) { + ret = irq; + goto out_disable_clks; + } s->port.irq = irq; ret = devm_request_irq(&pdev->dev, irq, mxs_auart_irq_handle, 0, dev_name(&pdev->dev), s); if (ret) - return ret; + goto out_disable_clks; platform_set_drvdata(pdev, s); ret = mxs_auart_init_gpios(s, &pdev->dev); if (ret) { dev_err(&pdev->dev, "Failed to initialize GPIOs.\n"); - return ret; + goto out_disable_clks; } /* @@ -1712,7 +1720,7 @@ static int mxs_auart_probe(struct platform_device *pdev) */ ret = mxs_auart_request_gpio_irq(s); if (ret) - return ret; + goto out_disable_clks; auart_port[s->port.line] = s; @@ -1720,7 +1728,7 @@ static int mxs_auart_probe(struct platform_device *pdev) ret = uart_add_one_port(&auart_driver, &s->port); if (ret) - goto out_disable_clks_free_qpio_irq; + goto out_free_qpio_irq; /* ASM9260 don't have version reg */ if (is_asm9260_auart(s)) { @@ -1734,13 +1742,15 @@ static int mxs_auart_probe(struct platform_device *pdev) return 0; -out_disable_clks_free_qpio_irq: - if (s->clk) - clk_disable_unprepare(s->clk_ahb); - if (s->clk_ahb) - clk_disable_unprepare(s->clk_ahb); +out_free_qpio_irq: mxs_auart_free_gpio_irq(s); auart_port[pdev->id] = NULL; + +out_disable_clks: + if (is_asm9260_auart(s)) { + clk_disable_unprepare(s->clk); + clk_disable_unprepare(s->clk_ahb); + } return ret; } @@ -1751,6 +1761,10 @@ static int mxs_auart_remove(struct platform_device *pdev) uart_remove_one_port(&auart_driver, &s->port); auart_port[pdev->id] = NULL; mxs_auart_free_gpio_irq(s); + if (is_asm9260_auart(s)) { + clk_disable_unprepare(s->clk); + clk_disable_unprepare(s->clk_ahb); + } return 0; } diff --git a/drivers/tty/serial/pxa.c b/drivers/tty/serial/pxa.c index baf552944d56..eda3c7710d6a 100644 --- a/drivers/tty/serial/pxa.c +++ b/drivers/tty/serial/pxa.c @@ -885,6 +885,10 @@ static int serial_pxa_probe(struct platform_device *dev) sport->port.line = dev->id; else if (ret < 0) goto err_clk; + if (sport->port.line >= ARRAY_SIZE(serial_pxa_ports)) { + dev_err(&dev->dev, "serial%d out of range\n", sport->port.line); + return -EINVAL; + } snprintf(sport->name, PXA_NAME_LEN - 1, "UART%d", sport->port.line + 1); sport->port.membase = ioremap(mmres->start, resource_size(mmres)); diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c new file mode 100644 index 000000000000..65ff669373d4 --- /dev/null +++ b/drivers/tty/serial/qcom_geni_serial.c @@ -0,0 +1,1158 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2017-2018, The Linux foundation. All rights reserved. + +#include <linux/clk.h> +#include <linux/console.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/qcom-geni-se.h> +#include <linux/serial.h> +#include <linux/serial_core.h> +#include <linux/slab.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> + +/* UART specific GENI registers */ +#define SE_UART_TX_TRANS_CFG 0x25c +#define SE_UART_TX_WORD_LEN 0x268 +#define SE_UART_TX_STOP_BIT_LEN 0x26c +#define SE_UART_TX_TRANS_LEN 0x270 +#define SE_UART_RX_TRANS_CFG 0x280 +#define SE_UART_RX_WORD_LEN 0x28c +#define SE_UART_RX_STALE_CNT 0x294 +#define SE_UART_TX_PARITY_CFG 0x2a4 +#define SE_UART_RX_PARITY_CFG 0x2a8 + +/* SE_UART_TRANS_CFG */ +#define UART_TX_PAR_EN BIT(0) +#define UART_CTS_MASK BIT(1) + +/* SE_UART_TX_WORD_LEN */ +#define TX_WORD_LEN_MSK GENMASK(9, 0) + +/* SE_UART_TX_STOP_BIT_LEN */ +#define TX_STOP_BIT_LEN_MSK GENMASK(23, 0) +#define TX_STOP_BIT_LEN_1 0 +#define TX_STOP_BIT_LEN_1_5 1 +#define TX_STOP_BIT_LEN_2 2 + +/* SE_UART_TX_TRANS_LEN */ +#define TX_TRANS_LEN_MSK GENMASK(23, 0) + +/* SE_UART_RX_TRANS_CFG */ +#define UART_RX_INS_STATUS_BIT BIT(2) +#define UART_RX_PAR_EN BIT(3) + +/* SE_UART_RX_WORD_LEN */ +#define RX_WORD_LEN_MASK GENMASK(9, 0) + +/* SE_UART_RX_STALE_CNT */ +#define RX_STALE_CNT GENMASK(23, 0) + +/* SE_UART_TX_PARITY_CFG/RX_PARITY_CFG */ +#define PAR_CALC_EN BIT(0) +#define PAR_MODE_MSK GENMASK(2, 1) +#define PAR_MODE_SHFT 1 +#define PAR_EVEN 0x00 +#define PAR_ODD 0x01 +#define PAR_SPACE 0x10 +#define PAR_MARK 0x11 + +/* UART M_CMD OP codes */ +#define UART_START_TX 0x1 +#define UART_START_BREAK 0x4 +#define UART_STOP_BREAK 0x5 +/* UART S_CMD OP codes */ +#define UART_START_READ 0x1 +#define UART_PARAM 0x1 + +#define UART_OVERSAMPLING 32 +#define STALE_TIMEOUT 16 +#define DEFAULT_BITS_PER_CHAR 10 +#define GENI_UART_CONS_PORTS 1 +#define DEF_FIFO_DEPTH_WORDS 16 +#define DEF_TX_WM 2 +#define DEF_FIFO_WIDTH_BITS 32 +#define UART_CONSOLE_RX_WM 2 + +#ifdef CONFIG_CONSOLE_POLL +#define RX_BYTES_PW 1 +#else +#define RX_BYTES_PW 4 +#endif + +struct qcom_geni_serial_port { + struct uart_port uport; + struct geni_se se; + char name[20]; + u32 tx_fifo_depth; + u32 tx_fifo_width; + u32 rx_fifo_depth; + u32 tx_wm; + u32 rx_wm; + u32 rx_rfr; + enum geni_se_xfer_mode xfer_mode; + bool setup; + int (*handle_rx)(struct uart_port *uport, u32 bytes, bool drop); + unsigned int xmit_size; + unsigned int baud; + unsigned int tx_bytes_pw; + unsigned int rx_bytes_pw; + bool brk; +}; + +static const struct uart_ops qcom_geni_serial_pops; +static struct uart_driver qcom_geni_console_driver; +static int handle_rx_console(struct uart_port *uport, u32 bytes, bool drop); +static unsigned int qcom_geni_serial_tx_empty(struct uart_port *port); +static void qcom_geni_serial_stop_rx(struct uart_port *uport); + +static const unsigned long root_freq[] = {7372800, 14745600, 19200000, 29491200, + 32000000, 48000000, 64000000, 80000000, + 96000000, 100000000}; + +#define to_dev_port(ptr, member) \ + container_of(ptr, struct qcom_geni_serial_port, member) + +static struct qcom_geni_serial_port qcom_geni_console_port; + +static int qcom_geni_serial_request_port(struct uart_port *uport) +{ + struct platform_device *pdev = to_platform_device(uport->dev); + struct qcom_geni_serial_port *port = to_dev_port(uport, uport); + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + uport->membase = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(uport->membase)) + return PTR_ERR(uport->membase); + port->se.base = uport->membase; + return 0; +} + +static void qcom_geni_serial_config_port(struct uart_port *uport, int cfg_flags) +{ + if (cfg_flags & UART_CONFIG_TYPE) { + uport->type = PORT_MSM; + qcom_geni_serial_request_port(uport); + } +} + +static unsigned int qcom_geni_cons_get_mctrl(struct uart_port *uport) +{ + return TIOCM_DSR | TIOCM_CAR | TIOCM_CTS; +} + +static void qcom_geni_cons_set_mctrl(struct uart_port *uport, + unsigned int mctrl) +{ +} + +static const char *qcom_geni_serial_get_type(struct uart_port *uport) +{ + return "MSM"; +} + +static struct qcom_geni_serial_port *get_port_from_line(int line) +{ + if (line < 0 || line >= GENI_UART_CONS_PORTS) + return ERR_PTR(-ENXIO); + return &qcom_geni_console_port; +} + +static bool qcom_geni_serial_poll_bit(struct uart_port *uport, + int offset, int field, bool set) +{ + u32 reg; + struct qcom_geni_serial_port *port; + unsigned int baud; + unsigned int fifo_bits; + unsigned long timeout_us = 20000; + + /* Ensure polling is not re-ordered before the prior writes/reads */ + mb(); + + if (uport->private_data) { + port = to_dev_port(uport, uport); + baud = port->baud; + if (!baud) + baud = 115200; + fifo_bits = port->tx_fifo_depth * port->tx_fifo_width; + /* + * Total polling iterations based on FIFO worth of bytes to be + * sent at current baud. Add a little fluff to the wait. + */ + timeout_us = ((fifo_bits * USEC_PER_SEC) / baud) + 500; + } + + return !readl_poll_timeout_atomic(uport->membase + offset, reg, + (bool)(reg & field) == set, 10, timeout_us); +} + +static void qcom_geni_serial_setup_tx(struct uart_port *uport, u32 xmit_size) +{ + u32 m_cmd; + + writel_relaxed(xmit_size, uport->membase + SE_UART_TX_TRANS_LEN); + m_cmd = UART_START_TX << M_OPCODE_SHFT; + writel(m_cmd, uport->membase + SE_GENI_M_CMD0); +} + +static void qcom_geni_serial_poll_tx_done(struct uart_port *uport) +{ + int done; + u32 irq_clear = M_CMD_DONE_EN; + + done = qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS, + M_CMD_DONE_EN, true); + if (!done) { + writel_relaxed(M_GENI_CMD_ABORT, uport->membase + + SE_GENI_M_CMD_CTRL_REG); + irq_clear |= M_CMD_ABORT_EN; + qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS, + M_CMD_ABORT_EN, true); + } + writel_relaxed(irq_clear, uport->membase + SE_GENI_M_IRQ_CLEAR); +} + +static void qcom_geni_serial_abort_rx(struct uart_port *uport) +{ + u32 irq_clear = S_CMD_DONE_EN | S_CMD_ABORT_EN; + + writel(S_GENI_CMD_ABORT, uport->membase + SE_GENI_S_CMD_CTRL_REG); + qcom_geni_serial_poll_bit(uport, SE_GENI_S_CMD_CTRL_REG, + S_GENI_CMD_ABORT, false); + writel_relaxed(irq_clear, uport->membase + SE_GENI_S_IRQ_CLEAR); + writel_relaxed(FORCE_DEFAULT, uport->membase + GENI_FORCE_DEFAULT_REG); +} + +#ifdef CONFIG_CONSOLE_POLL +static int qcom_geni_serial_get_char(struct uart_port *uport) +{ + u32 rx_fifo; + u32 status; + + status = readl_relaxed(uport->membase + SE_GENI_M_IRQ_STATUS); + writel_relaxed(status, uport->membase + SE_GENI_M_IRQ_CLEAR); + + status = readl_relaxed(uport->membase + SE_GENI_S_IRQ_STATUS); + writel_relaxed(status, uport->membase + SE_GENI_S_IRQ_CLEAR); + + /* + * Ensure the writes to clear interrupts is not re-ordered after + * reading the data. + */ + mb(); + + status = readl_relaxed(uport->membase + SE_GENI_RX_FIFO_STATUS); + if (!(status & RX_FIFO_WC_MSK)) + return NO_POLL_CHAR; + + rx_fifo = readl(uport->membase + SE_GENI_RX_FIFOn); + return rx_fifo & 0xff; +} + +static void qcom_geni_serial_poll_put_char(struct uart_port *uport, + unsigned char c) +{ + struct qcom_geni_serial_port *port = to_dev_port(uport, uport); + + writel_relaxed(port->tx_wm, uport->membase + SE_GENI_TX_WATERMARK_REG); + qcom_geni_serial_setup_tx(uport, 1); + WARN_ON(!qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS, + M_TX_FIFO_WATERMARK_EN, true)); + writel_relaxed(c, uport->membase + SE_GENI_TX_FIFOn); + writel_relaxed(M_TX_FIFO_WATERMARK_EN, uport->membase + + SE_GENI_M_IRQ_CLEAR); + qcom_geni_serial_poll_tx_done(uport); +} +#endif + +#ifdef CONFIG_SERIAL_QCOM_GENI_CONSOLE +static void qcom_geni_serial_wr_char(struct uart_port *uport, int ch) +{ + writel_relaxed(ch, uport->membase + SE_GENI_TX_FIFOn); +} + +static void +__qcom_geni_serial_console_write(struct uart_port *uport, const char *s, + unsigned int count) +{ + int i; + u32 bytes_to_send = count; + + for (i = 0; i < count; i++) { + if (s[i] == '\n') + bytes_to_send++; + } + + writel_relaxed(DEF_TX_WM, uport->membase + SE_GENI_TX_WATERMARK_REG); + qcom_geni_serial_setup_tx(uport, bytes_to_send); + for (i = 0; i < count; ) { + size_t chars_to_write = 0; + size_t avail = DEF_FIFO_DEPTH_WORDS - DEF_TX_WM; + + /* + * If the WM bit never set, then the Tx state machine is not + * in a valid state, so break, cancel/abort any existing + * command. Unfortunately the current data being written is + * lost. + */ + if (!qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS, + M_TX_FIFO_WATERMARK_EN, true)) + break; + chars_to_write = min_t(size_t, (size_t)(count - i), avail / 2); + uart_console_write(uport, s + i, chars_to_write, + qcom_geni_serial_wr_char); + writel_relaxed(M_TX_FIFO_WATERMARK_EN, uport->membase + + SE_GENI_M_IRQ_CLEAR); + i += chars_to_write; + } + qcom_geni_serial_poll_tx_done(uport); +} + +static void qcom_geni_serial_console_write(struct console *co, const char *s, + unsigned int count) +{ + struct uart_port *uport; + struct qcom_geni_serial_port *port; + bool locked = true; + unsigned long flags; + + WARN_ON(co->index < 0 || co->index >= GENI_UART_CONS_PORTS); + + port = get_port_from_line(co->index); + if (IS_ERR(port)) + return; + + uport = &port->uport; + if (oops_in_progress) + locked = spin_trylock_irqsave(&uport->lock, flags); + else + spin_lock_irqsave(&uport->lock, flags); + + /* Cancel the current write to log the fault */ + if (!locked) { + geni_se_cancel_m_cmd(&port->se); + if (!qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS, + M_CMD_CANCEL_EN, true)) { + geni_se_abort_m_cmd(&port->se); + qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS, + M_CMD_ABORT_EN, true); + writel_relaxed(M_CMD_ABORT_EN, uport->membase + + SE_GENI_M_IRQ_CLEAR); + } + writel_relaxed(M_CMD_CANCEL_EN, uport->membase + + SE_GENI_M_IRQ_CLEAR); + } + + __qcom_geni_serial_console_write(uport, s, count); + if (locked) + spin_unlock_irqrestore(&uport->lock, flags); +} + +static int handle_rx_console(struct uart_port *uport, u32 bytes, bool drop) +{ + u32 i; + unsigned char buf[sizeof(u32)]; + struct tty_port *tport; + struct qcom_geni_serial_port *port = to_dev_port(uport, uport); + + tport = &uport->state->port; + for (i = 0; i < bytes; ) { + int c; + int chunk = min_t(int, bytes - i, port->rx_bytes_pw); + + ioread32_rep(uport->membase + SE_GENI_RX_FIFOn, buf, 1); + i += chunk; + if (drop) + continue; + + for (c = 0; c < chunk; c++) { + int sysrq; + + uport->icount.rx++; + if (port->brk && buf[c] == 0) { + port->brk = false; + if (uart_handle_break(uport)) + continue; + } + + sysrq = uart_handle_sysrq_char(uport, buf[c]); + if (!sysrq) + tty_insert_flip_char(tport, buf[c], TTY_NORMAL); + } + } + if (!drop) + tty_flip_buffer_push(tport); + return 0; +} +#else +static int handle_rx_console(struct uart_port *uport, u32 bytes, bool drop) +{ + return -EPERM; +} + +#endif /* CONFIG_SERIAL_QCOM_GENI_CONSOLE */ + +static void qcom_geni_serial_start_tx(struct uart_port *uport) +{ + u32 irq_en; + struct qcom_geni_serial_port *port = to_dev_port(uport, uport); + u32 status; + + if (port->xfer_mode == GENI_SE_FIFO) { + status = readl_relaxed(uport->membase + SE_GENI_STATUS); + if (status & M_GENI_CMD_ACTIVE) + return; + + if (!qcom_geni_serial_tx_empty(uport)) + return; + + /* + * Ensure writing to IRQ_EN & watermark registers are not + * re-ordered before checking the status of the Serial + * Engine and TX FIFO + */ + mb(); + + irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN); + irq_en |= M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN; + + writel_relaxed(port->tx_wm, uport->membase + + SE_GENI_TX_WATERMARK_REG); + writel_relaxed(irq_en, uport->membase + SE_GENI_M_IRQ_EN); + } +} + +static void qcom_geni_serial_stop_tx(struct uart_port *uport) +{ + u32 irq_en; + u32 status; + struct qcom_geni_serial_port *port = to_dev_port(uport, uport); + + irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN); + irq_en &= ~M_CMD_DONE_EN; + if (port->xfer_mode == GENI_SE_FIFO) { + irq_en &= ~M_TX_FIFO_WATERMARK_EN; + writel_relaxed(0, uport->membase + + SE_GENI_TX_WATERMARK_REG); + } + port->xmit_size = 0; + writel_relaxed(irq_en, uport->membase + SE_GENI_M_IRQ_EN); + status = readl_relaxed(uport->membase + SE_GENI_STATUS); + /* Possible stop tx is called multiple times. */ + if (!(status & M_GENI_CMD_ACTIVE)) + return; + + /* + * Ensure cancel command write is not re-ordered before checking + * the status of the Primary Sequencer. + */ + mb(); + + geni_se_cancel_m_cmd(&port->se); + if (!qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS, + M_CMD_CANCEL_EN, true)) { + geni_se_abort_m_cmd(&port->se); + qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS, + M_CMD_ABORT_EN, true); + writel_relaxed(M_CMD_ABORT_EN, uport->membase + + SE_GENI_M_IRQ_CLEAR); + } + writel_relaxed(M_CMD_CANCEL_EN, uport->membase + SE_GENI_M_IRQ_CLEAR); +} + +static void qcom_geni_serial_start_rx(struct uart_port *uport) +{ + u32 irq_en; + u32 status; + struct qcom_geni_serial_port *port = to_dev_port(uport, uport); + + status = readl_relaxed(uport->membase + SE_GENI_STATUS); + if (status & S_GENI_CMD_ACTIVE) + qcom_geni_serial_stop_rx(uport); + + /* + * Ensure setup command write is not re-ordered before checking + * the status of the Secondary Sequencer. + */ + mb(); + + geni_se_setup_s_cmd(&port->se, UART_START_READ, 0); + + if (port->xfer_mode == GENI_SE_FIFO) { + irq_en = readl_relaxed(uport->membase + SE_GENI_S_IRQ_EN); + irq_en |= S_RX_FIFO_WATERMARK_EN | S_RX_FIFO_LAST_EN; + writel_relaxed(irq_en, uport->membase + SE_GENI_S_IRQ_EN); + + irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN); + irq_en |= M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN; + writel_relaxed(irq_en, uport->membase + SE_GENI_M_IRQ_EN); + } +} + +static void qcom_geni_serial_stop_rx(struct uart_port *uport) +{ + u32 irq_en; + u32 status; + struct qcom_geni_serial_port *port = to_dev_port(uport, uport); + u32 irq_clear = S_CMD_DONE_EN; + + if (port->xfer_mode == GENI_SE_FIFO) { + irq_en = readl_relaxed(uport->membase + SE_GENI_S_IRQ_EN); + irq_en &= ~(S_RX_FIFO_WATERMARK_EN | S_RX_FIFO_LAST_EN); + writel_relaxed(irq_en, uport->membase + SE_GENI_S_IRQ_EN); + + irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN); + irq_en &= ~(M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN); + writel_relaxed(irq_en, uport->membase + SE_GENI_M_IRQ_EN); + } + + status = readl_relaxed(uport->membase + SE_GENI_STATUS); + /* Possible stop rx is called multiple times. */ + if (!(status & S_GENI_CMD_ACTIVE)) + return; + + /* + * Ensure cancel command write is not re-ordered before checking + * the status of the Secondary Sequencer. + */ + mb(); + + geni_se_cancel_s_cmd(&port->se); + qcom_geni_serial_poll_bit(uport, SE_GENI_S_CMD_CTRL_REG, + S_GENI_CMD_CANCEL, false); + status = readl_relaxed(uport->membase + SE_GENI_STATUS); + writel_relaxed(irq_clear, uport->membase + SE_GENI_S_IRQ_CLEAR); + if (status & S_GENI_CMD_ACTIVE) + qcom_geni_serial_abort_rx(uport); +} + +static void qcom_geni_serial_handle_rx(struct uart_port *uport, bool drop) +{ + u32 status; + u32 word_cnt; + u32 last_word_byte_cnt; + u32 last_word_partial; + u32 total_bytes; + struct qcom_geni_serial_port *port = to_dev_port(uport, uport); + + status = readl_relaxed(uport->membase + SE_GENI_RX_FIFO_STATUS); + word_cnt = status & RX_FIFO_WC_MSK; + last_word_partial = status & RX_LAST; + last_word_byte_cnt = (status & RX_LAST_BYTE_VALID_MSK) >> + RX_LAST_BYTE_VALID_SHFT; + + if (!word_cnt) + return; + total_bytes = port->rx_bytes_pw * (word_cnt - 1); + if (last_word_partial && last_word_byte_cnt) + total_bytes += last_word_byte_cnt; + else + total_bytes += port->rx_bytes_pw; + port->handle_rx(uport, total_bytes, drop); +} + +static void qcom_geni_serial_handle_tx(struct uart_port *uport) +{ + struct qcom_geni_serial_port *port = to_dev_port(uport, uport); + struct circ_buf *xmit = &uport->state->xmit; + size_t avail; + size_t remaining; + int i; + u32 status; + unsigned int chunk; + int tail; + + chunk = uart_circ_chars_pending(xmit); + status = readl_relaxed(uport->membase + SE_GENI_TX_FIFO_STATUS); + /* Both FIFO and framework buffer are drained */ + if (chunk == port->xmit_size && !status) { + port->xmit_size = 0; + uart_circ_clear(xmit); + qcom_geni_serial_stop_tx(uport); + goto out_write_wakeup; + } + chunk -= port->xmit_size; + + avail = (port->tx_fifo_depth - port->tx_wm) * port->tx_bytes_pw; + tail = (xmit->tail + port->xmit_size) & (UART_XMIT_SIZE - 1); + if (chunk > (UART_XMIT_SIZE - tail)) + chunk = UART_XMIT_SIZE - tail; + if (chunk > avail) + chunk = avail; + + if (!chunk) + goto out_write_wakeup; + + qcom_geni_serial_setup_tx(uport, chunk); + + remaining = chunk; + for (i = 0; i < chunk; ) { + unsigned int tx_bytes; + unsigned int buf = 0; + int c; + + tx_bytes = min_t(size_t, remaining, (size_t)port->tx_bytes_pw); + for (c = 0; c < tx_bytes ; c++) + buf |= (xmit->buf[tail + c] << (c * BITS_PER_BYTE)); + + writel_relaxed(buf, uport->membase + SE_GENI_TX_FIFOn); + + i += tx_bytes; + tail = (tail + tx_bytes) & (UART_XMIT_SIZE - 1); + uport->icount.tx += tx_bytes; + remaining -= tx_bytes; + } + qcom_geni_serial_poll_tx_done(uport); + port->xmit_size += chunk; +out_write_wakeup: + uart_write_wakeup(uport); +} + +static irqreturn_t qcom_geni_serial_isr(int isr, void *dev) +{ + unsigned int m_irq_status; + unsigned int s_irq_status; + struct uart_port *uport = dev; + unsigned long flags; + unsigned int m_irq_en; + bool drop_rx = false; + struct tty_port *tport = &uport->state->port; + struct qcom_geni_serial_port *port = to_dev_port(uport, uport); + + if (uport->suspended) + return IRQ_HANDLED; + + spin_lock_irqsave(&uport->lock, flags); + m_irq_status = readl_relaxed(uport->membase + SE_GENI_M_IRQ_STATUS); + s_irq_status = readl_relaxed(uport->membase + SE_GENI_S_IRQ_STATUS); + m_irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN); + writel_relaxed(m_irq_status, uport->membase + SE_GENI_M_IRQ_CLEAR); + writel_relaxed(s_irq_status, uport->membase + SE_GENI_S_IRQ_CLEAR); + + if (WARN_ON(m_irq_status & M_ILLEGAL_CMD_EN)) + goto out_unlock; + + if (s_irq_status & S_RX_FIFO_WR_ERR_EN) { + uport->icount.overrun++; + tty_insert_flip_char(tport, 0, TTY_OVERRUN); + } + + if (m_irq_status & (M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN) && + m_irq_en & (M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN)) + qcom_geni_serial_handle_tx(uport); + + if (s_irq_status & S_GP_IRQ_0_EN || s_irq_status & S_GP_IRQ_1_EN) { + if (s_irq_status & S_GP_IRQ_0_EN) + uport->icount.parity++; + drop_rx = true; + } else if (s_irq_status & S_GP_IRQ_2_EN || + s_irq_status & S_GP_IRQ_3_EN) { + uport->icount.brk++; + port->brk = true; + } + + if (s_irq_status & S_RX_FIFO_WATERMARK_EN || + s_irq_status & S_RX_FIFO_LAST_EN) + qcom_geni_serial_handle_rx(uport, drop_rx); + +out_unlock: + spin_unlock_irqrestore(&uport->lock, flags); + return IRQ_HANDLED; +} + +static int get_tx_fifo_size(struct qcom_geni_serial_port *port) +{ + struct uart_port *uport; + + if (!port) + return -ENODEV; + + uport = &port->uport; + port->tx_fifo_depth = geni_se_get_tx_fifo_depth(&port->se); + port->tx_fifo_width = geni_se_get_tx_fifo_width(&port->se); + port->rx_fifo_depth = geni_se_get_rx_fifo_depth(&port->se); + uport->fifosize = + (port->tx_fifo_depth * port->tx_fifo_width) / BITS_PER_BYTE; + return 0; +} + +static void set_rfr_wm(struct qcom_geni_serial_port *port) +{ + /* + * Set RFR (Flow off) to FIFO_DEPTH - 2. + * RX WM level at 10% RX_FIFO_DEPTH. + * TX WM level at 10% TX_FIFO_DEPTH. + */ + port->rx_rfr = port->rx_fifo_depth - 2; + port->rx_wm = UART_CONSOLE_RX_WM; + port->tx_wm = DEF_TX_WM; +} + +static void qcom_geni_serial_shutdown(struct uart_port *uport) +{ + unsigned long flags; + + /* Stop the console before stopping the current tx */ + console_stop(uport->cons); + + disable_irq(uport->irq); + free_irq(uport->irq, uport); + spin_lock_irqsave(&uport->lock, flags); + qcom_geni_serial_stop_tx(uport); + qcom_geni_serial_stop_rx(uport); + spin_unlock_irqrestore(&uport->lock, flags); +} + +static int qcom_geni_serial_port_setup(struct uart_port *uport) +{ + struct qcom_geni_serial_port *port = to_dev_port(uport, uport); + unsigned int rxstale = DEFAULT_BITS_PER_CHAR * STALE_TIMEOUT; + + set_rfr_wm(port); + writel_relaxed(rxstale, uport->membase + SE_UART_RX_STALE_CNT); + /* + * Make an unconditional cancel on the main sequencer to reset + * it else we could end up in data loss scenarios. + */ + port->xfer_mode = GENI_SE_FIFO; + qcom_geni_serial_poll_tx_done(uport); + geni_se_config_packing(&port->se, BITS_PER_BYTE, port->tx_bytes_pw, + false, true, false); + geni_se_config_packing(&port->se, BITS_PER_BYTE, port->rx_bytes_pw, + false, false, true); + geni_se_init(&port->se, port->rx_wm, port->rx_rfr); + geni_se_select_mode(&port->se, port->xfer_mode); + port->setup = true; + return 0; +} + +static int qcom_geni_serial_startup(struct uart_port *uport) +{ + int ret; + u32 proto; + struct qcom_geni_serial_port *port = to_dev_port(uport, uport); + + scnprintf(port->name, sizeof(port->name), + "qcom_serial_geni%d", uport->line); + + proto = geni_se_read_proto(&port->se); + if (proto != GENI_SE_UART) { + dev_err(uport->dev, "Invalid FW loaded, proto: %d\n", proto); + return -ENXIO; + } + + get_tx_fifo_size(port); + if (!port->setup) { + ret = qcom_geni_serial_port_setup(uport); + if (ret) + return ret; + } + + ret = request_irq(uport->irq, qcom_geni_serial_isr, IRQF_TRIGGER_HIGH, + port->name, uport); + if (ret) + dev_err(uport->dev, "Failed to get IRQ ret %d\n", ret); + return ret; +} + +static unsigned long get_clk_cfg(unsigned long clk_freq) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(root_freq); i++) { + if (!(root_freq[i] % clk_freq)) + return root_freq[i]; + } + return 0; +} + +static unsigned long get_clk_div_rate(unsigned int baud, unsigned int *clk_div) +{ + unsigned long ser_clk; + unsigned long desired_clk; + + desired_clk = baud * UART_OVERSAMPLING; + ser_clk = get_clk_cfg(desired_clk); + if (!ser_clk) { + pr_err("%s: Can't find matching DFS entry for baud %d\n", + __func__, baud); + return ser_clk; + } + + *clk_div = ser_clk / desired_clk; + return ser_clk; +} + +static void qcom_geni_serial_set_termios(struct uart_port *uport, + struct ktermios *termios, struct ktermios *old) +{ + unsigned int baud; + unsigned int bits_per_char; + unsigned int tx_trans_cfg; + unsigned int tx_parity_cfg; + unsigned int rx_trans_cfg; + unsigned int rx_parity_cfg; + unsigned int stop_bit_len; + unsigned int clk_div; + unsigned long ser_clk_cfg; + struct qcom_geni_serial_port *port = to_dev_port(uport, uport); + unsigned long clk_rate; + + qcom_geni_serial_stop_rx(uport); + /* baud rate */ + baud = uart_get_baud_rate(uport, termios, old, 300, 4000000); + port->baud = baud; + clk_rate = get_clk_div_rate(baud, &clk_div); + if (!clk_rate) + goto out_restart_rx; + + uport->uartclk = clk_rate; + clk_set_rate(port->se.clk, clk_rate); + ser_clk_cfg = SER_CLK_EN; + ser_clk_cfg |= clk_div << CLK_DIV_SHFT; + + /* parity */ + tx_trans_cfg = readl_relaxed(uport->membase + SE_UART_TX_TRANS_CFG); + tx_parity_cfg = readl_relaxed(uport->membase + SE_UART_TX_PARITY_CFG); + rx_trans_cfg = readl_relaxed(uport->membase + SE_UART_RX_TRANS_CFG); + rx_parity_cfg = readl_relaxed(uport->membase + SE_UART_RX_PARITY_CFG); + if (termios->c_cflag & PARENB) { + tx_trans_cfg |= UART_TX_PAR_EN; + rx_trans_cfg |= UART_RX_PAR_EN; + tx_parity_cfg |= PAR_CALC_EN; + rx_parity_cfg |= PAR_CALC_EN; + if (termios->c_cflag & PARODD) { + tx_parity_cfg |= PAR_ODD; + rx_parity_cfg |= PAR_ODD; + } else if (termios->c_cflag & CMSPAR) { + tx_parity_cfg |= PAR_SPACE; + rx_parity_cfg |= PAR_SPACE; + } else { + tx_parity_cfg |= PAR_EVEN; + rx_parity_cfg |= PAR_EVEN; + } + } else { + tx_trans_cfg &= ~UART_TX_PAR_EN; + rx_trans_cfg &= ~UART_RX_PAR_EN; + tx_parity_cfg &= ~PAR_CALC_EN; + rx_parity_cfg &= ~PAR_CALC_EN; + } + + /* bits per char */ + switch (termios->c_cflag & CSIZE) { + case CS5: + bits_per_char = 5; + break; + case CS6: + bits_per_char = 6; + break; + case CS7: + bits_per_char = 7; + break; + case CS8: + default: + bits_per_char = 8; + break; + } + + /* stop bits */ + if (termios->c_cflag & CSTOPB) + stop_bit_len = TX_STOP_BIT_LEN_2; + else + stop_bit_len = TX_STOP_BIT_LEN_1; + + /* flow control, clear the CTS_MASK bit if using flow control. */ + if (termios->c_cflag & CRTSCTS) + tx_trans_cfg &= ~UART_CTS_MASK; + else + tx_trans_cfg |= UART_CTS_MASK; + + if (baud) + uart_update_timeout(uport, termios->c_cflag, baud); + + writel_relaxed(tx_trans_cfg, uport->membase + SE_UART_TX_TRANS_CFG); + writel_relaxed(tx_parity_cfg, uport->membase + SE_UART_TX_PARITY_CFG); + writel_relaxed(rx_trans_cfg, uport->membase + SE_UART_RX_TRANS_CFG); + writel_relaxed(rx_parity_cfg, uport->membase + SE_UART_RX_PARITY_CFG); + writel_relaxed(bits_per_char, uport->membase + SE_UART_TX_WORD_LEN); + writel_relaxed(bits_per_char, uport->membase + SE_UART_RX_WORD_LEN); + writel_relaxed(stop_bit_len, uport->membase + SE_UART_TX_STOP_BIT_LEN); + writel_relaxed(ser_clk_cfg, uport->membase + GENI_SER_M_CLK_CFG); + writel_relaxed(ser_clk_cfg, uport->membase + GENI_SER_S_CLK_CFG); +out_restart_rx: + qcom_geni_serial_start_rx(uport); +} + +static unsigned int qcom_geni_serial_tx_empty(struct uart_port *uport) +{ + return !readl_relaxed(uport->membase + SE_GENI_TX_FIFO_STATUS); +} + +#ifdef CONFIG_SERIAL_QCOM_GENI_CONSOLE +static int __init qcom_geni_console_setup(struct console *co, char *options) +{ + struct uart_port *uport; + struct qcom_geni_serial_port *port; + int baud; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + if (co->index >= GENI_UART_CONS_PORTS || co->index < 0) + return -ENXIO; + + port = get_port_from_line(co->index); + if (IS_ERR(port)) { + pr_err("Invalid line %d(%d)\n", co->index, (int)PTR_ERR(port)); + return PTR_ERR(port); + } + + uport = &port->uport; + + if (unlikely(!uport->membase)) + return -ENXIO; + + if (geni_se_resources_on(&port->se)) { + dev_err(port->se.dev, "Error turning on resources\n"); + return -ENXIO; + } + + if (unlikely(geni_se_read_proto(&port->se) != GENI_SE_UART)) { + geni_se_resources_off(&port->se); + return -ENXIO; + } + + if (!port->setup) { + port->tx_bytes_pw = 1; + port->rx_bytes_pw = RX_BYTES_PW; + qcom_geni_serial_stop_rx(uport); + qcom_geni_serial_port_setup(uport); + } + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(uport, co, baud, parity, bits, flow); +} + +static int __init console_register(struct uart_driver *drv) +{ + return uart_register_driver(drv); +} + +static void console_unregister(struct uart_driver *drv) +{ + uart_unregister_driver(drv); +} + +static struct console cons_ops = { + .name = "ttyMSM", + .write = qcom_geni_serial_console_write, + .device = uart_console_device, + .setup = qcom_geni_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &qcom_geni_console_driver, +}; + +static struct uart_driver qcom_geni_console_driver = { + .owner = THIS_MODULE, + .driver_name = "qcom_geni_console", + .dev_name = "ttyMSM", + .nr = GENI_UART_CONS_PORTS, + .cons = &cons_ops, +}; +#else +static int console_register(struct uart_driver *drv) +{ + return 0; +} + +static void console_unregister(struct uart_driver *drv) +{ +} +#endif /* CONFIG_SERIAL_QCOM_GENI_CONSOLE */ + +static void qcom_geni_serial_cons_pm(struct uart_port *uport, + unsigned int new_state, unsigned int old_state) +{ + struct qcom_geni_serial_port *port = to_dev_port(uport, uport); + + if (unlikely(!uart_console(uport))) + return; + + if (new_state == UART_PM_STATE_ON && old_state == UART_PM_STATE_OFF) + geni_se_resources_on(&port->se); + else if (new_state == UART_PM_STATE_OFF && + old_state == UART_PM_STATE_ON) + geni_se_resources_off(&port->se); +} + +static const struct uart_ops qcom_geni_console_pops = { + .tx_empty = qcom_geni_serial_tx_empty, + .stop_tx = qcom_geni_serial_stop_tx, + .start_tx = qcom_geni_serial_start_tx, + .stop_rx = qcom_geni_serial_stop_rx, + .set_termios = qcom_geni_serial_set_termios, + .startup = qcom_geni_serial_startup, + .request_port = qcom_geni_serial_request_port, + .config_port = qcom_geni_serial_config_port, + .shutdown = qcom_geni_serial_shutdown, + .type = qcom_geni_serial_get_type, + .set_mctrl = qcom_geni_cons_set_mctrl, + .get_mctrl = qcom_geni_cons_get_mctrl, +#ifdef CONFIG_CONSOLE_POLL + .poll_get_char = qcom_geni_serial_get_char, + .poll_put_char = qcom_geni_serial_poll_put_char, +#endif + .pm = qcom_geni_serial_cons_pm, +}; + +static int qcom_geni_serial_probe(struct platform_device *pdev) +{ + int ret = 0; + int line = -1; + struct qcom_geni_serial_port *port; + struct uart_port *uport; + struct resource *res; + + if (pdev->dev.of_node) + line = of_alias_get_id(pdev->dev.of_node, "serial"); + else + line = pdev->id; + + if (line < 0 || line >= GENI_UART_CONS_PORTS) + return -ENXIO; + port = get_port_from_line(line); + if (IS_ERR(port)) { + ret = PTR_ERR(port); + dev_err(&pdev->dev, "Invalid line %d(%d)\n", line, ret); + return ret; + } + + uport = &port->uport; + /* Don't allow 2 drivers to access the same port */ + if (uport->private_data) + return -ENODEV; + + uport->dev = &pdev->dev; + port->se.dev = &pdev->dev; + port->se.wrapper = dev_get_drvdata(pdev->dev.parent); + port->se.clk = devm_clk_get(&pdev->dev, "se"); + if (IS_ERR(port->se.clk)) { + ret = PTR_ERR(port->se.clk); + dev_err(&pdev->dev, "Err getting SE Core clk %d\n", ret); + return ret; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -EINVAL; + uport->mapbase = res->start; + + port->tx_fifo_depth = DEF_FIFO_DEPTH_WORDS; + port->rx_fifo_depth = DEF_FIFO_DEPTH_WORDS; + port->tx_fifo_width = DEF_FIFO_WIDTH_BITS; + + uport->irq = platform_get_irq(pdev, 0); + if (uport->irq < 0) { + dev_err(&pdev->dev, "Failed to get IRQ %d\n", uport->irq); + return uport->irq; + } + + uport->private_data = &qcom_geni_console_driver; + platform_set_drvdata(pdev, port); + port->handle_rx = handle_rx_console; + port->setup = false; + return uart_add_one_port(&qcom_geni_console_driver, uport); +} + +static int qcom_geni_serial_remove(struct platform_device *pdev) +{ + struct qcom_geni_serial_port *port = platform_get_drvdata(pdev); + struct uart_driver *drv = port->uport.private_data; + + uart_remove_one_port(drv, &port->uport); + return 0; +} + +static int __maybe_unused qcom_geni_serial_sys_suspend_noirq(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct qcom_geni_serial_port *port = platform_get_drvdata(pdev); + struct uart_port *uport = &port->uport; + + uart_suspend_port(uport->private_data, uport); + return 0; +} + +static int __maybe_unused qcom_geni_serial_sys_resume_noirq(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct qcom_geni_serial_port *port = platform_get_drvdata(pdev); + struct uart_port *uport = &port->uport; + + if (console_suspend_enabled && uport->suspended) { + uart_resume_port(uport->private_data, uport); + disable_irq(uport->irq); + } + return 0; +} + +static const struct dev_pm_ops qcom_geni_serial_pm_ops = { + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(qcom_geni_serial_sys_suspend_noirq, + qcom_geni_serial_sys_resume_noirq) +}; + +static const struct of_device_id qcom_geni_serial_match_table[] = { + { .compatible = "qcom,geni-debug-uart", }, + {} +}; +MODULE_DEVICE_TABLE(of, qcom_geni_serial_match_table); + +static struct platform_driver qcom_geni_serial_platform_driver = { + .remove = qcom_geni_serial_remove, + .probe = qcom_geni_serial_probe, + .driver = { + .name = "qcom_geni_serial", + .of_match_table = qcom_geni_serial_match_table, + .pm = &qcom_geni_serial_pm_ops, + }, +}; + +static int __init qcom_geni_serial_init(void) +{ + int ret; + + qcom_geni_console_port.uport.iotype = UPIO_MEM; + qcom_geni_console_port.uport.ops = &qcom_geni_console_pops; + qcom_geni_console_port.uport.flags = UPF_BOOT_AUTOCONF; + qcom_geni_console_port.uport.line = 0; + + ret = console_register(&qcom_geni_console_driver); + if (ret) + return ret; + + ret = platform_driver_register(&qcom_geni_serial_platform_driver); + if (ret) + console_unregister(&qcom_geni_console_driver); + return ret; +} +module_init(qcom_geni_serial_init); + +static void __exit qcom_geni_serial_exit(void) +{ + platform_driver_unregister(&qcom_geni_serial_platform_driver); + console_unregister(&qcom_geni_console_driver); +} +module_exit(qcom_geni_serial_exit); + +MODULE_DESCRIPTION("Serial driver for GENI based QUP cores"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index f9fecc5ed0ce..3f2f8c118ce0 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -1818,6 +1818,10 @@ static int s3c24xx_serial_probe(struct platform_device *pdev) dbg("s3c24xx_serial_probe(%p) %d\n", pdev, index); + if (index >= ARRAY_SIZE(s3c24xx_serial_ports)) { + dev_err(&pdev->dev, "serial%d out of range\n", index); + return -EINVAL; + } ourport = &s3c24xx_serial_ports[index]; ourport->drv_data = s3c24xx_get_driver_data(pdev); diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 35b9201db3b4..0466f9f08a91 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -1786,6 +1786,8 @@ static void uart_line_info(struct seq_file *m, struct uart_driver *drv, int i) seq_printf(m, " brk:%d", uport->icount.brk); if (uport->icount.overrun) seq_printf(m, " oe:%d", uport->icount.overrun); + if (uport->icount.buf_overrun) + seq_printf(m, " bo:%d", uport->icount.buf_overrun); #define INFOBIT(bit, str) \ if (uport->mctrl & (bit)) \ diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 44adf9db38f8..fdbbff547106 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -33,6 +33,7 @@ #include <linux/init.h> #include <linux/interrupt.h> #include <linux/ioport.h> +#include <linux/ktime.h> #include <linux/major.h> #include <linux/module.h> #include <linux/mm.h> @@ -143,8 +144,8 @@ struct sci_port { void *rx_buf[2]; size_t buf_len_rx; struct work_struct work_tx; - struct timer_list rx_timer; - unsigned int rx_timeout; + struct hrtimer rx_timer; + unsigned int rx_timeout; /* microseconds */ #endif unsigned int rx_frame; int rx_trigger; @@ -1231,6 +1232,15 @@ static void sci_rx_dma_release(struct sci_port *s, bool enable_pio) } } +static void start_hrtimer_us(struct hrtimer *hrt, unsigned long usec) +{ + long sec = usec / 1000000; + long nsec = (usec % 1000000) * 1000; + ktime_t t = ktime_set(sec, nsec); + + hrtimer_start(hrt, t, HRTIMER_MODE_REL); +} + static void sci_dma_rx_complete(void *arg) { struct sci_port *s = arg; @@ -1249,7 +1259,7 @@ static void sci_dma_rx_complete(void *arg) if (active >= 0) count = sci_dma_rx_push(s, s->rx_buf[active], s->buf_len_rx); - mod_timer(&s->rx_timer, jiffies + s->rx_timeout); + start_hrtimer_us(&s->rx_timer, s->rx_timeout); if (count) tty_flip_buffer_push(&port->state->port); @@ -1393,9 +1403,9 @@ static void work_fn_tx(struct work_struct *work) dma_async_issue_pending(chan); } -static void rx_timer_fn(struct timer_list *t) +static enum hrtimer_restart rx_timer_fn(struct hrtimer *t) { - struct sci_port *s = from_timer(s, t, rx_timer); + struct sci_port *s = container_of(t, struct sci_port, rx_timer); struct dma_chan *chan = s->chan_rx; struct uart_port *port = &s->port; struct dma_tx_state state; @@ -1412,7 +1422,7 @@ static void rx_timer_fn(struct timer_list *t) active = sci_dma_rx_find_active(s); if (active < 0) { spin_unlock_irqrestore(&port->lock, flags); - return; + return HRTIMER_NORESTART; } status = dmaengine_tx_status(s->chan_rx, s->active_rx, &state); @@ -1422,7 +1432,7 @@ static void rx_timer_fn(struct timer_list *t) s->active_rx, active); /* Let packet complete handler take care of the packet */ - return; + return HRTIMER_NORESTART; } dmaengine_pause(chan); @@ -1437,7 +1447,7 @@ static void rx_timer_fn(struct timer_list *t) if (status == DMA_COMPLETE) { spin_unlock_irqrestore(&port->lock, flags); dev_dbg(port->dev, "Transaction complete after DMA engine was stopped"); - return; + return HRTIMER_NORESTART; } /* Handle incomplete DMA receive */ @@ -1462,6 +1472,8 @@ static void rx_timer_fn(struct timer_list *t) serial_port_out(port, SCSCR, scr | SCSCR_RIE); spin_unlock_irqrestore(&port->lock, flags); + + return HRTIMER_NORESTART; } static struct dma_chan *sci_request_dma_chan(struct uart_port *port, @@ -1573,7 +1585,8 @@ static void sci_request_dma(struct uart_port *port) dma += s->buf_len_rx; } - timer_setup(&s->rx_timer, rx_timer_fn, 0); + hrtimer_init(&s->rx_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + s->rx_timer.function = rx_timer_fn; if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) sci_submit_rx(s); @@ -1632,9 +1645,9 @@ static irqreturn_t sci_rx_interrupt(int irq, void *ptr) /* Clear current interrupt */ serial_port_out(port, SCxSR, ssr & ~(SCIF_DR | SCxSR_RDxF(port))); - dev_dbg(port->dev, "Rx IRQ %lu: setup t-out in %u jiffies\n", + dev_dbg(port->dev, "Rx IRQ %lu: setup t-out in %u us\n", jiffies, s->rx_timeout); - mod_timer(&s->rx_timer, jiffies + s->rx_timeout); + start_hrtimer_us(&s->rx_timer, s->rx_timeout); return IRQ_HANDLED; } @@ -1645,7 +1658,7 @@ static irqreturn_t sci_rx_interrupt(int irq, void *ptr) scif_set_rtrg(port, s->rx_trigger); mod_timer(&s->rx_fifo_timer, jiffies + DIV_ROUND_UP( - s->rx_frame * s->rx_fifo_timeout, 1000)); + s->rx_frame * HZ * s->rx_fifo_timeout, 1000000)); } /* I think sci_receive_chars has to be called irrespective @@ -2081,7 +2094,7 @@ static void sci_shutdown(struct uart_port *port) if (s->chan_rx) { dev_dbg(port->dev, "%s(%d) deleting rx_timer\n", __func__, port->line); - del_timer_sync(&s->rx_timer); + hrtimer_cancel(&s->rx_timer); } #endif @@ -2482,11 +2495,11 @@ done: if (termios->c_cflag & PARENB) bits++; - s->rx_frame = (100 * bits * HZ) / (baud / 10); + s->rx_frame = (10000 * bits) / (baud / 100); #ifdef CONFIG_SERIAL_SH_SCI_DMA - s->rx_timeout = DIV_ROUND_UP(s->buf_len_rx * 2 * s->rx_frame, 1000); - if (s->rx_timeout < msecs_to_jiffies(20)) - s->rx_timeout = msecs_to_jiffies(20); + s->rx_timeout = s->buf_len_rx * 2 * s->rx_frame; + if (s->rx_timeout < 20) + s->rx_timeout = 20; #endif if ((termios->c_cflag & CREAD) != 0) @@ -3098,6 +3111,10 @@ static struct plat_sci_port *sci_parse_dt(struct platform_device *pdev, dev_err(&pdev->dev, "failed to get alias id (%d)\n", id); return NULL; } + if (id >= ARRAY_SIZE(sci_ports)) { + dev_err(&pdev->dev, "serial%d out of range\n", id); + return NULL; + } sp = &sci_ports[id]; *dev_id = id; diff --git a/drivers/tty/serial/sirfsoc_uart.c b/drivers/tty/serial/sirfsoc_uart.c index 9925b00a9777..38622f2a30a9 100644 --- a/drivers/tty/serial/sirfsoc_uart.c +++ b/drivers/tty/serial/sirfsoc_uart.c @@ -1283,6 +1283,11 @@ static int sirfsoc_uart_probe(struct platform_device *pdev) goto err; } sirfport->port.line = of_alias_get_id(np, "serial"); + if (sirfport->port.line >= ARRAY_SIZE(sirf_ports)) { + dev_err(&pdev->dev, "serial%d out of range\n", + sirfport->port.line); + return -EINVAL; + } sirf_ports[sirfport->port.line] = sirfport; sirfport->port.iotype = UPIO_MEM; sirfport->port.flags = UPF_BOOT_AUTOCONF; diff --git a/drivers/tty/serial/st-asc.c b/drivers/tty/serial/st-asc.c index c763253514e9..5f9f01fac6dd 100644 --- a/drivers/tty/serial/st-asc.c +++ b/drivers/tty/serial/st-asc.c @@ -782,7 +782,9 @@ static struct asc_port *asc_of_get_asc_port(struct platform_device *pdev) if (!np) return NULL; - id = of_alias_get_id(np, ASC_SERIAL_NAME); + id = of_alias_get_id(np, "serial"); + if (id < 0) + id = of_alias_get_id(np, ASC_SERIAL_NAME); if (id < 0) id = 0; diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index 0fa735b60f2d..e8d7a7bb4339 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -62,6 +62,113 @@ static void stm32_clr_bits(struct uart_port *port, u32 reg, u32 bits) writel_relaxed(val, port->membase + reg); } +static void stm32_config_reg_rs485(u32 *cr1, u32 *cr3, u32 delay_ADE, + u32 delay_DDE, u32 baud) +{ + u32 rs485_deat_dedt; + u32 rs485_deat_dedt_max = (USART_CR1_DEAT_MASK >> USART_CR1_DEAT_SHIFT); + bool over8; + + *cr3 |= USART_CR3_DEM; + over8 = *cr1 & USART_CR1_OVER8; + + if (over8) + rs485_deat_dedt = delay_ADE * baud * 8; + else + rs485_deat_dedt = delay_ADE * baud * 16; + + rs485_deat_dedt = DIV_ROUND_CLOSEST(rs485_deat_dedt, 1000); + rs485_deat_dedt = rs485_deat_dedt > rs485_deat_dedt_max ? + rs485_deat_dedt_max : rs485_deat_dedt; + rs485_deat_dedt = (rs485_deat_dedt << USART_CR1_DEAT_SHIFT) & + USART_CR1_DEAT_MASK; + *cr1 |= rs485_deat_dedt; + + if (over8) + rs485_deat_dedt = delay_DDE * baud * 8; + else + rs485_deat_dedt = delay_DDE * baud * 16; + + rs485_deat_dedt = DIV_ROUND_CLOSEST(rs485_deat_dedt, 1000); + rs485_deat_dedt = rs485_deat_dedt > rs485_deat_dedt_max ? + rs485_deat_dedt_max : rs485_deat_dedt; + rs485_deat_dedt = (rs485_deat_dedt << USART_CR1_DEDT_SHIFT) & + USART_CR1_DEDT_MASK; + *cr1 |= rs485_deat_dedt; +} + +static int stm32_config_rs485(struct uart_port *port, + struct serial_rs485 *rs485conf) +{ + 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 usartdiv, baud, cr1, cr3; + bool over8; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + stm32_clr_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit)); + + port->rs485 = *rs485conf; + + rs485conf->flags |= SER_RS485_RX_DURING_TX; + + if (rs485conf->flags & SER_RS485_ENABLED) { + cr1 = readl_relaxed(port->membase + ofs->cr1); + cr3 = readl_relaxed(port->membase + ofs->cr3); + usartdiv = readl_relaxed(port->membase + ofs->brr); + usartdiv = usartdiv & GENMASK(15, 0); + over8 = cr1 & USART_CR1_OVER8; + + if (over8) + usartdiv = usartdiv | (usartdiv & GENMASK(4, 0)) + << USART_BRR_04_R_SHIFT; + + baud = DIV_ROUND_CLOSEST(port->uartclk, usartdiv); + stm32_config_reg_rs485(&cr1, &cr3, + rs485conf->delay_rts_before_send, + rs485conf->delay_rts_after_send, baud); + + if (rs485conf->flags & SER_RS485_RTS_ON_SEND) { + cr3 &= ~USART_CR3_DEP; + rs485conf->flags &= ~SER_RS485_RTS_AFTER_SEND; + } else { + cr3 |= USART_CR3_DEP; + rs485conf->flags |= SER_RS485_RTS_AFTER_SEND; + } + + writel_relaxed(cr3, port->membase + ofs->cr3); + writel_relaxed(cr1, port->membase + ofs->cr1); + } else { + stm32_clr_bits(port, ofs->cr3, USART_CR3_DEM | USART_CR3_DEP); + stm32_clr_bits(port, ofs->cr1, + USART_CR1_DEDT_MASK | USART_CR1_DEAT_MASK); + } + + stm32_set_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit)); + spin_unlock_irqrestore(&port->lock, flags); + + return 0; +} + +static int stm32_init_rs485(struct uart_port *port, + struct platform_device *pdev) +{ + struct serial_rs485 *rs485conf = &port->rs485; + + rs485conf->flags = 0; + rs485conf->delay_rts_before_send = 0; + rs485conf->delay_rts_after_send = 0; + + if (!pdev->dev.of_node) + return -ENODEV; + + uart_get_rs485_mode(&pdev->dev, rs485conf); + + return 0; +} + static int stm32_pending_rx(struct uart_port *port, u32 *sr, int *last_res, bool threaded) { @@ -498,6 +605,7 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios, 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; + struct serial_rs485 *rs485conf = &port->rs485; unsigned int baud; u32 usartdiv, mantissa, fraction, oversampling; tcflag_t cflag = termios->c_cflag; @@ -515,7 +623,7 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios, writel_relaxed(0, port->membase + ofs->cr1); cr1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_RXNEIE; - cr1 |= BIT(cfg->uart_enable_bit); + if (stm32_port->fifoen) cr1 |= USART_CR1_FIFOEN; cr2 = 0; @@ -553,9 +661,11 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios, */ if (usartdiv < 16) { oversampling = 8; + cr1 |= USART_CR1_OVER8; stm32_set_bits(port, ofs->cr1, USART_CR1_OVER8); } else { oversampling = 16; + cr1 &= ~USART_CR1_OVER8; stm32_clr_bits(port, ofs->cr1, USART_CR1_OVER8); } @@ -592,10 +702,28 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios, if (stm32_port->rx_ch) cr3 |= USART_CR3_DMAR; + if (rs485conf->flags & SER_RS485_ENABLED) { + stm32_config_reg_rs485(&cr1, &cr3, + rs485conf->delay_rts_before_send, + rs485conf->delay_rts_after_send, baud); + if (rs485conf->flags & SER_RS485_RTS_ON_SEND) { + cr3 &= ~USART_CR3_DEP; + rs485conf->flags &= ~SER_RS485_RTS_AFTER_SEND; + } else { + cr3 |= USART_CR3_DEP; + rs485conf->flags |= SER_RS485_RTS_AFTER_SEND; + } + + } else { + cr3 &= ~(USART_CR3_DEM | USART_CR3_DEP); + cr1 &= ~(USART_CR1_DEDT_MASK | USART_CR1_DEAT_MASK); + } + writel_relaxed(cr3, port->membase + ofs->cr3); writel_relaxed(cr2, port->membase + ofs->cr2); writel_relaxed(cr1, port->membase + ofs->cr1); + stm32_set_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit)); spin_unlock_irqrestore(&port->lock, flags); } @@ -681,6 +809,10 @@ static int stm32_init_port(struct stm32_port *stm32port, port->ops = &stm32_uart_ops; port->dev = &pdev->dev; port->irq = platform_get_irq(pdev, 0); + port->rs485_config = stm32_config_rs485; + + stm32_init_rs485(port, pdev); + stm32port->wakeirq = platform_get_irq(pdev, 1); stm32port->fifoen = stm32port->info->cfg.has_fifo; diff --git a/drivers/tty/serial/stm32-usart.h b/drivers/tty/serial/stm32-usart.h index 2294d0f05872..6f294e280ea3 100644 --- a/drivers/tty/serial/stm32-usart.h +++ b/drivers/tty/serial/stm32-usart.h @@ -135,6 +135,7 @@ struct stm32_usart_info stm32h7_info = { #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 +#define USART_BRR_04_R_SHIFT 1 /* USART_CR1 */ #define USART_CR1_SBK BIT(0) @@ -162,6 +163,8 @@ struct stm32_usart_info stm32h7_info = { #define USART_CR1_M1 BIT(28) /* F7 */ #define USART_CR1_IE_MASK (GENMASK(8, 4) | BIT(14) | BIT(26) | BIT(27)) #define USART_CR1_FIFOEN BIT(29) /* H7 */ +#define USART_CR1_DEAT_SHIFT 21 +#define USART_CR1_DEDT_SHIFT 16 /* USART_CR2 */ #define USART_CR2_ADD_MASK GENMASK(3, 0) /* F4 */ diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index b9b2bc76bcac..abcb4d09a2d8 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -1110,7 +1110,7 @@ static struct uart_port *cdns_uart_get_port(int id) struct uart_port *port; /* Try the given port id if failed use default method */ - if (cdns_uart_port[id].mapbase != 0) { + if (id < CDNS_UART_NR_PORTS && cdns_uart_port[id].mapbase != 0) { /* Find the next unused port */ for (id = 0; id < CDNS_UART_NR_PORTS; id++) if (cdns_uart_port[id].mapbase == 0) diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index b4e57c5a8bba..f97251f39c26 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -1354,6 +1354,11 @@ static void csi_m(struct vc_data *vc) case 3: vc->vc_italic = 1; break; + case 21: + /* + * No console drivers support double underline, so + * convert it to a single underline. + */ case 4: vc->vc_underline = 1; break; @@ -1389,7 +1394,6 @@ static void csi_m(struct vc_data *vc) vc->vc_disp_ctrl = 1; vc->vc_toggle_meta = 1; break; - case 21: case 22: vc->vc_intensity = 1; break; diff --git a/drivers/usb/misc/sisusbvga/sisusb_con.c b/drivers/usb/misc/sisusbvga/sisusb_con.c index 73f7bde78e11..a0d6e0af957c 100644 --- a/drivers/usb/misc/sisusbvga/sisusb_con.c +++ b/drivers/usb/misc/sisusbvga/sisusb_con.c @@ -1217,7 +1217,7 @@ font_op_error: /* Interface routine */ static int sisusbcon_font_set(struct vc_data *c, struct console_font *font, - unsigned flags) + unsigned int flags) { struct sisusb_usb_data *sisusb; unsigned charcount = font->charcount; @@ -1338,29 +1338,65 @@ static void sisusbdummycon_init(struct vc_data *vc, int init) vc_resize(vc, 80, 25); } -static int sisusbdummycon_dummy(void) +static void sisusbdummycon_deinit(struct vc_data *vc) { } +static void sisusbdummycon_clear(struct vc_data *vc, int sy, int sx, + int height, int width) { } +static void sisusbdummycon_putc(struct vc_data *vc, int c, int ypos, + int xpos) { } +static void sisusbdummycon_putcs(struct vc_data *vc, const unsigned short *s, + int count, int ypos, int xpos) { } +static void sisusbdummycon_cursor(struct vc_data *vc, int mode) { } + +static bool sisusbdummycon_scroll(struct vc_data *vc, unsigned int top, + unsigned int bottom, enum con_scroll dir, + unsigned int lines) { - return 0; + return false; } -#define SISUSBCONDUMMY (void *)sisusbdummycon_dummy +static int sisusbdummycon_switch(struct vc_data *vc) +{ + return 0; +} + +static int sisusbdummycon_blank(struct vc_data *vc, int blank, int mode_switch) +{ + return 0; +} + +static int sisusbdummycon_font_set(struct vc_data *vc, + struct console_font *font, + unsigned int flags) +{ + return 0; +} + +static int sisusbdummycon_font_default(struct vc_data *vc, + struct console_font *font, char *name) +{ + return 0; +} + +static int sisusbdummycon_font_copy(struct vc_data *vc, int con) +{ + return 0; +} static const struct consw sisusb_dummy_con = { .owner = THIS_MODULE, .con_startup = sisusbdummycon_startup, .con_init = sisusbdummycon_init, - .con_deinit = SISUSBCONDUMMY, - .con_clear = SISUSBCONDUMMY, - .con_putc = SISUSBCONDUMMY, - .con_putcs = SISUSBCONDUMMY, - .con_cursor = SISUSBCONDUMMY, - .con_scroll = SISUSBCONDUMMY, - .con_switch = SISUSBCONDUMMY, - .con_blank = SISUSBCONDUMMY, - .con_font_set = SISUSBCONDUMMY, - .con_font_get = SISUSBCONDUMMY, - .con_font_default = SISUSBCONDUMMY, - .con_font_copy = SISUSBCONDUMMY, + .con_deinit = sisusbdummycon_deinit, + .con_clear = sisusbdummycon_clear, + .con_putc = sisusbdummycon_putc, + .con_putcs = sisusbdummycon_putcs, + .con_cursor = sisusbdummycon_cursor, + .con_scroll = sisusbdummycon_scroll, + .con_switch = sisusbdummycon_switch, + .con_blank = sisusbdummycon_blank, + .con_font_set = sisusbdummycon_font_set, + .con_font_default = sisusbdummycon_font_default, + .con_font_copy = sisusbdummycon_font_copy, }; int diff --git a/drivers/video/console/dummycon.c b/drivers/video/console/dummycon.c index b90ef96e43d6..f2eafe2ed980 100644 --- a/drivers/video/console/dummycon.c +++ b/drivers/video/console/dummycon.c @@ -41,12 +41,47 @@ static void dummycon_init(struct vc_data *vc, int init) vc_resize(vc, DUMMY_COLUMNS, DUMMY_ROWS); } -static int dummycon_dummy(void) +static void dummycon_deinit(struct vc_data *vc) { } +static void dummycon_clear(struct vc_data *vc, int sy, int sx, int height, + int width) { } +static void dummycon_putc(struct vc_data *vc, int c, int ypos, int xpos) { } +static void dummycon_putcs(struct vc_data *vc, const unsigned short *s, + int count, int ypos, int xpos) { } +static void dummycon_cursor(struct vc_data *vc, int mode) { } + +static bool dummycon_scroll(struct vc_data *vc, unsigned int top, + unsigned int bottom, enum con_scroll dir, + unsigned int lines) +{ + return false; +} + +static int dummycon_switch(struct vc_data *vc) { - return 0; + return 0; } -#define DUMMY (void *)dummycon_dummy +static int dummycon_blank(struct vc_data *vc, int blank, int mode_switch) +{ + return 0; +} + +static int dummycon_font_set(struct vc_data *vc, struct console_font *font, + unsigned int flags) +{ + return 0; +} + +static int dummycon_font_default(struct vc_data *vc, + struct console_font *font, char *name) +{ + return 0; +} + +static int dummycon_font_copy(struct vc_data *vc, int con) +{ + return 0; +} /* * The console `switch' structure for the dummy console @@ -55,19 +90,19 @@ static int dummycon_dummy(void) */ const struct consw dummy_con = { - .owner = THIS_MODULE, - .con_startup = dummycon_startup, - .con_init = dummycon_init, - .con_deinit = DUMMY, - .con_clear = DUMMY, - .con_putc = DUMMY, - .con_putcs = DUMMY, - .con_cursor = DUMMY, - .con_scroll = DUMMY, - .con_switch = DUMMY, - .con_blank = DUMMY, - .con_font_set = DUMMY, - .con_font_default = DUMMY, - .con_font_copy = DUMMY, + .owner = THIS_MODULE, + .con_startup = dummycon_startup, + .con_init = dummycon_init, + .con_deinit = dummycon_deinit, + .con_clear = dummycon_clear, + .con_putc = dummycon_putc, + .con_putcs = dummycon_putcs, + .con_cursor = dummycon_cursor, + .con_scroll = dummycon_scroll, + .con_switch = dummycon_switch, + .con_blank = dummycon_blank, + .con_font_set = dummycon_font_set, + .con_font_default = dummycon_font_default, + .con_font_copy = dummycon_font_copy, }; EXPORT_SYMBOL_GPL(dummy_con); diff --git a/drivers/video/console/newport_con.c b/drivers/video/console/newport_con.c index 42d02a206059..7f2526b43b33 100644 --- a/drivers/video/console/newport_con.c +++ b/drivers/video/console/newport_con.c @@ -673,12 +673,12 @@ static bool newport_scroll(struct vc_data *vc, unsigned int t, unsigned int b, return true; } -static int newport_dummy(struct vc_data *c) +static int newport_set_origin(struct vc_data *vc) { return 0; } -#define DUMMY (void *) newport_dummy +static void newport_save_screen(struct vc_data *vc) { } const struct consw newport_con = { .owner = THIS_MODULE, @@ -694,8 +694,8 @@ const struct consw newport_con = { .con_blank = newport_blank, .con_font_set = newport_font_set, .con_font_default = newport_font_default, - .con_set_origin = DUMMY, - .con_save_screen = DUMMY + .con_set_origin = newport_set_origin, + .con_save_screen = newport_save_screen }; static int newport_probe(struct gio_device *dev, diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c index a17ba1465815..f09e17b60e45 100644 --- a/drivers/video/console/vgacon.c +++ b/drivers/video/console/vgacon.c @@ -1272,7 +1272,8 @@ static int vgacon_adjust_height(struct vc_data *vc, unsigned fontheight) return 0; } -static int vgacon_font_set(struct vc_data *c, struct console_font *font, unsigned flags) +static int vgacon_font_set(struct vc_data *c, struct console_font *font, + unsigned int flags) { unsigned charcount = font->charcount; int rc; @@ -1407,21 +1408,20 @@ static bool vgacon_scroll(struct vc_data *c, unsigned int t, unsigned int b, * The console `switch' structure for the VGA based console */ -static int vgacon_dummy(struct vc_data *c) -{ - return 0; -} - -#define DUMMY (void *) vgacon_dummy +static void vgacon_clear(struct vc_data *vc, int sy, int sx, int height, + int width) { } +static void vgacon_putc(struct vc_data *vc, int c, int ypos, int xpos) { } +static void vgacon_putcs(struct vc_data *vc, const unsigned short *s, + int count, int ypos, int xpos) { } const struct consw vga_con = { .owner = THIS_MODULE, .con_startup = vgacon_startup, .con_init = vgacon_init, .con_deinit = vgacon_deinit, - .con_clear = DUMMY, - .con_putc = DUMMY, - .con_putcs = DUMMY, + .con_clear = vgacon_clear, + .con_putc = vgacon_putc, + .con_putcs = vgacon_putcs, .con_cursor = vgacon_cursor, .con_scroll = vgacon_scroll, .con_switch = vgacon_switch, diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c index 5baf7bc054e1..3e330e0f56ed 100644 --- a/drivers/video/fbdev/core/fbcon.c +++ b/drivers/video/fbdev/core/fbcon.c @@ -2595,7 +2595,8 @@ static int fbcon_copy_font(struct vc_data *vc, int con) * is ever implemented. */ -static int fbcon_set_font(struct vc_data *vc, struct console_font *font, unsigned flags) +static int fbcon_set_font(struct vc_data *vc, struct console_font *font, + unsigned int flags) { struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; unsigned charcount = font->charcount; diff --git a/fs/devpts/inode.c b/fs/devpts/inode.c index e31d6ed3ec32..e072e955ce33 100644 --- a/fs/devpts/inode.c +++ b/fs/devpts/inode.c @@ -138,10 +138,6 @@ static int devpts_ptmx_path(struct path *path) struct super_block *sb; int err; - /* Has the devpts filesystem already been found? */ - if (path->mnt->mnt_sb->s_magic == DEVPTS_SUPER_MAGIC) - return 0; - /* Is a devpts filesystem at "pts" in the same directory? */ err = path_pts(path); if (err) @@ -156,25 +152,53 @@ static int devpts_ptmx_path(struct path *path) return 0; } +/* + * Try to find a suitable devpts filesystem. We support the following + * scenarios: + * - The ptmx device node is located in the same directory as the devpts + * mount where the pts device nodes are located. + * This is e.g. the case when calling open on the /dev/pts/ptmx device + * node when the devpts filesystem is mounted at /dev/pts. + * - The ptmx device node is located outside the devpts filesystem mount + * where the pts device nodes are located. For example, the ptmx device + * is a symlink, separate device node, or bind-mount. + * A supported scenario is bind-mounting /dev/pts/ptmx to /dev/ptmx and + * then calling open on /dev/ptmx. In this case a suitable pts + * subdirectory can be found in the common parent directory /dev of the + * devpts mount and the ptmx bind-mount, after resolving the /dev/ptmx + * bind-mount. + * If no suitable pts subdirectory can be found this function will fail. + * This is e.g. the case when bind-mounting /dev/pts/ptmx to /ptmx. + */ struct vfsmount *devpts_mntget(struct file *filp, struct pts_fs_info *fsi) { struct path path; - int err; + int err = 0; path = filp->f_path; path_get(&path); - err = devpts_ptmx_path(&path); + /* Walk upward while the start point is a bind mount of + * a single file. + */ + while (path.mnt->mnt_root == path.dentry) + if (follow_up(&path) == 0) + break; + + /* devpts_ptmx_path() finds a devpts fs or returns an error. */ + if ((path.mnt->mnt_sb->s_magic != DEVPTS_SUPER_MAGIC) || + (DEVPTS_SB(path.mnt->mnt_sb) != fsi)) + err = devpts_ptmx_path(&path); dput(path.dentry); - if (err) { - mntput(path.mnt); - return ERR_PTR(err); - } - if (DEVPTS_SB(path.mnt->mnt_sb) != fsi) { - mntput(path.mnt); - return ERR_PTR(-ENODEV); + if (!err) { + if (DEVPTS_SB(path.mnt->mnt_sb) == fsi) + return path.mnt; + + err = -ENODEV; } - return path.mnt; + + mntput(path.mnt); + return ERR_PTR(err); } struct pts_fs_info *devpts_acquire(struct file *filp) @@ -182,15 +206,19 @@ struct pts_fs_info *devpts_acquire(struct file *filp) struct pts_fs_info *result; struct path path; struct super_block *sb; - int err; path = filp->f_path; path_get(&path); - err = devpts_ptmx_path(&path); - if (err) { - result = ERR_PTR(err); - goto out; + /* Has the devpts filesystem already been found? */ + if (path.mnt->mnt_sb->s_magic != DEVPTS_SUPER_MAGIC) { + int err; + + err = devpts_ptmx_path(&path); + if (err) { + result = ERR_PTR(err); + goto out; + } } /* diff --git a/include/linux/console.h b/include/linux/console.h index b8920a031a3e..dfd6b0e97855 100644 --- a/include/linux/console.h +++ b/include/linux/console.h @@ -46,46 +46,52 @@ enum con_scroll { struct consw { struct module *owner; const char *(*con_startup)(void); - void (*con_init)(struct vc_data *, int); - void (*con_deinit)(struct vc_data *); - void (*con_clear)(struct vc_data *, int, int, int, int); - void (*con_putc)(struct vc_data *, int, int, int); - void (*con_putcs)(struct vc_data *, const unsigned short *, int, int, int); - void (*con_cursor)(struct vc_data *, int); - bool (*con_scroll)(struct vc_data *, unsigned int top, + void (*con_init)(struct vc_data *vc, int init); + void (*con_deinit)(struct vc_data *vc); + void (*con_clear)(struct vc_data *vc, int sy, int sx, int height, + int width); + void (*con_putc)(struct vc_data *vc, int c, int ypos, int xpos); + void (*con_putcs)(struct vc_data *vc, const unsigned short *s, + int count, int ypos, int xpos); + void (*con_cursor)(struct vc_data *vc, int mode); + bool (*con_scroll)(struct vc_data *vc, unsigned int top, unsigned int bottom, enum con_scroll dir, unsigned int lines); - int (*con_switch)(struct vc_data *); - int (*con_blank)(struct vc_data *, int, int); - int (*con_font_set)(struct vc_data *, struct console_font *, unsigned); - int (*con_font_get)(struct vc_data *, struct console_font *); - int (*con_font_default)(struct vc_data *, struct console_font *, char *); - int (*con_font_copy)(struct vc_data *, int); - int (*con_resize)(struct vc_data *, unsigned int, unsigned int, - unsigned int); - void (*con_set_palette)(struct vc_data *, + int (*con_switch)(struct vc_data *vc); + int (*con_blank)(struct vc_data *vc, int blank, int mode_switch); + int (*con_font_set)(struct vc_data *vc, struct console_font *font, + unsigned int flags); + int (*con_font_get)(struct vc_data *vc, struct console_font *font); + int (*con_font_default)(struct vc_data *vc, + struct console_font *font, char *name); + int (*con_font_copy)(struct vc_data *vc, int con); + int (*con_resize)(struct vc_data *vc, unsigned int width, + unsigned int height, unsigned int user); + void (*con_set_palette)(struct vc_data *vc, const unsigned char *table); - void (*con_scrolldelta)(struct vc_data *, int lines); - int (*con_set_origin)(struct vc_data *); - void (*con_save_screen)(struct vc_data *); - u8 (*con_build_attr)(struct vc_data *, u8, u8, u8, u8, u8, u8); - void (*con_invert_region)(struct vc_data *, u16 *, int); - u16 *(*con_screen_pos)(struct vc_data *, int); - unsigned long (*con_getxy)(struct vc_data *, unsigned long, int *, int *); + void (*con_scrolldelta)(struct vc_data *vc, int lines); + int (*con_set_origin)(struct vc_data *vc); + void (*con_save_screen)(struct vc_data *vc); + u8 (*con_build_attr)(struct vc_data *vc, u8 color, u8 intensity, + u8 blink, u8 underline, u8 reverse, u8 italic); + void (*con_invert_region)(struct vc_data *vc, u16 *p, int count); + u16 *(*con_screen_pos)(struct vc_data *vc, int offset); + unsigned long (*con_getxy)(struct vc_data *vc, unsigned long position, + int *px, int *py); /* * Flush the video console driver's scrollback buffer */ - void (*con_flush_scrollback)(struct vc_data *); + void (*con_flush_scrollback)(struct vc_data *vc); /* * Prepare the console for the debugger. This includes, but is not * limited to, unblanking the console, loading an appropriate * palette, and allowing debugger generated output. */ - int (*con_debug_enter)(struct vc_data *); + int (*con_debug_enter)(struct vc_data *vc); /* * Restore the console to its pre-debug state as closely as possible. */ - int (*con_debug_leave)(struct vc_data *); + int (*con_debug_leave)(struct vc_data *vc); }; extern const struct consw *conswitchp; diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index a637a7d8ce5b..44f74b54d483 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2557,6 +2557,9 @@ #define PCI_DEVICE_ID_TEHUTI_3010 0x3010 #define PCI_DEVICE_ID_TEHUTI_3014 0x3014 +#define PCI_VENDOR_ID_SUNIX 0x1fd4 +#define PCI_DEVICE_ID_SUNIX_1999 0x1999 + #define PCI_VENDOR_ID_HINT 0x3388 #define PCI_DEVICE_ID_HINT_VXPROII_IDE 0x8013 diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index b32df49a3bd5..1d356105f25a 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -379,7 +379,7 @@ extern int of_setup_earlycon(const struct earlycon_id *match, extern bool earlycon_acpi_spcr_enable __initdata; int setup_earlycon(char *buf); #else -static const bool earlycon_acpi_spcr_enable; +static const bool earlycon_acpi_spcr_enable EARLYCON_USED_OR_UNUSED; static inline int setup_earlycon(char *buf) { return 0; } #endif diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h index 1c8413f93e3d..dce5f9dae121 100644 --- a/include/uapi/linux/serial_core.h +++ b/include/uapi/linux/serial_core.h @@ -76,6 +76,9 @@ #define PORT_SUNZILOG 38 #define PORT_SUNSAB 39 +/* Nuvoton UART */ +#define PORT_NPCM 40 + /* Intel EG20 */ #define PORT_PCH_8LINE 44 #define PORT_PCH_2LINE 45 diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 7442dfb73b7f..dbda89c9d9b9 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -7,6 +7,7 @@ TARGETS += cpufreq TARGETS += cpu-hotplug TARGETS += efivarfs TARGETS += exec +TARGETS += filesystems TARGETS += firmware TARGETS += ftrace TARGETS += futex diff --git a/tools/testing/selftests/filesystems/.gitignore b/tools/testing/selftests/filesystems/.gitignore index 31d6e426b6d4..8449cf6716ce 100644 --- a/tools/testing/selftests/filesystems/.gitignore +++ b/tools/testing/selftests/filesystems/.gitignore @@ -1 +1,2 @@ dnotify_test +devpts_pts diff --git a/tools/testing/selftests/filesystems/Makefile b/tools/testing/selftests/filesystems/Makefile index 13a73bf725b5..4e6d09fb166f 100644 --- a/tools/testing/selftests/filesystems/Makefile +++ b/tools/testing/selftests/filesystems/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 -TEST_PROGS := dnotify_test +TEST_PROGS := dnotify_test devpts_pts all: $(TEST_PROGS) include ../lib.mk diff --git a/tools/testing/selftests/filesystems/devpts_pts.c b/tools/testing/selftests/filesystems/devpts_pts.c new file mode 100644 index 000000000000..b9055e974289 --- /dev/null +++ b/tools/testing/selftests/filesystems/devpts_pts.c @@ -0,0 +1,313 @@ +// SPDX-License-Identifier: GPL-2.0 +#define _GNU_SOURCE +#include <errno.h> +#include <fcntl.h> +#include <sched.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <sys/mount.h> +#include <sys/wait.h> + +static bool terminal_dup2(int duplicate, int original) +{ + int ret; + + ret = dup2(duplicate, original); + if (ret < 0) + return false; + + return true; +} + +static int terminal_set_stdfds(int fd) +{ + int i; + + if (fd < 0) + return 0; + + for (i = 0; i < 3; i++) + if (!terminal_dup2(fd, (int[]){STDIN_FILENO, STDOUT_FILENO, + STDERR_FILENO}[i])) + return -1; + + return 0; +} + +static int login_pty(int fd) +{ + int ret; + + setsid(); + + ret = ioctl(fd, TIOCSCTTY, NULL); + if (ret < 0) + return -1; + + ret = terminal_set_stdfds(fd); + if (ret < 0) + return -1; + + if (fd > STDERR_FILENO) + close(fd); + + return 0; +} + +static int wait_for_pid(pid_t pid) +{ + int status, ret; + +again: + ret = waitpid(pid, &status, 0); + if (ret == -1) { + if (errno == EINTR) + goto again; + return -1; + } + if (ret != pid) + goto again; + + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) + return -1; + + return 0; +} + +static int resolve_procfd_symlink(int fd, char *buf, size_t buflen) +{ + int ret; + char procfd[4096]; + + ret = snprintf(procfd, 4096, "/proc/self/fd/%d", fd); + if (ret < 0 || ret >= 4096) + return -1; + + ret = readlink(procfd, buf, buflen); + if (ret < 0 || (size_t)ret >= buflen) + return -1; + + buf[ret] = '\0'; + + return 0; +} + +static int do_tiocgptpeer(char *ptmx, char *expected_procfd_contents) +{ + int ret; + int master = -1, slave = -1, fret = -1; + + master = open(ptmx, O_RDWR | O_NOCTTY | O_CLOEXEC); + if (master < 0) { + fprintf(stderr, "Failed to open \"%s\": %s\n", ptmx, + strerror(errno)); + return -1; + } + + /* + * grantpt() makes assumptions about /dev/pts/ so ignore it. It's also + * not really needed. + */ + ret = unlockpt(master); + if (ret < 0) { + fprintf(stderr, "Failed to unlock terminal\n"); + goto do_cleanup; + } + +#ifdef TIOCGPTPEER + slave = ioctl(master, TIOCGPTPEER, O_RDWR | O_NOCTTY | O_CLOEXEC); +#endif + if (slave < 0) { + if (errno == EINVAL) { + fprintf(stderr, "TIOCGPTPEER is not supported. " + "Skipping test.\n"); + fret = EXIT_SUCCESS; + } + + fprintf(stderr, "Failed to perform TIOCGPTPEER ioctl\n"); + goto do_cleanup; + } + + pid_t pid = fork(); + if (pid < 0) + goto do_cleanup; + + if (pid == 0) { + char buf[4096]; + + ret = login_pty(slave); + if (ret < 0) { + fprintf(stderr, "Failed to setup terminal\n"); + _exit(EXIT_FAILURE); + } + + ret = resolve_procfd_symlink(STDIN_FILENO, buf, sizeof(buf)); + if (ret < 0) { + fprintf(stderr, "Failed to retrieve pathname of pts " + "slave file descriptor\n"); + _exit(EXIT_FAILURE); + } + + if (strncmp(expected_procfd_contents, buf, + strlen(expected_procfd_contents)) != 0) { + fprintf(stderr, "Received invalid contents for " + "\"/proc/<pid>/fd/%d\" symlink: %s\n", + STDIN_FILENO, buf); + _exit(-1); + } + + fprintf(stderr, "Contents of \"/proc/<pid>/fd/%d\" " + "symlink are valid: %s\n", STDIN_FILENO, buf); + + _exit(EXIT_SUCCESS); + } + + ret = wait_for_pid(pid); + if (ret < 0) + goto do_cleanup; + + fret = EXIT_SUCCESS; + +do_cleanup: + if (master >= 0) + close(master); + if (slave >= 0) + close(slave); + + return fret; +} + +static int verify_non_standard_devpts_mount(void) +{ + char *mntpoint; + int ret = -1; + char devpts[] = P_tmpdir "/devpts_fs_XXXXXX"; + char ptmx[] = P_tmpdir "/devpts_fs_XXXXXX/ptmx"; + + ret = umount("/dev/pts"); + if (ret < 0) { + fprintf(stderr, "Failed to unmount \"/dev/pts\": %s\n", + strerror(errno)); + return -1; + } + + (void)umount("/dev/ptmx"); + + mntpoint = mkdtemp(devpts); + if (!mntpoint) { + fprintf(stderr, "Failed to create temporary mountpoint: %s\n", + strerror(errno)); + return -1; + } + + ret = mount("devpts", mntpoint, "devpts", MS_NOSUID | MS_NOEXEC, + "newinstance,ptmxmode=0666,mode=0620,gid=5"); + if (ret < 0) { + fprintf(stderr, "Failed to mount devpts fs to \"%s\" in new " + "mount namespace: %s\n", mntpoint, + strerror(errno)); + unlink(mntpoint); + return -1; + } + + ret = snprintf(ptmx, sizeof(ptmx), "%s/ptmx", devpts); + if (ret < 0 || (size_t)ret >= sizeof(ptmx)) { + unlink(mntpoint); + return -1; + } + + ret = do_tiocgptpeer(ptmx, mntpoint); + unlink(mntpoint); + if (ret < 0) + return -1; + + return 0; +} + +static int verify_ptmx_bind_mount(void) +{ + int ret; + + ret = mount("/dev/pts/ptmx", "/dev/ptmx", NULL, MS_BIND, NULL); + if (ret < 0) { + fprintf(stderr, "Failed to bind mount \"/dev/pts/ptmx\" to " + "\"/dev/ptmx\" mount namespace\n"); + return -1; + } + + ret = do_tiocgptpeer("/dev/ptmx", "/dev/pts/"); + if (ret < 0) + return -1; + + return 0; +} + +static int verify_invalid_ptmx_bind_mount(void) +{ + int ret; + char mntpoint_fd; + char ptmx[] = P_tmpdir "/devpts_ptmx_XXXXXX"; + + mntpoint_fd = mkstemp(ptmx); + if (mntpoint_fd < 0) { + fprintf(stderr, "Failed to create temporary directory: %s\n", + strerror(errno)); + return -1; + } + + ret = mount("/dev/pts/ptmx", ptmx, NULL, MS_BIND, NULL); + close(mntpoint_fd); + if (ret < 0) { + fprintf(stderr, "Failed to bind mount \"/dev/pts/ptmx\" to " + "\"%s\" mount namespace\n", ptmx); + return -1; + } + + ret = do_tiocgptpeer(ptmx, "/dev/pts/"); + if (ret == 0) + return -1; + + return 0; +} + +int main(int argc, char *argv[]) +{ + int ret; + + if (!isatty(STDIN_FILENO)) { + fprintf(stderr, "Standard input file desciptor is not attached " + "to a terminal. Skipping test\n"); + exit(EXIT_FAILURE); + } + + ret = unshare(CLONE_NEWNS); + if (ret < 0) { + fprintf(stderr, "Failed to unshare mount namespace\n"); + exit(EXIT_FAILURE); + } + + ret = mount("", "/", NULL, MS_PRIVATE | MS_REC, 0); + if (ret < 0) { + fprintf(stderr, "Failed to make \"/\" MS_PRIVATE in new mount " + "namespace\n"); + exit(EXIT_FAILURE); + } + + ret = verify_ptmx_bind_mount(); + if (ret < 0) + exit(EXIT_FAILURE); + + ret = verify_invalid_ptmx_bind_mount(); + if (ret < 0) + exit(EXIT_FAILURE); + + ret = verify_non_standard_devpts_mount(); + if (ret < 0) + exit(EXIT_FAILURE); + + exit(EXIT_SUCCESS); +} |