diff options
Diffstat (limited to 'drivers/tty/serial')
27 files changed, 1181 insertions, 380 deletions
diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h index ce8d4ffcc425..b2bdc35f7495 100644 --- a/drivers/tty/serial/8250/8250.h +++ b/drivers/tty/serial/8250/8250.h @@ -81,6 +81,9 @@ struct serial8250_config { #define UART_CAP_HFIFO (1 << 14) /* UART has a "hidden" FIFO */ #define UART_CAP_RPM (1 << 15) /* Runtime PM is active while idle */ #define UART_CAP_IRDA (1 << 16) /* UART supports IrDA line discipline */ +#define UART_CAP_MINI (1 << 17) /* Mini UART on BCM283X family lacks: + * STOP PARITY EPAR SPAR WLEN5 WLEN6 + */ #define UART_BUG_QUOT (1 << 0) /* UART has buggy quot LSB */ #define UART_BUG_TXEN (1 << 1) /* UART has buggy TX IIR status */ diff --git a/drivers/tty/serial/8250/8250_aspeed_vuart.c b/drivers/tty/serial/8250/8250_aspeed_vuart.c new file mode 100644 index 000000000000..822be4906763 --- /dev/null +++ b/drivers/tty/serial/8250/8250_aspeed_vuart.c @@ -0,0 +1,323 @@ +/* + * Serial Port driver for Aspeed VUART device + * + * Copyright (C) 2016 Jeremy Kerr <jk@ozlabs.org>, IBM Corp. + * Copyright (C) 2006 Arnd Bergmann <arnd@arndb.de>, IBM Corp. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include <linux/device.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/of_platform.h> +#include <linux/clk.h> + +#include "8250.h" + +#define ASPEED_VUART_GCRA 0x20 +#define ASPEED_VUART_GCRA_VUART_EN BIT(0) +#define ASPEED_VUART_GCRA_DISABLE_HOST_TX_DISCARD BIT(5) +#define ASPEED_VUART_GCRB 0x24 +#define ASPEED_VUART_GCRB_HOST_SIRQ_MASK GENMASK(7, 4) +#define ASPEED_VUART_GCRB_HOST_SIRQ_SHIFT 4 +#define ASPEED_VUART_ADDRL 0x28 +#define ASPEED_VUART_ADDRH 0x2c + +struct aspeed_vuart { + struct device *dev; + void __iomem *regs; + struct clk *clk; + int line; +}; + +/* + * The VUART is basically two UART 'front ends' connected by their FIFO + * (no actual serial line in between). One is on the BMC side (management + * controller) and one is on the host CPU side. + * + * It allows the BMC to provide to the host a "UART" that pipes into + * the BMC itself and can then be turned by the BMC into a network console + * of some sort for example. + * + * This driver is for the BMC side. The sysfs files allow the BMC + * userspace which owns the system configuration policy, to specify + * at what IO port and interrupt number the host side will appear + * to the host on the Host <-> BMC LPC bus. It could be different on a + * different system (though most of them use 3f8/4). + */ + +static ssize_t lpc_address_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aspeed_vuart *vuart = dev_get_drvdata(dev); + u16 addr; + + addr = (readb(vuart->regs + ASPEED_VUART_ADDRH) << 8) | + (readb(vuart->regs + ASPEED_VUART_ADDRL)); + + return snprintf(buf, PAGE_SIZE - 1, "0x%x\n", addr); +} + +static ssize_t lpc_address_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct aspeed_vuart *vuart = dev_get_drvdata(dev); + unsigned long val; + int err; + + err = kstrtoul(buf, 0, &val); + if (err) + return err; + + writeb(val >> 8, vuart->regs + ASPEED_VUART_ADDRH); + writeb(val >> 0, vuart->regs + ASPEED_VUART_ADDRL); + + return count; +} + +static DEVICE_ATTR_RW(lpc_address); + +static ssize_t sirq_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aspeed_vuart *vuart = dev_get_drvdata(dev); + u8 reg; + + reg = readb(vuart->regs + ASPEED_VUART_GCRB); + reg &= ASPEED_VUART_GCRB_HOST_SIRQ_MASK; + reg >>= ASPEED_VUART_GCRB_HOST_SIRQ_SHIFT; + + return snprintf(buf, PAGE_SIZE - 1, "%u\n", reg); +} + +static ssize_t sirq_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct aspeed_vuart *vuart = dev_get_drvdata(dev); + unsigned long val; + int err; + u8 reg; + + err = kstrtoul(buf, 0, &val); + if (err) + return err; + + val <<= ASPEED_VUART_GCRB_HOST_SIRQ_SHIFT; + val &= ASPEED_VUART_GCRB_HOST_SIRQ_MASK; + + reg = readb(vuart->regs + ASPEED_VUART_GCRB); + reg &= ~ASPEED_VUART_GCRB_HOST_SIRQ_MASK; + reg |= val; + writeb(reg, vuart->regs + ASPEED_VUART_GCRB); + + return count; +} + +static DEVICE_ATTR_RW(sirq); + +static struct attribute *aspeed_vuart_attrs[] = { + &dev_attr_sirq.attr, + &dev_attr_lpc_address.attr, + NULL, +}; + +static const struct attribute_group aspeed_vuart_attr_group = { + .attrs = aspeed_vuart_attrs, +}; + +static void aspeed_vuart_set_enabled(struct aspeed_vuart *vuart, bool enabled) +{ + u8 reg = readb(vuart->regs + ASPEED_VUART_GCRA); + + if (enabled) + reg |= ASPEED_VUART_GCRA_VUART_EN; + else + reg &= ~ASPEED_VUART_GCRA_VUART_EN; + + writeb(reg, vuart->regs + ASPEED_VUART_GCRA); +} + +static void aspeed_vuart_set_host_tx_discard(struct aspeed_vuart *vuart, + bool discard) +{ + u8 reg; + + reg = readb(vuart->regs + ASPEED_VUART_GCRA); + + /* If the DISABLE_HOST_TX_DISCARD bit is set, discard is disabled */ + if (!discard) + reg |= ASPEED_VUART_GCRA_DISABLE_HOST_TX_DISCARD; + else + reg &= ~ASPEED_VUART_GCRA_DISABLE_HOST_TX_DISCARD; + + writeb(reg, vuart->regs + ASPEED_VUART_GCRA); +} + +static int aspeed_vuart_startup(struct uart_port *uart_port) +{ + struct uart_8250_port *uart_8250_port = up_to_u8250p(uart_port); + struct aspeed_vuart *vuart = uart_8250_port->port.private_data; + int rc; + + rc = serial8250_do_startup(uart_port); + if (rc) + return rc; + + aspeed_vuart_set_host_tx_discard(vuart, false); + + return 0; +} + +static void aspeed_vuart_shutdown(struct uart_port *uart_port) +{ + struct uart_8250_port *uart_8250_port = up_to_u8250p(uart_port); + struct aspeed_vuart *vuart = uart_8250_port->port.private_data; + + aspeed_vuart_set_host_tx_discard(vuart, true); + + serial8250_do_shutdown(uart_port); +} + +static int aspeed_vuart_probe(struct platform_device *pdev) +{ + struct uart_8250_port port; + struct aspeed_vuart *vuart; + struct device_node *np; + struct resource *res; + u32 clk, prop; + int rc; + + np = pdev->dev.of_node; + + vuart = devm_kzalloc(&pdev->dev, sizeof(*vuart), GFP_KERNEL); + if (!vuart) + return -ENOMEM; + + vuart->dev = &pdev->dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + vuart->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(vuart->regs)) + return PTR_ERR(vuart->regs); + + memset(&port, 0, sizeof(port)); + port.port.private_data = vuart; + port.port.membase = vuart->regs; + port.port.mapbase = res->start; + port.port.mapsize = resource_size(res); + port.port.startup = aspeed_vuart_startup; + port.port.shutdown = aspeed_vuart_shutdown; + port.port.dev = &pdev->dev; + + rc = sysfs_create_group(&vuart->dev->kobj, &aspeed_vuart_attr_group); + if (rc < 0) + return rc; + + if (of_property_read_u32(np, "clock-frequency", &clk)) { + vuart->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(vuart->clk)) { + dev_warn(&pdev->dev, + "clk or clock-frequency not defined\n"); + return PTR_ERR(vuart->clk); + } + + rc = clk_prepare_enable(vuart->clk); + if (rc < 0) + return rc; + + clk = clk_get_rate(vuart->clk); + } + + /* If current-speed was set, then try not to change it. */ + if (of_property_read_u32(np, "current-speed", &prop) == 0) + port.port.custom_divisor = clk / (16 * prop); + + /* Check for shifted address mapping */ + if (of_property_read_u32(np, "reg-offset", &prop) == 0) + port.port.mapbase += prop; + + /* Check for registers offset within the devices address range */ + if (of_property_read_u32(np, "reg-shift", &prop) == 0) + port.port.regshift = prop; + + /* Check for fifo size */ + if (of_property_read_u32(np, "fifo-size", &prop) == 0) + port.port.fifosize = prop; + + /* Check for a fixed line number */ + rc = of_alias_get_id(np, "serial"); + if (rc >= 0) + port.port.line = rc; + + port.port.irq = irq_of_parse_and_map(np, 0); + port.port.irqflags = IRQF_SHARED; + port.port.iotype = UPIO_MEM; + port.port.type = PORT_16550A; + port.port.uartclk = clk; + port.port.flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF + | UPF_FIXED_PORT | UPF_FIXED_TYPE | UPF_NO_THRE_TEST; + + if (of_property_read_bool(np, "no-loopback-test")) + port.port.flags |= UPF_SKIP_TEST; + + if (port.port.fifosize) + port.capabilities = UART_CAP_FIFO; + + if (of_property_read_bool(np, "auto-flow-control")) + port.capabilities |= UART_CAP_AFE; + + rc = serial8250_register_8250_port(&port); + if (rc < 0) + goto err_clk_disable; + + vuart->line = rc; + + aspeed_vuart_set_enabled(vuart, true); + aspeed_vuart_set_host_tx_discard(vuart, true); + platform_set_drvdata(pdev, vuart); + + return 0; + +err_clk_disable: + clk_disable_unprepare(vuart->clk); + irq_dispose_mapping(port.port.irq); + return rc; +} + +static int aspeed_vuart_remove(struct platform_device *pdev) +{ + struct aspeed_vuart *vuart = platform_get_drvdata(pdev); + + aspeed_vuart_set_enabled(vuart, false); + serial8250_unregister_port(vuart->line); + sysfs_remove_group(&vuart->dev->kobj, &aspeed_vuart_attr_group); + clk_disable_unprepare(vuart->clk); + + return 0; +} + +static const struct of_device_id aspeed_vuart_table[] = { + { .compatible = "aspeed,ast2400-vuart" }, + { .compatible = "aspeed,ast2500-vuart" }, + { }, +}; + +static struct platform_driver aspeed_vuart_driver = { + .driver = { + .name = "aspeed-vuart", + .of_match_table = aspeed_vuart_table, + }, + .probe = aspeed_vuart_probe, + .remove = aspeed_vuart_remove, +}; + +module_platform_driver(aspeed_vuart_driver); + +MODULE_AUTHOR("Jeremy Kerr <jk@ozlabs.org>"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Driver for Aspeed VUART device"); diff --git a/drivers/tty/serial/8250/8250_bcm2835aux.c b/drivers/tty/serial/8250/8250_bcm2835aux.c index e10f1244409b..a23c7da42ea8 100644 --- a/drivers/tty/serial/8250/8250_bcm2835aux.c +++ b/drivers/tty/serial/8250/8250_bcm2835aux.c @@ -39,7 +39,7 @@ static int bcm2835aux_serial_probe(struct platform_device *pdev) /* initialize data */ spin_lock_init(&data->uart.port.lock); - data->uart.capabilities = UART_CAP_FIFO; + data->uart.capabilities = UART_CAP_FIFO | UART_CAP_MINI; data->uart.port.dev = &pdev->dev; data->uart.port.regshift = 2; data->uart.port.type = PORT_16550; diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 1aab3010fbfa..b5def356af63 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -1043,24 +1043,13 @@ int serial8250_register_8250_port(struct uart_8250_port *up) if (up->dl_write) uart->dl_write = up->dl_write; - if (uart->port.type != PORT_8250_CIR) { - if (serial8250_isa_config != NULL) - serial8250_isa_config(0, &uart->port, - &uart->capabilities); - - ret = uart_add_one_port(&serial8250_reg, - &uart->port); - if (ret == 0) - ret = uart->port.line; - } else { - dev_info(uart->port.dev, - "skipping CIR port at 0x%lx / 0x%llx, IRQ %d\n", - uart->port.iobase, - (unsigned long long)uart->port.mapbase, - uart->port.irq); + if (serial8250_isa_config != NULL) + serial8250_isa_config(0, &uart->port, + &uart->capabilities); - ret = 0; - } + ret = uart_add_one_port(&serial8250_reg, &uart->port); + if (ret == 0) + ret = uart->port.line; } mutex_unlock(&serial_mutex); diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index 1270ff163f63..b5c98e5bf524 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -9,10 +9,13 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License. */ +#include <linux/acpi.h> +#include <linux/dmi.h> #include <linux/io.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> +#include <linux/property.h> #include <linux/serial_core.h> #include <linux/serial_reg.h> #include <linux/slab.h> @@ -60,8 +63,50 @@ #define UART_EXAR_MPIOSEL_15_8 0x99 /* MPIOSEL[15:8] */ #define UART_EXAR_MPIOOD_15_8 0x9a /* MPIOOD[15:8] */ +#define UART_EXAR_RS485_DLY(x) ((x) << 4) + +/* + * IOT2040 MPIO wiring semantics: + * + * MPIO Port Function + * ---- ---- -------- + * 0 2 Mode bit 0 + * 1 2 Mode bit 1 + * 2 2 Terminate bus + * 3 - <reserved> + * 4 3 Mode bit 0 + * 5 3 Mode bit 1 + * 6 3 Terminate bus + * 7 - <reserved> + * 8 2 Enable + * 9 3 Enable + * 10 - Red LED + * 11..15 - <unused> + */ + +/* IOT2040 MPIOs 0..7 */ +#define IOT2040_UART_MODE_RS232 0x01 +#define IOT2040_UART_MODE_RS485 0x02 +#define IOT2040_UART_MODE_RS422 0x03 +#define IOT2040_UART_TERMINATE_BUS 0x04 + +#define IOT2040_UART1_MASK 0x0f +#define IOT2040_UART2_SHIFT 4 + +#define IOT2040_UARTS_DEFAULT_MODE 0x11 /* both RS232 */ +#define IOT2040_UARTS_GPIO_LO_MODE 0x88 /* reserved pins as input */ + +/* IOT2040 MPIOs 8..15 */ +#define IOT2040_UARTS_ENABLE 0x03 +#define IOT2040_UARTS_GPIO_HI_MODE 0xF8 /* enable & LED as outputs */ + struct exar8250; +struct exar8250_platform { + int (*rs485_config)(struct uart_port *, struct serial_rs485 *); + int (*register_gpio)(struct pci_dev *, struct uart_8250_port *); +}; + /** * struct exar8250_board - board information * @num_ports: number of serial ports @@ -109,7 +154,6 @@ pci_fastcom335_setup(struct exar8250 *priv, struct pci_dev *pcidev, u8 __iomem *p; int err; - port->port.flags |= UPF_EXAR_EFR; port->port.uartclk = baud * 16; err = default_setup(priv, pcidev, idx, offset, port); @@ -171,24 +215,32 @@ pci_xr17c154_setup(struct exar8250 *priv, struct pci_dev *pcidev, return default_setup(priv, pcidev, idx, offset, port); } -static void setup_gpio(u8 __iomem *p) +static void setup_gpio(struct pci_dev *pcidev, u8 __iomem *p) { + /* + * The Commtech adapters required the MPIOs to be driven low. The Exar + * devices will export them as GPIOs, so we pre-configure them safely + * as inputs. + */ + u8 dir = pcidev->vendor == PCI_VENDOR_ID_EXAR ? 0xff : 0x00; + writeb(0x00, p + UART_EXAR_MPIOINT_7_0); writeb(0x00, p + UART_EXAR_MPIOLVL_7_0); writeb(0x00, p + UART_EXAR_MPIO3T_7_0); writeb(0x00, p + UART_EXAR_MPIOINV_7_0); - writeb(0x00, p + UART_EXAR_MPIOSEL_7_0); + writeb(dir, p + UART_EXAR_MPIOSEL_7_0); writeb(0x00, p + UART_EXAR_MPIOOD_7_0); writeb(0x00, p + UART_EXAR_MPIOINT_15_8); writeb(0x00, p + UART_EXAR_MPIOLVL_15_8); writeb(0x00, p + UART_EXAR_MPIO3T_15_8); writeb(0x00, p + UART_EXAR_MPIOINV_15_8); - writeb(0x00, p + UART_EXAR_MPIOSEL_15_8); + writeb(dir, p + UART_EXAR_MPIOSEL_15_8); writeb(0x00, p + UART_EXAR_MPIOOD_15_8); } static void * -xr17v35x_register_gpio(struct pci_dev *pcidev) +__xr17v35x_register_gpio(struct pci_dev *pcidev, + const struct property_entry *properties) { struct platform_device *pdev; @@ -196,8 +248,11 @@ xr17v35x_register_gpio(struct pci_dev *pcidev) if (!pdev) return NULL; - platform_set_drvdata(pdev, pcidev); - if (platform_device_add(pdev) < 0) { + pdev->dev.parent = &pcidev->dev; + ACPI_COMPANION_SET(&pdev->dev, ACPI_COMPANION(&pcidev->dev)); + + if (platform_device_add_properties(pdev, properties) < 0 || + platform_device_add(pdev) < 0) { platform_device_put(pdev); return NULL; } @@ -205,17 +260,131 @@ xr17v35x_register_gpio(struct pci_dev *pcidev) return pdev; } +static const struct property_entry exar_gpio_properties[] = { + PROPERTY_ENTRY_U32("linux,first-pin", 0), + PROPERTY_ENTRY_U32("ngpios", 16), + { } +}; + +static int xr17v35x_register_gpio(struct pci_dev *pcidev, + struct uart_8250_port *port) +{ + if (pcidev->vendor == PCI_VENDOR_ID_EXAR) + port->port.private_data = + __xr17v35x_register_gpio(pcidev, exar_gpio_properties); + + return 0; +} + +static const struct exar8250_platform exar8250_default_platform = { + .register_gpio = xr17v35x_register_gpio, +}; + +static int iot2040_rs485_config(struct uart_port *port, + struct serial_rs485 *rs485) +{ + bool is_rs485 = !!(rs485->flags & SER_RS485_ENABLED); + u8 __iomem *p = port->membase; + u8 mask = IOT2040_UART1_MASK; + u8 mode, value; + + if (is_rs485) { + if (rs485->flags & SER_RS485_RX_DURING_TX) + mode = IOT2040_UART_MODE_RS422; + else + mode = IOT2040_UART_MODE_RS485; + + if (rs485->flags & SER_RS485_TERMINATE_BUS) + mode |= IOT2040_UART_TERMINATE_BUS; + } else { + mode = IOT2040_UART_MODE_RS232; + } + + if (port->line == 3) { + mask <<= IOT2040_UART2_SHIFT; + mode <<= IOT2040_UART2_SHIFT; + } + + value = readb(p + UART_EXAR_MPIOLVL_7_0); + value &= ~mask; + value |= mode; + writeb(value, p + UART_EXAR_MPIOLVL_7_0); + + value = readb(p + UART_EXAR_FCTR); + if (is_rs485) + value |= UART_FCTR_EXAR_485; + else + value &= ~UART_FCTR_EXAR_485; + writeb(value, p + UART_EXAR_FCTR); + + if (is_rs485) + writeb(UART_EXAR_RS485_DLY(4), p + UART_MSR); + + port->rs485 = *rs485; + + return 0; +} + +static const struct property_entry iot2040_gpio_properties[] = { + PROPERTY_ENTRY_U32("linux,first-pin", 10), + PROPERTY_ENTRY_U32("ngpios", 1), + { } +}; + +static int iot2040_register_gpio(struct pci_dev *pcidev, + struct uart_8250_port *port) +{ + u8 __iomem *p = port->port.membase; + + writeb(IOT2040_UARTS_DEFAULT_MODE, p + UART_EXAR_MPIOLVL_7_0); + writeb(IOT2040_UARTS_GPIO_LO_MODE, p + UART_EXAR_MPIOSEL_7_0); + writeb(IOT2040_UARTS_ENABLE, p + UART_EXAR_MPIOLVL_15_8); + writeb(IOT2040_UARTS_GPIO_HI_MODE, p + UART_EXAR_MPIOSEL_15_8); + + port->port.private_data = + __xr17v35x_register_gpio(pcidev, iot2040_gpio_properties); + + return 0; +} + +static const struct exar8250_platform iot2040_platform = { + .rs485_config = iot2040_rs485_config, + .register_gpio = iot2040_register_gpio, +}; + +static const struct dmi_system_id exar_platforms[] = { + { + .matches = { + DMI_EXACT_MATCH(DMI_BOARD_NAME, "SIMATIC IOT2000"), + DMI_EXACT_MATCH(DMI_BOARD_ASSET_TAG, + "6ES7647-0AA00-1YA2"), + }, + .driver_data = (void *)&iot2040_platform, + }, + {} +}; + static int pci_xr17v35x_setup(struct exar8250 *priv, struct pci_dev *pcidev, struct uart_8250_port *port, int idx) { const struct exar8250_board *board = priv->board; + const struct exar8250_platform *platform; + const struct dmi_system_id *dmi_match; unsigned int offset = idx * 0x400; unsigned int baud = 7812500; u8 __iomem *p; int ret; + dmi_match = dmi_first_match(exar_platforms); + if (dmi_match) + platform = dmi_match->driver_data; + else + platform = &exar8250_default_platform; + port->port.uartclk = baud * 16; + port->port.rs485_config = platform->rs485_config; + /* * Setup the uart clock for the devices on expansion slot to * half the clock speed of the main chip (which is 125MHz) @@ -236,12 +405,12 @@ pci_xr17v35x_setup(struct exar8250 *priv, struct pci_dev *pcidev, if (idx == 0) { /* Setup Multipurpose Input/Output pins. */ - setup_gpio(p); + setup_gpio(pcidev, p); - port->port.private_data = xr17v35x_register_gpio(pcidev); + ret = platform->register_gpio(pcidev, port); } - return 0; + return ret; } static void pci_xr17v35x_exit(struct pci_dev *pcidev) diff --git a/drivers/tty/serial/8250/8250_of.c b/drivers/tty/serial/8250/8250_of.c index 1cbadafc6889..0cf95fddccfc 100644 --- a/drivers/tty/serial/8250/8250_of.c +++ b/drivers/tty/serial/8250/8250_of.c @@ -19,11 +19,13 @@ #include <linux/of_irq.h> #include <linux/of_platform.h> #include <linux/clk.h> +#include <linux/reset.h> #include "8250.h" struct of_serial_info { struct clk *clk; + struct reset_control *rst; int type; int line; }; @@ -132,6 +134,13 @@ static int of_platform_serial_setup(struct platform_device *ofdev, } } + info->rst = devm_reset_control_get_optional_shared(&ofdev->dev, NULL); + if (IS_ERR(info->rst)) + goto out; + ret = reset_control_deassert(info->rst); + if (ret) + goto out; + port->type = type; port->uartclk = clk; port->flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_IOREMAP @@ -229,6 +238,7 @@ static int of_platform_serial_remove(struct platform_device *ofdev) serial8250_unregister_port(info->line); + reset_control_assert(info->rst); if (info->clk) clk_disable_unprepare(info->clk); kfree(info); diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c index e7e64913a748..833771bca0a5 100644 --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -613,6 +613,10 @@ static int omap_8250_startup(struct uart_port *port) up->lsr_saved_flags = 0; up->msr_saved_flags = 0; + /* Disable DMA for console UART */ + if (uart_console(port)) + up->dma = NULL; + if (up->dma) { ret = serial8250_request_dma(up); if (ret) { @@ -782,8 +786,27 @@ unlock: static void __dma_rx_complete(void *param) { - __dma_rx_do_complete(param); - omap_8250_rx_dma(param); + struct uart_8250_port *p = param; + struct uart_8250_dma *dma = p->dma; + struct dma_tx_state state; + unsigned long flags; + + spin_lock_irqsave(&p->port.lock, flags); + + /* + * If the tx status is not DMA_COMPLETE, then this is a delayed + * completion callback. A previous RX timeout flush would have + * already pushed the data, so exit. + */ + if (dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state) != + DMA_COMPLETE) { + spin_unlock_irqrestore(&p->port.lock, flags); + return; + } + __dma_rx_do_complete(p); + omap_8250_rx_dma(p); + + spin_unlock_irqrestore(&p->port.lock, flags); } static void omap_8250_rx_dma_flush(struct uart_8250_port *p) diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index 68fd045a7025..a5fe0e66c607 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -1764,6 +1764,10 @@ void serial8250_tx_chars(struct uart_8250_port *up) if ((up->capabilities & UART_CAP_HFIFO) && (serial_in(up, UART_LSR) & BOTH_EMPTY) != BOTH_EMPTY) break; + /* The BCM2835 MINI UART THRE bit is really a not-full bit. */ + if ((up->capabilities & UART_CAP_MINI) && + !(serial_in(up, UART_LSR) & UART_LSR_THRE)) + break; } while (--count > 0); if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) @@ -2228,7 +2232,7 @@ int serial8250_do_startup(struct uart_port *port) } } - if (port->irq) { + if (port->irq && !(up->port.flags & UPF_NO_THRE_TEST)) { unsigned char iir1; /* * Test for UARTs that do not reassert THRE when the @@ -2585,6 +2589,12 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, unsigned long flags; unsigned int baud, quot, frac = 0; + if (up->capabilities & UART_CAP_MINI) { + termios->c_cflag &= ~(CSTOPB | PARENB | PARODD | CMSPAR); + if ((termios->c_cflag & CSIZE) == CS5 || + (termios->c_cflag & CSIZE) == CS6) + termios->c_cflag = (termios->c_cflag & ~CSIZE) | CS7; + } cval = serial8250_compute_lcr(up, termios->c_cflag); baud = serial8250_get_baud_rate(port, termios, old); diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig index 0e3f529d50e9..a1161ec0256f 100644 --- a/drivers/tty/serial/8250/Kconfig +++ b/drivers/tty/serial/8250/Kconfig @@ -224,6 +224,16 @@ config SERIAL_8250_ACCENT To compile this driver as a module, choose M here: the module will be called 8250_accent. +config SERIAL_8250_ASPEED_VUART + tristate "Aspeed Virtual UART" + depends on SERIAL_8250 + depends on OF + help + If you want to use the virtual UART (VUART) device on Aspeed + BMC platforms, enable this option. This enables the 16550A- + compatible device on the local LPC bus, giving a UART device + with no physical RS232 connections. + config SERIAL_8250_BOCA tristate "Support Boca cards" depends on SERIAL_8250 != n && ISA && SERIAL_8250_MANY_PORTS diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile index 2f30f9ecdb1b..a44a99a3e623 100644 --- a/drivers/tty/serial/8250/Makefile +++ b/drivers/tty/serial/8250/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_SERIAL_8250_EXAR) += 8250_exar.o obj-$(CONFIG_SERIAL_8250_HP300) += 8250_hp300.o obj-$(CONFIG_SERIAL_8250_CS) += serial_cs.o obj-$(CONFIG_SERIAL_8250_ACORN) += 8250_acorn.o +obj-$(CONFIG_SERIAL_8250_ASPEED_VUART) += 8250_aspeed_vuart.o obj-$(CONFIG_SERIAL_8250_BCM2835AUX) += 8250_bcm2835aux.o obj-$(CONFIG_SERIAL_8250_CONSOLE) += 8250_early.o obj-$(CONFIG_SERIAL_8250_FOURPORT) += 8250_fourport.o diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 5c8850f7a2a0..1f096e2bb398 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -114,32 +114,32 @@ config SERIAL_SB1250_DUART_CONSOLE If unsure, say Y. config SERIAL_ATMEL - bool "AT91 / AT32 on-chip serial port support" + bool "AT91 on-chip serial port support" depends on HAS_DMA - depends on ARCH_AT91 || AVR32 || COMPILE_TEST + depends on ARCH_AT91 || COMPILE_TEST select SERIAL_CORE select SERIAL_MCTRL_GPIO if GPIOLIB help This enables the driver for the on-chip UARTs of the Atmel - AT91 and AT32 processors. + AT91 processors. config SERIAL_ATMEL_CONSOLE - bool "Support for console on AT91 / AT32 serial port" + bool "Support for console on AT91 serial port" depends on SERIAL_ATMEL=y select SERIAL_CORE_CONSOLE help Say Y here if you wish to use an on-chip UART on a Atmel - AT91 or AT32 processor as the system console (the system + AT91 processor as the system console (the system console is the device which receives all kernel messages and warnings and which allows logins in single user mode). config SERIAL_ATMEL_PDC - bool "Support DMA transfers on AT91 / AT32 serial port" + bool "Support DMA transfers on AT91 serial port" depends on SERIAL_ATMEL default y help Say Y here if you wish to use the PDC to do DMA transfers to - and from the Atmel AT91 / AT32 serial port. In order to + and from the Atmel AT91 serial port. In order to actually use DMA transfers, make sure that the use_dma_tx and use_dma_rx members in the atmel_uart_data struct is set appropriately for each port. @@ -152,7 +152,7 @@ config SERIAL_ATMEL_TTYAT bool "Install as device ttyATn instead of ttySn" depends on SERIAL_ATMEL=y help - Say Y here if you wish to have the internal AT91 / AT32 UARTs + Say Y here if you wish to have the internal AT91 UARTs appear as /dev/ttyATn (major 204, minor starting at 154) instead of the normal /dev/ttySn (major 4, minor starting at 64). This is necessary if you also want other UARTs, such as @@ -1688,6 +1688,25 @@ config SERIAL_MVEBU_CONSOLE and warnings and which allows logins in single user mode) Otherwise, say 'N'. +config SERIAL_OWL + bool "Actions Semi Owl serial port support" + depends on ARCH_ACTIONS || COMPILE_TEST + select SERIAL_CORE + help + This driver is for Actions Semiconductor S500/S900 SoC's UART. + Say 'Y' here if you wish to use the on-board serial port. + Otherwise, say 'N'. + +config SERIAL_OWL_CONSOLE + bool "Console on Actions Semi Owl serial port" + depends on SERIAL_OWL=y + select SERIAL_CORE_CONSOLE + select SERIAL_EARLYCON + default y + help + Say 'Y' here if you wish to use Actions Semiconductor S500/S900 UART + as the system console. Only earlycon is implemented currently. + endmenu config SERIAL_MCTRL_GPIO diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 53c03e005132..fe88a75d9a59 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -92,6 +92,7 @@ obj-$(CONFIG_SERIAL_STM32) += stm32-usart.o obj-$(CONFIG_SERIAL_MVEBU_UART) += mvebu-uart.o obj-$(CONFIG_SERIAL_PIC32) += pic32_uart.o obj-$(CONFIG_SERIAL_MPS2_UART) += mps2-uart.o +obj-$(CONFIG_SERIAL_OWL) += owl-uart.o # GPIOLIB helpers for modem control lines obj-$(CONFIG_SERIAL_MCTRL_GPIO) += serial_mctrl_gpio.o diff --git a/drivers/tty/serial/amba-pl010.c b/drivers/tty/serial/amba-pl010.c index f2f251075109..24180adb1cbb 100644 --- a/drivers/tty/serial/amba-pl010.c +++ b/drivers/tty/serial/amba-pl010.c @@ -697,6 +697,7 @@ static struct console amba_console = { #define AMBA_CONSOLE NULL #endif +static DEFINE_MUTEX(amba_reg_lock); static struct uart_driver amba_reg = { .owner = THIS_MODULE, .driver_name = "ttyAM", @@ -749,6 +750,19 @@ static int pl010_probe(struct amba_device *dev, const struct amba_id *id) amba_ports[i] = uap; amba_set_drvdata(dev, uap); + + mutex_lock(&amba_reg_lock); + if (!amba_reg.state) { + ret = uart_register_driver(&amba_reg); + if (ret < 0) { + mutex_unlock(&amba_reg_lock); + dev_err(uap->port.dev, + "Failed to register AMBA-PL010 driver\n"); + return ret; + } + } + mutex_unlock(&amba_reg_lock); + ret = uart_add_one_port(&amba_reg, &uap->port); if (ret) amba_ports[i] = NULL; @@ -760,12 +774,18 @@ static int pl010_remove(struct amba_device *dev) { struct uart_amba_port *uap = amba_get_drvdata(dev); int i; + bool busy = false; uart_remove_one_port(&amba_reg, &uap->port); for (i = 0; i < ARRAY_SIZE(amba_ports); i++) if (amba_ports[i] == uap) amba_ports[i] = NULL; + else if (amba_ports[i]) + busy = true; + + if (!busy) + uart_unregister_driver(&amba_reg); return 0; } @@ -816,23 +836,14 @@ static struct amba_driver pl010_driver = { static int __init pl010_init(void) { - int ret; - printk(KERN_INFO "Serial: AMBA driver\n"); - ret = uart_register_driver(&amba_reg); - if (ret == 0) { - ret = amba_driver_register(&pl010_driver); - if (ret) - uart_unregister_driver(&amba_reg); - } - return ret; + return amba_driver_register(&pl010_driver); } static void __exit pl010_exit(void) { amba_driver_unregister(&pl010_driver); - uart_unregister_driver(&amba_reg); } module_init(pl010_init); diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index c355ac9abafc..7551cab438ff 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -1,5 +1,5 @@ /* - * Driver for Atmel AT91 / AT32 Serial ports + * Driver for Atmel AT91 Serial ports * Copyright (C) 2003 Rick Bronson * * Based on drivers/char/serial_sa1100.c, by Deep Blue Solutions Ltd. @@ -46,6 +46,7 @@ #include <linux/err.h> #include <linux/irq.h> #include <linux/suspend.h> +#include <linux/mm.h> #include <asm/io.h> #include <asm/ioctls.h> @@ -118,7 +119,6 @@ struct atmel_uart_char { /* * at91: 6 USARTs and one DBGU port (SAM9260) - * avr32: 4 * samx7: 3 USARTs and 5 UARTs */ #define ATMEL_MAX_UART 8 @@ -228,21 +228,6 @@ static inline void atmel_uart_writel(struct uart_port *port, u32 reg, u32 value) __raw_writel(value, port->membase + reg); } -#ifdef CONFIG_AVR32 - -/* AVR32 cannot handle 8 or 16bit I/O accesses but only 32bit I/O accesses */ -static inline u8 atmel_uart_read_char(struct uart_port *port) -{ - return __raw_readl(port->membase + ATMEL_US_RHR); -} - -static inline void atmel_uart_write_char(struct uart_port *port, u8 value) -{ - __raw_writel(value, port->membase + ATMEL_US_THR); -} - -#else - static inline u8 atmel_uart_read_char(struct uart_port *port) { return __raw_readb(port->membase + ATMEL_US_RHR); @@ -253,8 +238,6 @@ static inline void atmel_uart_write_char(struct uart_port *port, u8 value) __raw_writeb(value, port->membase + ATMEL_US_THR); } -#endif - #ifdef CONFIG_SERIAL_ATMEL_PDC static bool atmel_use_pdc_rx(struct uart_port *port) { @@ -959,7 +942,7 @@ static int atmel_prepare_tx_dma(struct uart_port *port) sg_set_page(&atmel_port->sg_tx, virt_to_page(port->state->xmit.buf), UART_XMIT_SIZE, - (unsigned long)port->state->xmit.buf & ~PAGE_MASK); + offset_in_page(port->state->xmit.buf)); nent = dma_map_sg(port->dev, &atmel_port->sg_tx, 1, @@ -1141,7 +1124,7 @@ static int atmel_prepare_rx_dma(struct uart_port *port) sg_set_page(&atmel_port->sg_rx, virt_to_page(ring->buf), sizeof(struct atmel_uart_char) * ATMEL_SERIAL_RINGSIZE, - (unsigned long)ring->buf & ~PAGE_MASK); + offset_in_page(ring->buf)); nent = dma_map_sg(port->dev, &atmel_port->sg_rx, 1, @@ -1655,72 +1638,56 @@ static void atmel_init_property(struct atmel_uart_port *atmel_port, struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; - struct atmel_uart_data *pdata = dev_get_platdata(&pdev->dev); - - if (np) { - /* DMA/PDC usage specification */ - if (of_property_read_bool(np, "atmel,use-dma-rx")) { - if (of_property_read_bool(np, "dmas")) { - atmel_port->use_dma_rx = true; - atmel_port->use_pdc_rx = false; - } else { - atmel_port->use_dma_rx = false; - atmel_port->use_pdc_rx = true; - } + + /* DMA/PDC usage specification */ + if (of_property_read_bool(np, "atmel,use-dma-rx")) { + if (of_property_read_bool(np, "dmas")) { + atmel_port->use_dma_rx = true; + atmel_port->use_pdc_rx = false; } else { atmel_port->use_dma_rx = false; - atmel_port->use_pdc_rx = false; + atmel_port->use_pdc_rx = true; } + } else { + atmel_port->use_dma_rx = false; + atmel_port->use_pdc_rx = false; + } - if (of_property_read_bool(np, "atmel,use-dma-tx")) { - if (of_property_read_bool(np, "dmas")) { - atmel_port->use_dma_tx = true; - atmel_port->use_pdc_tx = false; - } else { - atmel_port->use_dma_tx = false; - atmel_port->use_pdc_tx = true; - } + if (of_property_read_bool(np, "atmel,use-dma-tx")) { + if (of_property_read_bool(np, "dmas")) { + atmel_port->use_dma_tx = true; + atmel_port->use_pdc_tx = false; } else { atmel_port->use_dma_tx = false; - atmel_port->use_pdc_tx = false; + atmel_port->use_pdc_tx = true; } - } else { - atmel_port->use_pdc_rx = pdata->use_dma_rx; - atmel_port->use_pdc_tx = pdata->use_dma_tx; - atmel_port->use_dma_rx = false; atmel_port->use_dma_tx = false; + atmel_port->use_pdc_tx = false; } - } static void atmel_init_rs485(struct uart_port *port, struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; - struct atmel_uart_data *pdata = dev_get_platdata(&pdev->dev); - - if (np) { - struct serial_rs485 *rs485conf = &port->rs485; - u32 rs485_delay[2]; - /* rs485 properties */ - if (of_property_read_u32_array(np, "rs485-rts-delay", - rs485_delay, 2) == 0) { - rs485conf->delay_rts_before_send = rs485_delay[0]; - rs485conf->delay_rts_after_send = rs485_delay[1]; - rs485conf->flags = 0; - } - if (of_get_property(np, "rs485-rx-during-tx", NULL)) - rs485conf->flags |= SER_RS485_RX_DURING_TX; + struct serial_rs485 *rs485conf = &port->rs485; + u32 rs485_delay[2]; - if (of_get_property(np, "linux,rs485-enabled-at-boot-time", - NULL)) - rs485conf->flags |= SER_RS485_ENABLED; - } else { - port->rs485 = pdata->rs485; + /* rs485 properties */ + if (of_property_read_u32_array(np, "rs485-rts-delay", + rs485_delay, 2) == 0) { + rs485conf->delay_rts_before_send = rs485_delay[0]; + rs485conf->delay_rts_after_send = rs485_delay[1]; + rs485conf->flags = 0; } + if (of_get_property(np, "rs485-rx-during-tx", NULL)) + rs485conf->flags |= SER_RS485_RX_DURING_TX; + + if (of_get_property(np, "linux,rs485-enabled-at-boot-time", NULL)) + rs485conf->flags |= SER_RS485_ENABLED; } static void atmel_set_ops(struct uart_port *port) @@ -2402,7 +2369,6 @@ static int atmel_init_port(struct atmel_uart_port *atmel_port, { int ret; struct uart_port *port = &atmel_port->uart; - struct atmel_uart_data *pdata = dev_get_platdata(&pdev->dev); atmel_init_property(atmel_port, pdev); atmel_set_ops(port); @@ -2410,24 +2376,17 @@ static int atmel_init_port(struct atmel_uart_port *atmel_port, atmel_init_rs485(port, pdev); port->iotype = UPIO_MEM; - port->flags = UPF_BOOT_AUTOCONF; + port->flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP; port->ops = &atmel_pops; port->fifosize = 1; port->dev = &pdev->dev; port->mapbase = pdev->resource[0].start; port->irq = pdev->resource[1].start; port->rs485_config = atmel_config_rs485; + port->membase = NULL; memset(&atmel_port->rx_ring, 0, sizeof(atmel_port->rx_ring)); - if (pdata && pdata->regs) { - /* Already mapped by setup code */ - port->membase = pdata->regs; - } else { - port->flags |= UPF_IOREMAP; - port->membase = NULL; - } - /* for console, the clock could already be configured */ if (!atmel_port->clk) { atmel_port->clk = clk_get(&pdev->dev, "usart"); @@ -2460,8 +2419,6 @@ static int atmel_init_port(struct atmel_uart_port *atmel_port, return 0; } -struct platform_device *atmel_default_console_device; /* the serial console device */ - #ifdef CONFIG_SERIAL_ATMEL_CONSOLE static void atmel_console_putchar(struct uart_port *port, int ch) { @@ -2594,47 +2551,6 @@ static struct console atmel_console = { #define ATMEL_CONSOLE_DEVICE (&atmel_console) -/* - * Early console initialization (before VM subsystem initialized). - */ -static int __init atmel_console_init(void) -{ - int ret; - if (atmel_default_console_device) { - struct atmel_uart_data *pdata = - dev_get_platdata(&atmel_default_console_device->dev); - int id = pdata->num; - struct atmel_uart_port *atmel_port = &atmel_ports[id]; - - atmel_port->backup_imr = 0; - atmel_port->uart.line = id; - - add_preferred_console(ATMEL_DEVICENAME, id, NULL); - ret = atmel_init_port(atmel_port, atmel_default_console_device); - if (ret) - return ret; - register_console(&atmel_console); - } - - return 0; -} - -console_initcall(atmel_console_init); - -/* - * Late console initialization. - */ -static int __init atmel_late_console_init(void) -{ - if (atmel_default_console_device - && !(atmel_console.flags & CON_ENABLED)) - register_console(&atmel_console); - - return 0; -} - -core_initcall(atmel_late_console_init); - static inline bool atmel_is_console_port(struct uart_port *port) { return port->cons && port->cons->index == port->line; @@ -2804,19 +2720,13 @@ static int atmel_serial_probe(struct platform_device *pdev) { struct atmel_uart_port *atmel_port; struct device_node *np = pdev->dev.of_node; - struct atmel_uart_data *pdata = dev_get_platdata(&pdev->dev); void *data; int ret = -ENODEV; bool rs485_enabled; BUILD_BUG_ON(ATMEL_SERIAL_RINGSIZE & (ATMEL_SERIAL_RINGSIZE - 1)); - if (np) - ret = of_alias_get_id(np, "serial"); - else - if (pdata) - ret = pdata->num; - + ret = of_alias_get_id(np, "serial"); if (ret < 0) /* port id not found in platform data nor device-tree aliases: * auto-enumerate it */ diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 15df1ba78095..343de8c384b0 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -140,6 +140,8 @@ #define UARTBAUD_SBNS 0x00002000 #define UARTBAUD_SBR 0x00000000 #define UARTBAUD_SBR_MASK 0x1fff +#define UARTBAUD_OSR_MASK 0x1f +#define UARTBAUD_OSR_SHIFT 24 #define UARTSTAT_LBKDIF 0x80000000 #define UARTSTAT_RXEDGIF 0x40000000 @@ -231,12 +233,14 @@ #define DEV_NAME "ttyLP" #define UART_NR 6 +/* IMX lpuart has four extra unused regs located at the beginning */ +#define IMX_REG_OFF 0x10 + struct lpuart_port { struct uart_port port; struct clk *clk; unsigned int txfifo_size; unsigned int rxfifo_size; - bool lpuart32; bool lpuart_dma_tx_use; bool lpuart_dma_rx_use; @@ -258,13 +262,28 @@ struct lpuart_port { wait_queue_head_t dma_wait; }; +struct lpuart_soc_data { + char iotype; + u8 reg_off; +}; + +static const struct lpuart_soc_data vf_data = { + .iotype = UPIO_MEM, +}; + +static const struct lpuart_soc_data ls_data = { + .iotype = UPIO_MEM32BE, +}; + +static struct lpuart_soc_data imx_data = { + .iotype = UPIO_MEM32, + .reg_off = IMX_REG_OFF, +}; + static const struct of_device_id lpuart_dt_ids[] = { - { - .compatible = "fsl,vf610-lpuart", - }, - { - .compatible = "fsl,ls1021a-lpuart", - }, + { .compatible = "fsl,vf610-lpuart", .data = &vf_data, }, + { .compatible = "fsl,ls1021a-lpuart", .data = &ls_data, }, + { .compatible = "fsl,imx7ulp-lpuart", .data = &imx_data, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, lpuart_dt_ids); @@ -272,14 +291,29 @@ MODULE_DEVICE_TABLE(of, lpuart_dt_ids); /* Forward declare this for the dma callbacks*/ static void lpuart_dma_tx_complete(void *arg); -static u32 lpuart32_read(void __iomem *addr) +static inline u32 lpuart32_read(struct uart_port *port, u32 off) { - return ioread32be(addr); + switch (port->iotype) { + case UPIO_MEM32: + return readl(port->membase + off); + case UPIO_MEM32BE: + return ioread32be(port->membase + off); + default: + return 0; + } } -static void lpuart32_write(u32 val, void __iomem *addr) +static inline void lpuart32_write(struct uart_port *port, u32 val, + u32 off) { - iowrite32be(val, addr); + switch (port->iotype) { + case UPIO_MEM32: + writel(val, port->membase + off); + break; + case UPIO_MEM32BE: + iowrite32be(val, port->membase + off); + break; + } } static void lpuart_stop_tx(struct uart_port *port) @@ -295,9 +329,9 @@ static void lpuart32_stop_tx(struct uart_port *port) { unsigned long temp; - temp = lpuart32_read(port->membase + UARTCTRL); + temp = lpuart32_read(port, UARTCTRL); temp &= ~(UARTCTRL_TIE | UARTCTRL_TCIE); - lpuart32_write(temp, port->membase + UARTCTRL); + lpuart32_write(port, temp, UARTCTRL); } static void lpuart_stop_rx(struct uart_port *port) @@ -312,8 +346,8 @@ static void lpuart32_stop_rx(struct uart_port *port) { unsigned long temp; - temp = lpuart32_read(port->membase + UARTCTRL); - lpuart32_write(temp & ~UARTCTRL_RE, port->membase + UARTCTRL); + temp = lpuart32_read(port, UARTCTRL); + lpuart32_write(port, temp & ~UARTCTRL_RE, UARTCTRL); } static void lpuart_dma_tx(struct lpuart_port *sport) @@ -512,14 +546,14 @@ static inline void lpuart32_transmit_buffer(struct lpuart_port *sport) struct circ_buf *xmit = &sport->port.state->xmit; unsigned long txcnt; - txcnt = lpuart32_read(sport->port.membase + UARTWATER); + txcnt = lpuart32_read(&sport->port, UARTWATER); txcnt = txcnt >> UARTWATER_TXCNT_OFF; txcnt &= UARTWATER_COUNT_MASK; while (!uart_circ_empty(xmit) && (txcnt < sport->txfifo_size)) { - lpuart32_write(xmit->buf[xmit->tail], sport->port.membase + UARTDATA); + lpuart32_write(&sport->port, xmit->buf[xmit->tail], UARTDATA); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); sport->port.icount.tx++; - txcnt = lpuart32_read(sport->port.membase + UARTWATER); + txcnt = lpuart32_read(&sport->port, UARTWATER); txcnt = txcnt >> UARTWATER_TXCNT_OFF; txcnt &= UARTWATER_COUNT_MASK; } @@ -555,10 +589,10 @@ static void lpuart32_start_tx(struct uart_port *port) struct lpuart_port *sport = container_of(port, struct lpuart_port, port); unsigned long temp; - temp = lpuart32_read(port->membase + UARTCTRL); - lpuart32_write(temp | UARTCTRL_TIE, port->membase + UARTCTRL); + temp = lpuart32_read(port, UARTCTRL); + lpuart32_write(port, temp | UARTCTRL_TIE, UARTCTRL); - if (lpuart32_read(port->membase + UARTSTAT) & UARTSTAT_TDRE) + if (lpuart32_read(port, UARTSTAT) & UARTSTAT_TDRE) lpuart32_transmit_buffer(sport); } @@ -581,7 +615,7 @@ static unsigned int lpuart_tx_empty(struct uart_port *port) static unsigned int lpuart32_tx_empty(struct uart_port *port) { - return (lpuart32_read(port->membase + UARTSTAT) & UARTSTAT_TC) ? + return (lpuart32_read(port, UARTSTAT) & UARTSTAT_TC) ? TIOCSER_TEMT : 0; } @@ -593,22 +627,22 @@ static irqreturn_t lpuart_txint(int irq, void *dev_id) spin_lock_irqsave(&sport->port.lock, flags); if (sport->port.x_char) { - if (sport->lpuart32) - lpuart32_write(sport->port.x_char, sport->port.membase + UARTDATA); + if (sport->port.iotype & (UPIO_MEM32 | UPIO_MEM32BE)) + lpuart32_write(&sport->port, sport->port.x_char, UARTDATA); else writeb(sport->port.x_char, sport->port.membase + UARTDR); goto out; } if (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)) { - if (sport->lpuart32) + if (sport->port.iotype & (UPIO_MEM32 | UPIO_MEM32BE)) lpuart32_stop_tx(&sport->port); else lpuart_stop_tx(&sport->port); goto out; } - if (sport->lpuart32) + if (sport->port.iotype & (UPIO_MEM32 | UPIO_MEM32BE)) lpuart32_transmit_buffer(sport); else lpuart_transmit_buffer(sport); @@ -694,15 +728,15 @@ static irqreturn_t lpuart32_rxint(int irq, void *dev_id) spin_lock_irqsave(&sport->port.lock, flags); - while (!(lpuart32_read(sport->port.membase + UARTFIFO) & UARTFIFO_RXEMPT)) { + while (!(lpuart32_read(&sport->port, UARTFIFO) & UARTFIFO_RXEMPT)) { flg = TTY_NORMAL; sport->port.icount.rx++; /* * to clear the FE, OR, NF, FE, PE flags, * read STAT then read DATA reg */ - sr = lpuart32_read(sport->port.membase + UARTSTAT); - rx = lpuart32_read(sport->port.membase + UARTDATA); + sr = lpuart32_read(&sport->port, UARTSTAT); + rx = lpuart32_read(&sport->port, UARTDATA); rx &= 0x3ff; if (uart_handle_sysrq_char(&sport->port, (unsigned char)rx)) @@ -769,18 +803,18 @@ static irqreturn_t lpuart32_int(int irq, void *dev_id) struct lpuart_port *sport = dev_id; unsigned long sts, rxcount; - sts = lpuart32_read(sport->port.membase + UARTSTAT); - rxcount = lpuart32_read(sport->port.membase + UARTWATER); + sts = lpuart32_read(&sport->port, UARTSTAT); + rxcount = lpuart32_read(&sport->port, UARTWATER); rxcount = rxcount >> UARTWATER_RXCNT_OFF; if (sts & UARTSTAT_RDRF || rxcount > 0) lpuart32_rxint(irq, dev_id); if ((sts & UARTSTAT_TDRE) && - !(lpuart32_read(sport->port.membase + UARTBAUD) & UARTBAUD_TDMAE)) + !(lpuart32_read(&sport->port, UARTBAUD) & UARTBAUD_TDMAE)) lpuart_txint(irq, dev_id); - lpuart32_write(sts, sport->port.membase + UARTSTAT); + lpuart32_write(&sport->port, sts, UARTSTAT); return IRQ_HANDLED; } @@ -1041,7 +1075,7 @@ static unsigned int lpuart32_get_mctrl(struct uart_port *port) unsigned int temp = 0; unsigned long reg; - reg = lpuart32_read(port->membase + UARTMODIR); + reg = lpuart32_read(port, UARTMODIR); if (reg & UARTMODIR_TXCTSE) temp |= TIOCM_CTS; @@ -1076,7 +1110,7 @@ static void lpuart32_set_mctrl(struct uart_port *port, unsigned int mctrl) { unsigned long temp; - temp = lpuart32_read(port->membase + UARTMODIR) & + temp = lpuart32_read(port, UARTMODIR) & ~(UARTMODIR_RXRTSE | UARTMODIR_TXCTSE); if (mctrl & TIOCM_RTS) @@ -1085,7 +1119,7 @@ static void lpuart32_set_mctrl(struct uart_port *port, unsigned int mctrl) if (mctrl & TIOCM_CTS) temp |= UARTMODIR_TXCTSE; - lpuart32_write(temp, port->membase + UARTMODIR); + lpuart32_write(port, temp, UARTMODIR); } static void lpuart_break_ctl(struct uart_port *port, int break_state) @@ -1104,12 +1138,12 @@ static void lpuart32_break_ctl(struct uart_port *port, int break_state) { unsigned long temp; - temp = lpuart32_read(port->membase + UARTCTRL) & ~UARTCTRL_SBK; + temp = lpuart32_read(port, UARTCTRL) & ~UARTCTRL_SBK; if (break_state != 0) temp |= UARTCTRL_SBK; - lpuart32_write(temp, port->membase + UARTCTRL); + lpuart32_write(port, temp, UARTCTRL); } static void lpuart_setup_watermark(struct lpuart_port *sport) @@ -1149,24 +1183,24 @@ static void lpuart32_setup_watermark(struct lpuart_port *sport) unsigned long val, ctrl; unsigned long ctrl_saved; - ctrl = lpuart32_read(sport->port.membase + UARTCTRL); + ctrl = lpuart32_read(&sport->port, UARTCTRL); ctrl_saved = ctrl; ctrl &= ~(UARTCTRL_TIE | UARTCTRL_TCIE | UARTCTRL_TE | UARTCTRL_RIE | UARTCTRL_RE); - lpuart32_write(ctrl, sport->port.membase + UARTCTRL); + lpuart32_write(&sport->port, ctrl, UARTCTRL); /* enable FIFO mode */ - val = lpuart32_read(sport->port.membase + UARTFIFO); + val = lpuart32_read(&sport->port, UARTFIFO); val |= UARTFIFO_TXFE | UARTFIFO_RXFE; val |= UARTFIFO_TXFLUSH | UARTFIFO_RXFLUSH; - lpuart32_write(val, sport->port.membase + UARTFIFO); + lpuart32_write(&sport->port, val, UARTFIFO); /* set the watermark */ val = (0x1 << UARTWATER_RXWATER_OFF) | (0x0 << UARTWATER_TXWATER_OFF); - lpuart32_write(val, sport->port.membase + UARTWATER); + lpuart32_write(&sport->port, val, UARTWATER); /* Restore cr2 */ - lpuart32_write(ctrl_saved, sport->port.membase + UARTCTRL); + lpuart32_write(&sport->port, ctrl_saved, UARTCTRL); } static void rx_dma_timer_init(struct lpuart_port *sport) @@ -1242,7 +1276,7 @@ static int lpuart32_startup(struct uart_port *port) unsigned long temp; /* determine FIFO size */ - temp = lpuart32_read(sport->port.membase + UARTFIFO); + temp = lpuart32_read(&sport->port, UARTFIFO); sport->txfifo_size = 0x1 << (((temp >> UARTFIFO_TXSIZE_OFF) & UARTFIFO_FIFOSIZE_MASK) - 1); @@ -1259,10 +1293,10 @@ static int lpuart32_startup(struct uart_port *port) lpuart32_setup_watermark(sport); - temp = lpuart32_read(sport->port.membase + UARTCTRL); + temp = lpuart32_read(&sport->port, UARTCTRL); temp |= (UARTCTRL_RIE | UARTCTRL_TIE | UARTCTRL_RE | UARTCTRL_TE); temp |= UARTCTRL_ILIE; - lpuart32_write(temp, sport->port.membase + UARTCTRL); + lpuart32_write(&sport->port, temp, UARTCTRL); spin_unlock_irqrestore(&sport->port.lock, flags); return 0; @@ -1311,10 +1345,10 @@ static void lpuart32_shutdown(struct uart_port *port) spin_lock_irqsave(&port->lock, flags); /* disable Rx/Tx and interrupts */ - temp = lpuart32_read(port->membase + UARTCTRL); + temp = lpuart32_read(port, UARTCTRL); temp &= ~(UARTCTRL_TE | UARTCTRL_RE | UARTCTRL_TIE | UARTCTRL_TCIE | UARTCTRL_RIE); - lpuart32_write(temp, port->membase + UARTCTRL); + lpuart32_write(port, temp, UARTCTRL); spin_unlock_irqrestore(&port->lock, flags); @@ -1479,6 +1513,75 @@ lpuart_set_termios(struct uart_port *port, struct ktermios *termios, } static void +lpuart32_serial_setbrg(struct lpuart_port *sport, unsigned int baudrate) +{ + u32 sbr, osr, baud_diff, tmp_osr, tmp_sbr, tmp_diff, tmp; + u32 clk = sport->port.uartclk; + + /* + * The idea is to use the best OSR (over-sampling rate) possible. + * Note, OSR is typically hard-set to 16 in other LPUART instantiations. + * Loop to find the best OSR value possible, one that generates minimum + * baud_diff iterate through the rest of the supported values of OSR. + * + * Calculation Formula: + * Baud Rate = baud clock / ((OSR+1) × SBR) + */ + baud_diff = baudrate; + osr = 0; + sbr = 0; + + for (tmp_osr = 4; tmp_osr <= 32; tmp_osr++) { + /* calculate the temporary sbr value */ + tmp_sbr = (clk / (baudrate * tmp_osr)); + if (tmp_sbr == 0) + tmp_sbr = 1; + + /* + * calculate the baud rate difference based on the temporary + * osr and sbr values + */ + tmp_diff = clk / (tmp_osr * tmp_sbr) - baudrate; + + /* select best values between sbr and sbr+1 */ + tmp = clk / (tmp_osr * (tmp_sbr + 1)); + if (tmp_diff > (baudrate - tmp)) { + tmp_diff = baudrate - tmp; + tmp_sbr++; + } + + if (tmp_diff <= baud_diff) { + baud_diff = tmp_diff; + osr = tmp_osr; + sbr = tmp_sbr; + + if (!baud_diff) + break; + } + } + + /* handle buadrate outside acceptable rate */ + if (baud_diff > ((baudrate / 100) * 3)) + dev_warn(sport->port.dev, + "unacceptable baud rate difference of more than 3%%\n"); + + tmp = lpuart32_read(&sport->port, UARTBAUD); + + if ((osr > 3) && (osr < 8)) + tmp |= UARTBAUD_BOTHEDGE; + + tmp &= ~(UARTBAUD_OSR_MASK << UARTBAUD_OSR_SHIFT); + tmp |= (((osr-1) & UARTBAUD_OSR_MASK) << UARTBAUD_OSR_SHIFT); + + tmp &= ~UARTBAUD_SBR_MASK; + tmp |= sbr & UARTBAUD_SBR_MASK; + + tmp &= ~(UARTBAUD_TDMAE | UARTBAUD_RDMAE); + + lpuart32_write(&sport->port, tmp, UARTBAUD); +} + +static void lpuart32_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { @@ -1487,11 +1590,10 @@ lpuart32_set_termios(struct uart_port *port, struct ktermios *termios, unsigned long ctrl, old_ctrl, bd, modem; unsigned int baud; unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8; - unsigned int sbr; - ctrl = old_ctrl = lpuart32_read(sport->port.membase + UARTCTRL); - bd = lpuart32_read(sport->port.membase + UARTBAUD); - modem = lpuart32_read(sport->port.membase + UARTMODIR); + ctrl = old_ctrl = lpuart32_read(&sport->port, UARTCTRL); + bd = lpuart32_read(&sport->port, UARTBAUD); + modem = lpuart32_read(&sport->port, UARTMODIR); /* * only support CS8 and CS7, and for CS7 must enable PE. * supported mode: @@ -1577,21 +1679,16 @@ lpuart32_set_termios(struct uart_port *port, struct ktermios *termios, uart_update_timeout(port, termios->c_cflag, baud); /* wait transmit engin complete */ - while (!(lpuart32_read(sport->port.membase + UARTSTAT) & UARTSTAT_TC)) + while (!(lpuart32_read(&sport->port, UARTSTAT) & UARTSTAT_TC)) barrier(); /* disable transmit and receive */ - lpuart32_write(old_ctrl & ~(UARTCTRL_TE | UARTCTRL_RE), - sport->port.membase + UARTCTRL); + lpuart32_write(&sport->port, old_ctrl & ~(UARTCTRL_TE | UARTCTRL_RE), + UARTCTRL); - sbr = sport->port.uartclk / (16 * baud); - bd &= ~UARTBAUD_SBR_MASK; - bd |= sbr & UARTBAUD_SBR_MASK; - bd |= UARTBAUD_BOTHEDGE; - bd &= ~(UARTBAUD_TDMAE | UARTBAUD_RDMAE); - lpuart32_write(bd, sport->port.membase + UARTBAUD); - lpuart32_write(modem, sport->port.membase + UARTMODIR); - lpuart32_write(ctrl, sport->port.membase + UARTCTRL); + lpuart32_serial_setbrg(sport, baud); + lpuart32_write(&sport->port, modem, UARTMODIR); + lpuart32_write(&sport->port, ctrl, UARTCTRL); /* restore control register */ spin_unlock_irqrestore(&sport->port.lock, flags); @@ -1694,10 +1791,10 @@ static void lpuart_console_putchar(struct uart_port *port, int ch) static void lpuart32_console_putchar(struct uart_port *port, int ch) { - while (!(lpuart32_read(port->membase + UARTSTAT) & UARTSTAT_TDRE)) + while (!(lpuart32_read(port, UARTSTAT) & UARTSTAT_TDRE)) barrier(); - lpuart32_write(ch, port->membase + UARTDATA); + lpuart32_write(port, ch, UARTDATA); } static void @@ -1745,18 +1842,18 @@ lpuart32_console_write(struct console *co, const char *s, unsigned int count) spin_lock_irqsave(&sport->port.lock, flags); /* first save CR2 and then disable interrupts */ - cr = old_cr = lpuart32_read(sport->port.membase + UARTCTRL); + cr = old_cr = lpuart32_read(&sport->port, UARTCTRL); cr |= (UARTCTRL_TE | UARTCTRL_RE); cr &= ~(UARTCTRL_TIE | UARTCTRL_TCIE | UARTCTRL_RIE); - lpuart32_write(cr, sport->port.membase + UARTCTRL); + lpuart32_write(&sport->port, cr, UARTCTRL); uart_console_write(&sport->port, s, count, lpuart32_console_putchar); /* wait for transmitter finish complete and restore CR2 */ - while (!(lpuart32_read(sport->port.membase + UARTSTAT) & UARTSTAT_TC)) + while (!(lpuart32_read(&sport->port, UARTSTAT) & UARTSTAT_TC)) barrier(); - lpuart32_write(old_cr, sport->port.membase + UARTCTRL); + lpuart32_write(&sport->port, old_cr, UARTCTRL); if (locked) spin_unlock_irqrestore(&sport->port.lock, flags); @@ -1822,14 +1919,14 @@ lpuart32_console_get_options(struct lpuart_port *sport, int *baud, unsigned long cr, bd; unsigned int sbr, uartclk, baud_raw; - cr = lpuart32_read(sport->port.membase + UARTCTRL); + cr = lpuart32_read(&sport->port, UARTCTRL); cr &= UARTCTRL_TE | UARTCTRL_RE; if (!cr) return; /* ok, the port was enabled */ - cr = lpuart32_read(sport->port.membase + UARTCTRL); + cr = lpuart32_read(&sport->port, UARTCTRL); *parity = 'n'; if (cr & UARTCTRL_PE) { @@ -1844,7 +1941,7 @@ lpuart32_console_get_options(struct lpuart_port *sport, int *baud, else *bits = 8; - bd = lpuart32_read(sport->port.membase + UARTBAUD); + bd = lpuart32_read(&sport->port, UARTBAUD); bd &= UARTBAUD_SBR_MASK; sbr = bd; uartclk = clk_get_rate(sport->clk); @@ -1881,12 +1978,12 @@ static int __init lpuart_console_setup(struct console *co, char *options) if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); else - if (sport->lpuart32) + if (sport->port.iotype & (UPIO_MEM32 | UPIO_MEM32BE)) lpuart32_console_get_options(sport, &baud, &parity, &bits); else lpuart_console_get_options(sport, &baud, &parity, &bits); - if (sport->lpuart32) + if (sport->port.iotype & (UPIO_MEM32 | UPIO_MEM32BE)) lpuart32_setup_watermark(sport); else lpuart_setup_watermark(sport); @@ -1945,12 +2042,26 @@ static int __init lpuart32_early_console_setup(struct earlycon_device *device, if (!device->port.membase) return -ENODEV; + device->port.iotype = UPIO_MEM32BE; device->con->write = lpuart32_early_write; return 0; } +static int __init lpuart32_imx_early_console_setup(struct earlycon_device *device, + const char *opt) +{ + if (!device->port.membase) + return -ENODEV; + + device->port.iotype = UPIO_MEM32; + device->port.membase += IMX_REG_OFF; + device->con->write = lpuart32_early_write; + + return 0; +} OF_EARLYCON_DECLARE(lpuart, "fsl,vf610-lpuart", lpuart_early_console_setup); OF_EARLYCON_DECLARE(lpuart32, "fsl,ls1021a-lpuart", lpuart32_early_console_setup); +OF_EARLYCON_DECLARE(lpuart32, "fsl,imx7ulp-lpuart", lpuart32_imx_early_console_setup); EARLYCON_DECLARE(lpuart, lpuart_early_console_setup); EARLYCON_DECLARE(lpuart32, lpuart32_early_console_setup); @@ -1971,6 +2082,9 @@ static struct uart_driver lpuart_reg = { static int lpuart_probe(struct platform_device *pdev) { + const struct of_device_id *of_id = of_match_device(lpuart_dt_ids, + &pdev->dev); + const struct lpuart_soc_data *sdata = of_id->data; struct device_node *np = pdev->dev.of_node; struct lpuart_port *sport; struct resource *res; @@ -1988,25 +2102,23 @@ static int lpuart_probe(struct platform_device *pdev) return ret; } sport->port.line = ret; - sport->lpuart32 = of_device_is_compatible(np, "fsl,ls1021a-lpuart"); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); sport->port.membase = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(sport->port.membase)) return PTR_ERR(sport->port.membase); + sport->port.membase += sdata->reg_off; sport->port.mapbase = res->start; sport->port.dev = &pdev->dev; sport->port.type = PORT_LPUART; - sport->port.iotype = UPIO_MEM; ret = platform_get_irq(pdev, 0); if (ret < 0) { dev_err(&pdev->dev, "cannot obtain irq\n"); return ret; } sport->port.irq = ret; - - if (sport->lpuart32) + sport->port.iotype = sdata->iotype; + if (sport->port.iotype & (UPIO_MEM32 | UPIO_MEM32BE)) sport->port.ops = &lpuart32_pops; else sport->port.ops = &lpuart_pops; @@ -2033,7 +2145,7 @@ static int lpuart_probe(struct platform_device *pdev) platform_set_drvdata(pdev, &sport->port); - if (sport->lpuart32) + if (sport->port.iotype & (UPIO_MEM32 | UPIO_MEM32BE)) lpuart_reg.cons = LPUART32_CONSOLE; else lpuart_reg.cons = LPUART_CONSOLE; @@ -2086,11 +2198,11 @@ static int lpuart_suspend(struct device *dev) struct lpuart_port *sport = dev_get_drvdata(dev); unsigned long temp; - if (sport->lpuart32) { + if (sport->port.iotype & (UPIO_MEM32 | UPIO_MEM32BE)) { /* disable Rx/Tx and interrupts */ - temp = lpuart32_read(sport->port.membase + UARTCTRL); + temp = lpuart32_read(&sport->port, UARTCTRL); temp &= ~(UARTCTRL_TE | UARTCTRL_TIE | UARTCTRL_TCIE); - lpuart32_write(temp, sport->port.membase + UARTCTRL); + lpuart32_write(&sport->port, temp, UARTCTRL); } else { /* disable Rx/Tx and interrupts */ temp = readb(sport->port.membase + UARTCR2); @@ -2137,12 +2249,12 @@ static int lpuart_resume(struct device *dev) if (sport->port.suspended && !sport->port.irq_wake) clk_prepare_enable(sport->clk); - if (sport->lpuart32) { + if (sport->port.iotype & (UPIO_MEM32 | UPIO_MEM32BE)) { lpuart32_setup_watermark(sport); - temp = lpuart32_read(sport->port.membase + UARTCTRL); + temp = lpuart32_read(&sport->port, UARTCTRL); temp |= (UARTCTRL_RIE | UARTCTRL_TIE | UARTCTRL_RE | UARTCTRL_TE | UARTCTRL_ILIE); - lpuart32_write(temp, sport->port.membase + UARTCTRL); + lpuart32_write(&sport->port, temp, UARTCTRL); } else { lpuart_setup_watermark(sport); temp = readb(sport->port.membase + UARTCR2); diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index bbefddd92bfe..9e3162bf3bd1 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -186,6 +186,11 @@ #define UART_NR 8 +/* RX DMA buffer periods */ +#define RX_DMA_PERIODS 4 +#define RX_BUF_SIZE (PAGE_SIZE) + + /* i.MX21 type uart runs on all i.mx except i.MX1 and i.MX6q */ enum imx_uart_type { IMX1_UART, @@ -207,9 +212,6 @@ struct imx_port { unsigned int have_rtscts:1; unsigned int have_rtsgpio:1; unsigned int dte_mode:1; - unsigned int irda_inv_rx:1; - unsigned int irda_inv_tx:1; - unsigned short trcv_delay; /* transceiver delay */ struct clk *clk_ipg; struct clk *clk_per; const struct imx_uart_data *devdata; @@ -224,6 +226,7 @@ struct imx_port { struct dma_chan *dma_chan_rx, *dma_chan_tx; struct scatterlist rx_sgl, tx_sgl[2]; void *rx_buf; + unsigned int rx_buf_size; struct circ_buf rx_ring; unsigned int rx_periods; dma_cookie_t rx_cookie; @@ -964,8 +967,6 @@ static void imx_timeout(unsigned long data) } } -#define RX_BUF_SIZE (PAGE_SIZE) - /* * There are two kinds of RX DMA interrupts(such as in the MX6Q): * [1] the RX DMA buffer is full. @@ -1048,9 +1049,6 @@ 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) { struct scatterlist *sgl = &sport->rx_sgl; @@ -1061,9 +1059,8 @@ static int start_rx_dma(struct imx_port *sport) sport->rx_ring.head = 0; sport->rx_ring.tail = 0; - sport->rx_periods = RX_DMA_PERIODS; - sg_init_one(sgl, sport->rx_buf, RX_BUF_SIZE); + sg_init_one(sgl, sport->rx_buf, sport->rx_buf_size); ret = dma_map_sg(dev, sgl, 1, DMA_FROM_DEVICE); if (ret == 0) { dev_err(dev, "DMA mapping error for RX.\n"); @@ -1174,7 +1171,7 @@ static int imx_uart_dma_init(struct imx_port *sport) goto err; } - sport->rx_buf = kzalloc(PAGE_SIZE, GFP_KERNEL); + sport->rx_buf = kzalloc(sport->rx_buf_size, GFP_KERNEL); if (!sport->rx_buf) { ret = -ENOMEM; goto err; @@ -1302,7 +1299,9 @@ static int imx_startup(struct uart_port *port) imx_enable_dma(sport); temp = readl(sport->port.membase + UCR1); - temp |= UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN; + temp |= UCR1_RRDYEN | UCR1_UARTEN; + if (sport->have_rtscts) + temp |= UCR1_RTSDEN; writel(temp, sport->port.membase + UCR1); @@ -1340,29 +1339,13 @@ static int imx_startup(struct uart_port *port) imx_enable_ms(&sport->port); /* - * If the serial port is opened for reading start RX DMA immediately - * instead of waiting for RX FIFO interrupts. In our iMX53 the average - * delay for the first reception dropped from approximately 35000 - * microseconds to 1000 microseconds. + * Start RX DMA immediately instead of waiting for RX FIFO interrupts. + * In our iMX53 the average delay for the first reception dropped from + * approximately 35000 microseconds to 1000 microseconds. */ if (sport->dma_is_enabled) { - struct tty_struct *tty = sport->port.state->port.tty; - struct tty_file_private *file_priv; - int readcnt = 0; - - spin_lock(&tty->files_lock); - - if (!list_empty(&tty->tty_files)) - list_for_each_entry(file_priv, &tty->tty_files, list) - if (!(file_priv->file->f_flags & O_WRONLY)) - readcnt++; - - spin_unlock(&tty->files_lock); - - if (readcnt > 0) { - imx_disable_rx_int(sport); - start_rx_dma(sport); - } + imx_disable_rx_int(sport); + start_rx_dma(sport); } spin_unlock_irqrestore(&sport->port.lock, flags); @@ -2053,6 +2036,7 @@ static int serial_imx_probe_dt(struct imx_port *sport, { struct device_node *np = pdev->dev.of_node; int ret; + u32 dma_buf_size[2]; sport->devdata = of_device_get_match_data(&pdev->dev); if (!sport->devdata) @@ -2076,6 +2060,14 @@ static int serial_imx_probe_dt(struct imx_port *sport, if (of_get_property(np, "rts-gpios", NULL)) sport->have_rtsgpio = 1; + if (!of_property_read_u32_array(np, "fsl,dma-size", dma_buf_size, 2)) { + sport->rx_buf_size = dma_buf_size[0] * dma_buf_size[1]; + sport->rx_periods = dma_buf_size[1]; + } else { + sport->rx_buf_size = RX_BUF_SIZE; + sport->rx_periods = RX_DMA_PERIODS; + } + return 0; } #else diff --git a/drivers/tty/serial/ioc3_serial.c b/drivers/tty/serial/ioc3_serial.c index 2a61dd6b4009..906ee770ff4a 100644 --- a/drivers/tty/serial/ioc3_serial.c +++ b/drivers/tty/serial/ioc3_serial.c @@ -377,7 +377,7 @@ static struct ioc3_port *get_ioc3_port(struct uart_port *the_port) * called per port from attach... * @port: port to initialize */ -static int inline port_init(struct ioc3_port *port) +static inline int port_init(struct ioc3_port *port) { uint32_t sio_cr; struct port_hooks *hooks = port->ip_hooks; @@ -1430,7 +1430,7 @@ static int receive_chars(struct uart_port *the_port) * @pending: interrupts to handle */ -static int inline +static inline int ioc3uart_intr_one(struct ioc3_submodule *is, struct ioc3_driver_data *idd, unsigned int pending) diff --git a/drivers/tty/serial/ioc4_serial.c b/drivers/tty/serial/ioc4_serial.c index f96bcf9bee25..43d7d32eb150 100644 --- a/drivers/tty/serial/ioc4_serial.c +++ b/drivers/tty/serial/ioc4_serial.c @@ -824,7 +824,7 @@ pending_intrs(struct ioc4_soft *soft, int type) * called per port from attach... * @port: port to initialize */ -static int inline port_init(struct ioc4_port *port) +static inline int port_init(struct ioc4_port *port) { uint32_t sio_cr; struct hooks *hooks = port->ip_hooks; @@ -1048,7 +1048,7 @@ static irqreturn_t ioc4_intr(int irq, void *arg) * IOC4 with serial ports in the system. * @idd: Master module data for this IOC4 */ -static int inline ioc4_attach_local(struct ioc4_driver_data *idd) +static inline int ioc4_attach_local(struct ioc4_driver_data *idd) { struct ioc4_port *port; struct ioc4_port *ports[IOC4_NUM_SERIAL_PORTS]; diff --git a/drivers/tty/serial/meson_uart.c b/drivers/tty/serial/meson_uart.c index 60f16795d16b..42e4a4c7597f 100644 --- a/drivers/tty/serial/meson_uart.c +++ b/drivers/tty/serial/meson_uart.c @@ -286,7 +286,7 @@ static int meson_uart_startup(struct uart_port *port) writel(val, port->membase + AML_UART_MISC); ret = request_irq(port->irq, meson_uart_interrupt, 0, - meson_uart_type(port), port); + port->name, port); return ret; } @@ -298,8 +298,6 @@ static void meson_uart_change_speed(struct uart_port *port, unsigned long baud) while (!meson_uart_tx_empty(port)) cpu_relax(); - val = readl(port->membase + AML_UART_REG5); - val &= ~AML_UART_BAUD_MASK; if (port->uartclk == 24000000) { val = ((port->uartclk / 3) / baud) - 1; val |= AML_UART_BAUD_XTAL; @@ -355,7 +353,7 @@ static void meson_uart_set_termios(struct uart_port *port, if (cflags & CSTOPB) val |= AML_UART_STOP_BIN_2SB; else - val &= ~AML_UART_STOP_BIN_1SB; + val |= AML_UART_STOP_BIN_1SB; if (cflags & CRTSCTS) val &= ~AML_UART_TWO_WIRE_EN; @@ -395,51 +393,25 @@ static int meson_uart_verify_port(struct uart_port *port, return ret; } -static int meson_uart_res_size(struct uart_port *port) -{ - struct platform_device *pdev = to_platform_device(port->dev); - struct resource *res; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(port->dev, "cannot obtain I/O memory region"); - return -ENODEV; - } - - return resource_size(res); -} - static void meson_uart_release_port(struct uart_port *port) { - int size = meson_uart_res_size(port); - - if (port->flags & UPF_IOREMAP) { - devm_release_mem_region(port->dev, port->mapbase, size); - devm_iounmap(port->dev, port->membase); - port->membase = NULL; - } + devm_iounmap(port->dev, port->membase); + port->membase = NULL; + devm_release_mem_region(port->dev, port->mapbase, port->mapsize); } static int meson_uart_request_port(struct uart_port *port) { - int size = meson_uart_res_size(port); - - if (size < 0) - return size; - - if (!devm_request_mem_region(port->dev, port->mapbase, size, + if (!devm_request_mem_region(port->dev, port->mapbase, port->mapsize, dev_name(port->dev))) { dev_err(port->dev, "Memory region busy\n"); return -EBUSY; } - if (port->flags & UPF_IOREMAP) { - port->membase = devm_ioremap_nocache(port->dev, - port->mapbase, - size); - if (port->membase == NULL) - return -ENOMEM; - } + port->membase = devm_ioremap_nocache(port->dev, port->mapbase, + port->mapsize); + if (!port->membase) + return -ENOMEM; return 0; } @@ -470,6 +442,14 @@ static struct uart_ops meson_uart_ops = { }; #ifdef CONFIG_SERIAL_MESON_CONSOLE +static void meson_uart_enable_tx_engine(struct uart_port *port) +{ + u32 val; + + val = readl(port->membase + AML_UART_CONTROL); + val |= AML_UART_TX_EN; + writel(val, port->membase + AML_UART_CONTROL); +} static void meson_console_putchar(struct uart_port *port, int ch) { @@ -499,7 +479,6 @@ static void meson_serial_port_write(struct uart_port *port, const char *s, } val = readl(port->membase + AML_UART_CONTROL); - val |= AML_UART_TX_EN; tmp = val & ~(AML_UART_TX_INT_EN | AML_UART_RX_INT_EN); writel(tmp, port->membase + AML_UART_CONTROL); @@ -538,6 +517,8 @@ static int meson_serial_console_setup(struct console *co, char *options) if (!port || !port->membase) return -ENODEV; + meson_uart_enable_tx_engine(port); + if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); @@ -576,11 +557,16 @@ meson_serial_early_console_setup(struct earlycon_device *device, const char *opt if (!device->port.membase) return -ENODEV; + meson_uart_enable_tx_engine(&device->port); device->con->write = meson_serial_early_console_write; return 0; } +/* Legacy bindings, should be removed when no more used */ OF_EARLYCON_DECLARE(meson, "amlogic,meson-uart", meson_serial_early_console_setup); +/* Stable bindings */ +OF_EARLYCON_DECLARE(meson, "amlogic,meson-ao-uart", + meson_serial_early_console_setup); #define MESON_SERIAL_CONSOLE (&meson_serial_console) #else @@ -595,11 +581,76 @@ static struct uart_driver meson_uart_driver = { .cons = MESON_SERIAL_CONSOLE, }; +static inline struct clk *meson_uart_probe_clock(struct device *dev, + const char *id) +{ + struct clk *clk = NULL; + int ret; + + clk = devm_clk_get(dev, id); + if (IS_ERR(clk)) + return clk; + + ret = clk_prepare_enable(clk); + if (ret) { + dev_err(dev, "couldn't enable clk\n"); + return ERR_PTR(ret); + } + + devm_add_action_or_reset(dev, + (void(*)(void *))clk_disable_unprepare, + clk); + + return clk; +} + +/* + * This function gets clocks in the legacy non-stable DT bindings. + * This code will be remove once all the platforms switch to the + * new DT bindings. + */ +static int meson_uart_probe_clocks_legacy(struct platform_device *pdev, + struct uart_port *port) +{ + struct clk *clk = NULL; + + clk = meson_uart_probe_clock(&pdev->dev, NULL); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + port->uartclk = clk_get_rate(clk); + + return 0; +} + +static int meson_uart_probe_clocks(struct platform_device *pdev, + struct uart_port *port) +{ + struct clk *clk_xtal = NULL; + struct clk *clk_pclk = NULL; + struct clk *clk_baud = NULL; + + clk_pclk = meson_uart_probe_clock(&pdev->dev, "pclk"); + if (IS_ERR(clk_pclk)) + return PTR_ERR(clk_pclk); + + clk_xtal = meson_uart_probe_clock(&pdev->dev, "xtal"); + if (IS_ERR(clk_xtal)) + return PTR_ERR(clk_xtal); + + clk_baud = meson_uart_probe_clock(&pdev->dev, "baud"); + if (IS_ERR(clk_baud)) + return PTR_ERR(clk_baud); + + port->uartclk = clk_get_rate(clk_baud); + + return 0; +} + static int meson_uart_probe(struct platform_device *pdev) { struct resource *res_mem, *res_irq; struct uart_port *port; - struct clk *clk; int ret = 0; if (pdev->dev.of_node) @@ -625,15 +676,20 @@ static int meson_uart_probe(struct platform_device *pdev) if (!port) return -ENOMEM; - clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(clk)) - return PTR_ERR(clk); + /* Use legacy way until all platforms switch to new bindings */ + if (of_device_is_compatible(pdev->dev.of_node, "amlogic,meson-uart")) + ret = meson_uart_probe_clocks_legacy(pdev, port); + else + ret = meson_uart_probe_clocks(pdev, port); + + if (ret) + return ret; - port->uartclk = clk_get_rate(clk); port->iotype = UPIO_MEM; port->mapbase = res_mem->start; + port->mapsize = resource_size(res_mem); port->irq = res_irq->start; - port->flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP | UPF_LOW_LATENCY; + port->flags = UPF_BOOT_AUTOCONF | UPF_LOW_LATENCY; port->dev = &pdev->dev; port->line = pdev->id; port->type = PORT_MESON; @@ -668,9 +724,14 @@ static int meson_uart_remove(struct platform_device *pdev) return 0; } - static const struct of_device_id meson_uart_dt_match[] = { + /* Legacy bindings, should be removed when no more used */ { .compatible = "amlogic,meson-uart" }, + /* Stable bindings */ + { .compatible = "amlogic,meson6-uart" }, + { .compatible = "amlogic,meson8-uart" }, + { .compatible = "amlogic,meson8b-uart" }, + { .compatible = "amlogic,meson-gx-uart" }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, meson_uart_dt_match); diff --git a/drivers/tty/serial/mpsc.c b/drivers/tty/serial/mpsc.c index 1a60a2063e75..67ffecc50e42 100644 --- a/drivers/tty/serial/mpsc.c +++ b/drivers/tty/serial/mpsc.c @@ -754,9 +754,10 @@ static int mpsc_alloc_ring_mem(struct mpsc_port_info *pi) if (!dma_set_mask(pi->port.dev, 0xffffffff)) { printk(KERN_ERR "MPSC: Inadequate DMA support\n"); rc = -ENXIO; - } else if ((pi->dma_region = dma_alloc_noncoherent(pi->port.dev, + } else if ((pi->dma_region = dma_alloc_attrs(pi->port.dev, MPSC_DMA_ALLOC_SIZE, - &pi->dma_region_p, GFP_KERNEL)) + &pi->dma_region_p, GFP_KERNEL, + DMA_ATTR_NON_CONSISTENT)) == NULL) { printk(KERN_ERR "MPSC: Can't alloc Desc region\n"); rc = -ENOMEM; @@ -771,8 +772,9 @@ static void mpsc_free_ring_mem(struct mpsc_port_info *pi) pr_debug("mpsc_free_ring_mem[%d]: Freeing ring mem\n", pi->port.line); if (pi->dma_region) { - dma_free_noncoherent(pi->port.dev, MPSC_DMA_ALLOC_SIZE, - pi->dma_region, pi->dma_region_p); + dma_free_attrs(pi->port.dev, MPSC_DMA_ALLOC_SIZE, + pi->dma_region, pi->dma_region_p, + DMA_ATTR_NON_CONSISTENT); pi->dma_region = NULL; pi->dma_region_p = (dma_addr_t)NULL; } diff --git a/drivers/tty/serial/owl-uart.c b/drivers/tty/serial/owl-uart.c new file mode 100644 index 000000000000..1b8008797a1b --- /dev/null +++ b/drivers/tty/serial/owl-uart.c @@ -0,0 +1,135 @@ +/* + * Actions Semi Owl family serial console + * + * Copyright 2013 Actions Semi Inc. + * Author: Actions Semi, Inc. + * + * Copyright (c) 2016-2017 Andreas Färber + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/console.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/serial.h> +#include <linux/serial_core.h> + +#define OWL_UART_CTL 0x000 +#define OWL_UART_TXDAT 0x008 +#define OWL_UART_STAT 0x00c + +#define OWL_UART_CTL_TRFS_TX BIT(14) +#define OWL_UART_CTL_EN BIT(15) +#define OWL_UART_CTL_RXIE BIT(18) +#define OWL_UART_CTL_TXIE BIT(19) + +#define OWL_UART_STAT_RIP BIT(0) +#define OWL_UART_STAT_TIP BIT(1) +#define OWL_UART_STAT_TFFU BIT(6) +#define OWL_UART_STAT_TRFL_MASK (0x1f << 11) +#define OWL_UART_STAT_UTBB BIT(17) + +static inline void owl_uart_write(struct uart_port *port, u32 val, unsigned int off) +{ + writel(val, port->membase + off); +} + +static inline u32 owl_uart_read(struct uart_port *port, unsigned int off) +{ + return readl(port->membase + off); +} + +#ifdef CONFIG_SERIAL_OWL_CONSOLE + +static void owl_console_putchar(struct uart_port *port, int ch) +{ + if (!port->membase) + return; + + while (owl_uart_read(port, OWL_UART_STAT) & OWL_UART_STAT_TFFU) + cpu_relax(); + + owl_uart_write(port, ch, OWL_UART_TXDAT); +} + +static void owl_uart_port_write(struct uart_port *port, const char *s, + u_int count) +{ + u32 old_ctl, val; + unsigned long flags; + int locked; + + local_irq_save(flags); + + if (port->sysrq) + locked = 0; + else if (oops_in_progress) + locked = spin_trylock(&port->lock); + else { + spin_lock(&port->lock); + locked = 1; + } + + old_ctl = owl_uart_read(port, OWL_UART_CTL); + val = old_ctl | OWL_UART_CTL_TRFS_TX; + /* disable IRQ */ + val &= ~(OWL_UART_CTL_RXIE | OWL_UART_CTL_TXIE); + owl_uart_write(port, val, OWL_UART_CTL); + + uart_console_write(port, s, count, owl_console_putchar); + + /* wait until all contents have been sent out */ + while (owl_uart_read(port, OWL_UART_STAT) & OWL_UART_STAT_TRFL_MASK) + cpu_relax(); + + /* clear IRQ pending */ + val = owl_uart_read(port, OWL_UART_STAT); + val |= OWL_UART_STAT_TIP | OWL_UART_STAT_RIP; + owl_uart_write(port, val, OWL_UART_STAT); + + owl_uart_write(port, old_ctl, OWL_UART_CTL); + + if (locked) + spin_unlock(&port->lock); + + local_irq_restore(flags); +} + +static void owl_uart_early_console_write(struct console *co, + const char *s, + u_int count) +{ + struct earlycon_device *dev = co->data; + + owl_uart_port_write(&dev->port, s, count); +} + +static int __init +owl_uart_early_console_setup(struct earlycon_device *device, const char *opt) +{ + if (!device->port.membase) + return -ENODEV; + + device->con->write = owl_uart_early_console_write; + + return 0; +} +OF_EARLYCON_DECLARE(owl, "actions,owl-uart", + owl_uart_early_console_setup); + +#endif /* CONFIG_SERIAL_OWL_CONSOLE */ diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c index 42caccb5e87e..d3796dc26fa9 100644 --- a/drivers/tty/serial/pch_uart.c +++ b/drivers/tty/serial/pch_uart.c @@ -878,8 +878,7 @@ static int dma_handle_rx(struct eg20t_port *priv) sg_dma_len(sg) = priv->trigger_level; sg_set_page(&priv->sg_rx, virt_to_page(priv->rx_buf_virt), - sg_dma_len(sg), (unsigned long)priv->rx_buf_virt & - ~PAGE_MASK); + sg_dma_len(sg), offset_in_page(priv->rx_buf_virt)); sg_dma_address(sg) = priv->rx_buf_dma; diff --git a/drivers/tty/serial/sccnxp.c b/drivers/tty/serial/sccnxp.c index fcf803ffad19..cdd2f942317c 100644 --- a/drivers/tty/serial/sccnxp.c +++ b/drivers/tty/serial/sccnxp.c @@ -884,14 +884,19 @@ static int sccnxp_probe(struct platform_device *pdev) clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(clk)) { - if (PTR_ERR(clk) == -EPROBE_DEFER) { - ret = -EPROBE_DEFER; + ret = PTR_ERR(clk); + if (ret == -EPROBE_DEFER) goto err_out; - } + uartclk = 0; + } else { + clk_prepare_enable(clk); + uartclk = clk_get_rate(clk); + } + + if (!uartclk) { dev_notice(&pdev->dev, "Using default clock frequency\n"); uartclk = s->chip->freq_std; - } else - uartclk = clk_get_rate(clk); + } /* Check input frequency */ if ((uartclk < s->chip->freq_min) || (uartclk > s->chip->freq_max)) { diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 13bfd5dcffce..f534a40aebde 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -954,11 +954,10 @@ static int uart_set_info(struct tty_struct *tty, struct tty_port *port, old_custom_divisor != uport->custom_divisor) { /* * If they're setting up a custom divisor or speed, - * instead of clearing it, then bitch about it. No - * need to rate-limit; it's CAP_SYS_ADMIN only. + * instead of clearing it, then bitch about it. */ if (uport->flags & UPF_SPD_MASK) { - dev_notice(uport->dev, + dev_notice_ratelimited(uport->dev, "%s sets custom speed on %s. This is deprecated.\n", current->comm, tty_name(port->tty)); diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 71707e8e6e3f..da5ddfc14778 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1450,8 +1450,7 @@ static struct dma_chan *sci_request_dma_chan(struct uart_port *port, chan = dma_request_slave_channel(port->dev, dir == DMA_MEM_TO_DEV ? "tx" : "rx"); if (!chan) { - dev_warn(port->dev, - "dma_request_slave_channel_compat failed\n"); + dev_warn(port->dev, "dma_request_slave_channel failed\n"); return NULL; } @@ -1558,7 +1557,16 @@ static void sci_free_dma(struct uart_port *port) if (s->chan_rx) sci_rx_dma_release(s, false); } -#else + +static void sci_flush_buffer(struct uart_port *port) +{ + /* + * In uart_flush_buffer(), the xmit circular buffer has just been + * cleared, so we have to reset tx_dma_len accordingly. + */ + to_sci_port(port)->tx_dma_len = 0; +} +#else /* !CONFIG_SERIAL_SH_SCI_DMA */ static inline void sci_request_dma(struct uart_port *port) { } @@ -1566,7 +1574,9 @@ static inline void sci_request_dma(struct uart_port *port) static inline void sci_free_dma(struct uart_port *port) { } -#endif + +#define sci_flush_buffer NULL +#endif /* !CONFIG_SERIAL_SH_SCI_DMA */ static irqreturn_t sci_rx_interrupt(int irq, void *ptr) { @@ -2581,6 +2591,7 @@ static const struct uart_ops sci_uart_ops = { .break_ctl = sci_break_ctl, .startup = sci_startup, .shutdown = sci_shutdown, + .flush_buffer = sci_flush_buffer, .set_termios = sci_set_termios, .pm = sci_pm, .type = sci_type, @@ -2950,6 +2961,7 @@ static inline int sci_probe_earlyprintk(struct platform_device *pdev) static const char banner[] __initconst = "SuperH (H)SCI(F) driver initialized"; +static DEFINE_MUTEX(sci_uart_registration_lock); static struct uart_driver sci_uart_driver = { .owner = THIS_MODULE, .driver_name = "sci", @@ -3078,6 +3090,16 @@ static int sci_probe_single(struct platform_device *dev, return -EINVAL; } + mutex_lock(&sci_uart_registration_lock); + if (!sci_uart_driver.state) { + ret = uart_register_driver(&sci_uart_driver); + if (ret) { + mutex_unlock(&sci_uart_registration_lock); + return ret; + } + } + mutex_unlock(&sci_uart_registration_lock); + ret = sci_init_single(dev, sciport, index, p, false); if (ret) return ret; @@ -3201,24 +3223,17 @@ static struct platform_driver sci_driver = { static int __init sci_init(void) { - int ret; - pr_info("%s\n", banner); - ret = uart_register_driver(&sci_uart_driver); - if (likely(ret == 0)) { - ret = platform_driver_register(&sci_driver); - if (unlikely(ret)) - uart_unregister_driver(&sci_uart_driver); - } - - return ret; + return platform_driver_register(&sci_driver); } static void __exit sci_exit(void) { platform_driver_unregister(&sci_driver); - uart_unregister_driver(&sci_uart_driver); + + if (sci_uart_driver.state) + uart_unregister_driver(&sci_uart_driver); } #ifdef CONFIG_SERIAL_SH_SCI_CONSOLE diff --git a/drivers/tty/serial/sirfsoc_uart.c b/drivers/tty/serial/sirfsoc_uart.c index e03282d92b59..684cb8dd8050 100644 --- a/drivers/tty/serial/sirfsoc_uart.c +++ b/drivers/tty/serial/sirfsoc_uart.c @@ -1253,7 +1253,7 @@ next_hrt: return HRTIMER_RESTART; } -static struct of_device_id sirfsoc_uart_ids[] = { +static const struct of_device_id sirfsoc_uart_ids[] = { { .compatible = "sirf,prima2-uart", .data = &sirfsoc_uart,}, { .compatible = "sirf,atlas7-uart", .data = &sirfsoc_uart}, { .compatible = "sirf,prima2-usp-uart", .data = &sirfsoc_usp}, diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index c0539950f8d7..fde55dcdea5a 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -186,6 +186,7 @@ MODULE_PARM_DESC(rx_timeout, "Rx timeout, 1-255"); * @pclk: APB clock * @baud: Current baud rate * @clk_rate_change_nb: Notifier block for clock changes + * @quirks: Flags for RXBS support. */ struct cdns_uart { struct uart_port *port; @@ -1587,20 +1588,21 @@ static int cdns_uart_probe(struct platform_device *pdev) if (rc) { dev_err(&pdev->dev, "uart_add_one_port() failed; err=%i\n", rc); - goto err_out_notif_unreg; + goto err_out_pm_disable; } return 0; +err_out_pm_disable: + pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_dont_use_autosuspend(&pdev->dev); err_out_notif_unreg: #ifdef CONFIG_COMMON_CLK clk_notifier_unregister(cdns_uart_data->uartclk, &cdns_uart_data->clk_rate_change_nb); #endif err_out_clk_disable: - pm_runtime_disable(&pdev->dev); - pm_runtime_set_suspended(&pdev->dev); - pm_runtime_dont_use_autosuspend(&pdev->dev); clk_disable_unprepare(cdns_uart_data->uartclk); err_out_clk_dis_pclk: clk_disable_unprepare(cdns_uart_data->pclk); |