Merge tag 'tty-4.17-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 5 Apr 2018 01:43:49 +0000 (18:43 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 5 Apr 2018 01:43:49 +0000 (18:43 -0700)
Pull tty/serial driver updates from Greg KH:
 "Here is the big set of tty and serial driver patches for 4.17-rc1

  Not all that big really, most are just small fixes and additions to
  existing drivers. There's a bunch of work on the imx serial driver
  recently for some reason, and a new embedded serial driver added as
  well.

  Full details are in the shortlog.

  All of these have been in the linux-next tree for a while with no
  reported issues"

* tag 'tty-4.17-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty: (66 commits)
  serial: expose buf_overrun count through proc interface
  serial: mvebu-uart: fix tx lost characters
  tty: serial: msm_geni_serial: Fix return value check in qcom_geni_serial_probe()
  tty: serial: msm_geni_serial: Add serial driver support for GENI based QUP
  8250-men-mcb: add support for 16z025 and 16z057
  powerpc: Mark the variable earlycon_acpi_spcr_enable maybe_unused
  serial: stm32: fix initialization of RS485 mode
  ARM: dts: STi: Remove "console=ttyASN" from bootargs for STi boards
  vt: change SGR 21 to follow the standards
  serdev: Fix typo in serdev_device_alloc
  ARM: dts: STi: Fix aliases property name for STi boards
  tty: st-asc: Update tty alias
  serial: stm32: add support for RS485 hardware control mode
  dt-bindings: serial: stm32: add RS485 optional properties
  selftests: add devpts selftests
  devpts: comment devpts_mntget()
  devpts: resolve devpts bind-mounts
  devpts: hoist out check for DEVPTS_SUPER_MAGIC
  serial: 8250: Add Nuvoton NPCM UART
  serial: mxs-auart: disable clks of Alphascale ASM9260
  ...

53 files changed:
Documentation/devicetree/bindings/serial/8250.txt
Documentation/devicetree/bindings/serial/renesas,sci-serial.txt
Documentation/devicetree/bindings/serial/st,stm32-usart.txt
arch/arm/boot/dts/stih407-b2120.dts
arch/arm/boot/dts/stih410-b2120.dts
arch/arm/boot/dts/stih410-b2260.dts
arch/arm/boot/dts/stih418-b2199.dts
drivers/parport/parport_serial.c
drivers/tty/hvc/Kconfig
drivers/tty/hvc/Makefile
drivers/tty/hvc/hvc_riscv_sbi.c [new file with mode: 0644]
drivers/tty/serdev/core.c
drivers/tty/serial/8250/8250_dw.c
drivers/tty/serial/8250/8250_men_mcb.c
drivers/tty/serial/8250/8250_of.c
drivers/tty/serial/8250/8250_omap.c
drivers/tty/serial/8250/8250_pci.c
drivers/tty/serial/8250/8250_port.c
drivers/tty/serial/8250/Kconfig
drivers/tty/serial/Kconfig
drivers/tty/serial/Makefile
drivers/tty/serial/altera_uart.c
drivers/tty/serial/arc_uart.c
drivers/tty/serial/fsl_lpuart.c
drivers/tty/serial/imx.c
drivers/tty/serial/max310x.c
drivers/tty/serial/mvebu-uart.c
drivers/tty/serial/mxs-auart.c
drivers/tty/serial/pxa.c
drivers/tty/serial/qcom_geni_serial.c [new file with mode: 0644]
drivers/tty/serial/samsung.c
drivers/tty/serial/serial_core.c
drivers/tty/serial/sh-sci.c
drivers/tty/serial/sirfsoc_uart.c
drivers/tty/serial/st-asc.c
drivers/tty/serial/stm32-usart.c
drivers/tty/serial/stm32-usart.h
drivers/tty/serial/xilinx_uartps.c
drivers/tty/vt/vt.c
drivers/usb/misc/sisusbvga/sisusb_con.c
drivers/video/console/dummycon.c
drivers/video/console/newport_con.c
drivers/video/console/vgacon.c
drivers/video/fbdev/core/fbcon.c
fs/devpts/inode.c
include/linux/console.h
include/linux/pci_ids.h
include/linux/serial_core.h
include/uapi/linux/serial_core.h
tools/testing/selftests/Makefile
tools/testing/selftests/filesystems/.gitignore
tools/testing/selftests/filesystems/Makefile
tools/testing/selftests/filesystems/devpts_pts.c [new file with mode: 0644]

index dad3b2e..aeb6db4 100644 (file)
@@ -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.
index cf504d0..ad962f4 100644 (file)
@@ -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.
index d150b04..9d3efed 100644 (file)
@@ -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"
 
index c8ad905..a7a0f76 100644 (file)
@@ -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 = &ethernet0;
        };
 
index 9830be5..6c6b4cc 100644 (file)
@@ -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 = &ethernet0;
        };
 
index c663b70..50d3675 100644 (file)
@@ -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 = &ethernet0;
        };
 
index 4e6d915..7f5f325 100644 (file)
@@ -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 = &ethernet0;
        };
 
index e15b484..087e847 100644 (file)
@@ -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,
index 3bade5a..4293c17 100644 (file)
@@ -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
index 7da1934..98880e3 100644 (file)
@@ -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 (file)
index 0000000..75155bd
--- /dev/null
@@ -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);
index f439c72..df93b72 100644 (file)
@@ -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
index cd1b94a..6fcdb90 100644 (file)
@@ -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 */
index 3089778..127017c 100644 (file)
@@ -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");
index 160b890..9835b1c 100644 (file)
@@ -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);
index 57f6eba..624b501 100644 (file)
@@ -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);
 }
index a93f77a..3296a05 100644 (file)
@@ -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
index 1328c7e..95833cb 100644 (file)
 #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);
 
index 16b1496..f005eaf 100644 (file)
@@ -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.
index 736720a..0f058df 100644 (file)
@@ -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
index b30ee2e..daac675 100644 (file)
@@ -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
index b88b05f..0e487ce 100644 (file)
@@ -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;
index 2599f9e..d904a3a 100644 (file)
@@ -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;
 
index 8cf112f..51e47a6 100644 (file)
@@ -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);
index a33c685..91f3a1a 100644 (file)
 #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");
index 39f6358..efe55a1 100644 (file)
@@ -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;
index a100e98..750e564 100644 (file)
@@ -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)
index 079dc47..76aa289 100644 (file)
@@ -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;
 }
index baf5529..eda3c77 100644 (file)
@@ -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 (file)
index 0000000..65ff669
--- /dev/null
@@ -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");
index f9fecc5..3f2f8c1 100644 (file)
@@ -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);
index 35b9201..0466f9f 100644 (file)
@@ -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)) \
index 44adf9d..fdbbff5 100644 (file)
@@ -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;
index 9925b00..38622f2 100644 (file)
@@ -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;
index c763253..5f9f01f 100644 (file)
@@ -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;
index 0fa735b..e8d7a7b 100644 (file)
@@ -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;
 
index 2294d0f..6f294e2 100644 (file)
@@ -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 */
index b9b2bc7..abcb4d0 100644 (file)
@@ -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)
index b4e57c5..f97251f 100644 (file)
@@ -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;
index 73f7bde..a0d6e0a 100644 (file)
@@ -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
index b90ef96..f2eafe2 100644 (file)
@@ -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);
index 42d02a2..7f2526b 100644 (file)
@@ -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,
index a17ba14..f09e17b 100644 (file)
@@ -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,
index 5baf7bc..3e330e0 100644 (file)
@@ -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;
index e31d6ed..e072e95 100644 (file)
@@ -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;
+               }
        }
 
        /*
index b8920a0..dfd6b0e 100644 (file)
@@ -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;
index a637a7d..44f74b5 100644 (file)
 #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
 
index b32df49..1d35610 100644 (file)
@@ -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
 
index 1c8413f..dce5f9d 100644 (file)
@@ -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
index 7442dfb..dbda89c 100644 (file)
@@ -7,6 +7,7 @@ TARGETS += cpufreq
 TARGETS += cpu-hotplug
 TARGETS += efivarfs
 TARGETS += exec
+TARGETS += filesystems
 TARGETS += firmware
 TARGETS += ftrace
 TARGETS += futex
index 13a73bf..4e6d09f 100644 (file)
@@ -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 (file)
index 0000000..b9055e9
--- /dev/null
@@ -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);
+}