]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/commitdiff
Merge tag 'tty-3.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty
authorLinus Torvalds <torvalds@linux-foundation.org>
Sun, 15 Feb 2015 19:37:02 +0000 (11:37 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sun, 15 Feb 2015 19:37:02 +0000 (11:37 -0800)
Pull tty/serial driver patches from Greg KH:
 "Here's the big tty/serial driver update for 3.20-rc1.  Nothing huge
  here, just lots of driver updates and some core tty layer fixes as
  well.  All have been in linux-next with no reported issues"

* tag 'tty-3.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty: (119 commits)
  serial: 8250: Fix UART_BUG_TXEN workaround
  serial: driver for ETRAX FS UART
  tty: remove unused variable sprop
  serial: of-serial: fetch line number from DT
  serial: samsung: earlycon support depends on CONFIG_SERIAL_SAMSUNG_CONSOLE
  tty/serial: serial8250_set_divisor() can be static
  tty/serial: Add Spreadtrum sc9836-uart driver support
  Documentation: DT: Add bindings for Spreadtrum SoC Platform
  serial: samsung: remove redundant interrupt enabling
  tty: Remove external interface for tty_set_termios()
  serial: omap: Fix RTS handling
  serial: 8250_omap: Use UPSTAT_AUTORTS for RTS handling
  serial: core: Rework hw-assisted flow control support
  tty/serial: 8250_early: Add support for PXA UARTs
  tty/serial: of_serial: add support for PXA/MMP uarts
  tty/serial: of_serial: add DT alias ID handling
  serial: 8250: Prevent concurrent updates to shadow registers
  serial: 8250: Use canary to restart console after suspend
  serial: 8250: Refactor XR17V35X divisor calculation
  serial: 8250: Refactor divisor programming
  ...

73 files changed:
Documentation/devicetree/bindings/arm/sprd.txt [new file with mode: 0644]
Documentation/devicetree/bindings/serial/digicolor-usart.txt [new file with mode: 0644]
Documentation/devicetree/bindings/serial/sirf-uart.txt
Documentation/devicetree/bindings/serial/sprd-uart.txt [new file with mode: 0644]
Documentation/devicetree/bindings/vendor-prefixes.txt
Documentation/kernel-parameters.txt
arch/arm/boot/dts/exynos4210-origen.dts
arch/arm/boot/dts/exynos4210-smdkv310.dts
arch/arm/boot/dts/exynos4210-trats.dts
arch/arm/boot/dts/exynos4210-universal_c210.dts
arch/arm/boot/dts/exynos4412-odroid-common.dtsi
arch/arm/boot/dts/exynos4412-origen.dts
arch/arm/boot/dts/exynos4412-smdk4412.dts
arch/arm/boot/dts/exynos4412-tiny4412.dts
arch/arm/boot/dts/exynos4412-trats2.dts
arch/x86/include/asm/intel-mid.h
arch/x86/kernel/early_printk.c
arch/x86/platform/intel-mid/device_libs/Makefile
arch/x86/platform/intel-mid/device_libs/platform_max3111.c [deleted file]
arch/x86/platform/intel-mid/early_printk_intel_mid.c
drivers/bluetooth/hci_ath.c
drivers/pnp/driver.c
drivers/tty/amiserial.c
drivers/tty/ehv_bytechan.c
drivers/tty/isicom.c
drivers/tty/n_tty.c
drivers/tty/pty.c
drivers/tty/rocket.c
drivers/tty/serial/8250/8250_core.c
drivers/tty/serial/8250/8250_dma.c
drivers/tty/serial/8250/8250_dw.c
drivers/tty/serial/8250/8250_early.c
drivers/tty/serial/8250/8250_omap.c
drivers/tty/serial/8250/8250_pci.c
drivers/tty/serial/8250/8250_pnp.c
drivers/tty/serial/8250/Kconfig
drivers/tty/serial/Kconfig
drivers/tty/serial/Makefile
drivers/tty/serial/altera_jtaguart.c
drivers/tty/serial/altera_uart.c
drivers/tty/serial/atmel_serial.c
drivers/tty/serial/digicolor-usart.c [new file with mode: 0644]
drivers/tty/serial/etraxfs-uart.c [new file with mode: 0644]
drivers/tty/serial/fsl_lpuart.c
drivers/tty/serial/imx.c
drivers/tty/serial/mcf.c
drivers/tty/serial/men_z135_uart.c
drivers/tty/serial/mrst_max3110.c [deleted file]
drivers/tty/serial/mrst_max3110.h [deleted file]
drivers/tty/serial/msm_serial.c
drivers/tty/serial/mxs-auart.c
drivers/tty/serial/of_serial.c
drivers/tty/serial/omap-serial.c
drivers/tty/serial/samsung.c
drivers/tty/serial/samsung.h
drivers/tty/serial/serial_core.c
drivers/tty/serial/sh-sci.c
drivers/tty/serial/sirfsoc_uart.c
drivers/tty/serial/sirfsoc_uart.h
drivers/tty/serial/sprd_serial.c [new file with mode: 0644]
drivers/tty/serial/xilinx_uartps.c
drivers/tty/tty_buffer.c
drivers/tty/tty_ioctl.c
drivers/tty/tty_mutex.c
drivers/tty/vt/vt.c
include/linux/pnp.h
include/linux/serial_8250.h
include/linux/serial_core.h
include/linux/serial_s3c.h
include/linux/tty.h
include/linux/vt_buffer.h
include/uapi/linux/serial_core.h
include/uapi/linux/serial_reg.h

diff --git a/Documentation/devicetree/bindings/arm/sprd.txt b/Documentation/devicetree/bindings/arm/sprd.txt
new file mode 100644 (file)
index 0000000..31a629d
--- /dev/null
@@ -0,0 +1,11 @@
+Spreadtrum SoC Platforms Device Tree Bindings
+----------------------------------------------------
+
+Sharkl64 is a Spreadtrum's SoC Platform which is based
+on ARM 64-bit processor.
+
+SC9836 openphone board with SC9836 SoC based on the
+Sharkl64 Platform shall have the following properties.
+
+Required root node properties:
+        - compatible = "sprd,sc9836-openphone", "sprd,sc9836";
diff --git a/Documentation/devicetree/bindings/serial/digicolor-usart.txt b/Documentation/devicetree/bindings/serial/digicolor-usart.txt
new file mode 100644 (file)
index 0000000..2d3ede6
--- /dev/null
@@ -0,0 +1,27 @@
+Binding for Conexant Digicolor USART
+
+Note: this binding is only applicable for using the USART peripheral as
+UART. USART also support synchronous serial protocols like SPI and I2S. Use
+the binding that matches the wiring of your system.
+
+Required properties:
+- compatible : should be "cnxt,cx92755-usart".
+- reg: Should contain USART controller registers location and length.
+- interrupts: Should contain a single USART controller interrupt.
+- clocks: Must contain phandles to the USART clock
+  See ../clocks/clock-bindings.txt for details.
+
+Note: Each UART port should have an alias correctly numbered
+in "aliases" node.
+
+Example:
+       aliases {
+               serial0 = &uart0;
+       };
+
+       uart0: uart@f0000740 {
+               compatible = "cnxt,cx92755-usart";
+               reg = <0xf0000740 0x20>;
+               clocks = <&main_clk>;
+               interrupts = <44>;
+       };
index 3acdd969edf14a1252cabc988cf0df38785ae768..f0c39261c5d4443927737eee98c73076fb0632ad 100644 (file)
@@ -2,7 +2,7 @@
 
 Required properties:
 - compatible : Should be "sirf,prima2-uart", "sirf, prima2-usp-uart",
-               "sirf,marco-uart" or "sirf,marco-bt-uart" which means
+               "sirf,atlas7-uart" or "sirf,atlas7-bt-uart" which means
                uart located in BT module and used for BT.
 - reg : Offset and length of the register set for the device
 - interrupts : Should contain uart interrupt
@@ -37,7 +37,7 @@ usp@b0090000 {
 for uart use in BT module,
 uart6: uart@11000000 {
        cell-index = <6>;
-       compatible = "sirf,marco-bt-uart", "sirf,marco-uart";
+       compatible = "sirf,atlas7-bt-uart", "sirf,atlas7-uart";
        reg = <0x11000000 0x1000>;
        interrupts = <0 100 0>;
        clocks = <&clks 138>, <&clks 140>, <&clks 141>;
diff --git a/Documentation/devicetree/bindings/serial/sprd-uart.txt b/Documentation/devicetree/bindings/serial/sprd-uart.txt
new file mode 100644 (file)
index 0000000..2aff0f2
--- /dev/null
@@ -0,0 +1,7 @@
+* Spreadtrum serial UART
+
+Required properties:
+- compatible: must be "sprd,sc9836-uart"
+- reg: offset and length of the register set for the device
+- interrupts: exactly one interrupt specifier
+- clocks: phandles to input clocks.
index 1eb6444a48c4eeb06c124d01b3783576b2c6fdc3..cba613f8aa4f351af7c807fb5115d14c4a847672 100644 (file)
@@ -37,6 +37,7 @@ chrp  Common Hardware Reference Platform
 chunghwa       Chunghwa Picture Tubes Ltd.
 cirrus Cirrus Logic, Inc.
 cnm    Chips&Media, Inc.
+cnxt   Conexant Systems, Inc.
 cortina        Cortina Systems, Inc.
 cosmic Cosmic Circuits
 crystalfontz   Crystalfontz America, Inc.
@@ -162,6 +163,7 @@ snps        Synopsys, Inc.
 solidrun       SolidRun
 sony   Sony Corporation
 spansion       Spansion Inc.
+sprd   Spreadtrum Communications Inc.
 st     STMicroelectronics
 ste    ST-Ericsson
 stericsson     ST-Ericsson
index a89e326375702dae82244472e9955564e2949060..bfcb1a62a7b48466f3cbe5a08d3e1a632eadc3d2 100644 (file)
@@ -970,6 +970,18 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
 
                smh     Use ARM semihosting calls for early console.
 
+               s3c2410,<addr>
+               s3c2412,<addr>
+               s3c2440,<addr>
+               s3c6400,<addr>
+               s5pv210,<addr>
+               exynos4210,<addr>
+                       Use early console provided by serial driver available
+                       on Samsung SoCs, requires selecting proper type and
+                       a correct base address of the selected UART port. The
+                       serial port must already be setup and configured.
+                       Options are not yet supported.
+
        earlyprintk=    [X86,SH,BLACKFIN,ARM,M68k]
                        earlyprintk=vga
                        earlyprintk=efi
index f767c425d0b5d277a369e4d1101efe26a208d3fb..b811461414023fa293c75130d30d840fa4e5d3af 100644 (file)
@@ -31,6 +31,7 @@
 
        chosen {
                bootargs ="root=/dev/ram0 rw ramdisk=8192 initrd=0x41000000,8M console=ttySAC2,115200 init=/linuxrc";
+               stdout-path = &serial_2;
        };
 
        regulators {
index 676e6e0c8cf396b562bc3971db21ef93deb35406..86216fff1b4f42db7b4e7fb2cfc5beed60b30d39 100644 (file)
@@ -27,6 +27,7 @@
 
        chosen {
                bootargs = "root=/dev/ram0 rw ramdisk=8192 initrd=0x41000000,8M console=ttySAC1,115200 init=/linuxrc";
+               stdout-path = &serial_1;
        };
 
        sdhci@12530000 {
index 720836205546f22dd078d590aef80664d2af7011..a406df3d6df87e3c223ef5bd5138da1de831834c 100644 (file)
@@ -28,6 +28,7 @@
 
        chosen {
                bootargs = "console=ttySAC2,115200N8 root=/dev/mmcblk0p5 rootwait earlyprintk panic=5";
+               stdout-path = &serial_2;
        };
 
        regulators {
index aaf0cae4f5e87bd976d34a591f52f11b5f4990c5..6effb13f98a6c268c593da22edc1664bfdd258ff 100644 (file)
@@ -26,6 +26,7 @@
 
        chosen {
                bootargs = "console=ttySAC2,115200N8 root=/dev/mmcblk0p5 rw rootwait earlyprintk panic=5 maxcpus=1";
+               stdout-path = &serial_2;
        };
 
        sysram@02020000 {
index abd63366298a812c2df659006c726b6345bfb444..2c43d185930859dc90f62101beb101351740b563 100644 (file)
 #include "exynos4412.dtsi"
 
 / {
+       chosen {
+               stdout-path = &serial_1;
+       };
+
        firmware@0204F000 {
                compatible = "samsung,secure-firmware";
                reg = <0x0204F000 0x1000>;
index de15114fd07cafa2ef7bc4b39f41196ac9fd2337..bd8b73077d41faed72e4465280dc3f58c50b7385 100644 (file)
@@ -26,6 +26,7 @@
 
        chosen {
                bootargs ="console=ttySAC2,115200";
+               stdout-path = &serial_2;
        };
 
        firmware@0203F000 {
index ded0b70f764485bc0ab4fc6e4cc23f895b509f4b..b9256afbcc683ecdc3df2daf3d7276eee7fc1a2e 100644 (file)
@@ -25,6 +25,7 @@
 
        chosen {
                bootargs ="root=/dev/ram0 rw ramdisk=8192 initrd=0x41000000,8M console=ttySAC1,115200 init=/linuxrc";
+               stdout-path = &serial_1;
        };
 
        g2d@10800000 {
index ea6929d9c6212ff75943df09cee7fb49cc196020..d46fd4c2aeaa5a04bc4ba143150fe50626d0473b 100644 (file)
        model = "FriendlyARM TINY4412 board based on Exynos4412";
        compatible = "friendlyarm,tiny4412", "samsung,exynos4412", "samsung,exynos4";
 
+       chosen {
+               stdout-path = &serial_0;
+       };
+
        memory {
                reg = <0x40000000 0x40000000>;
        };
index 29231b4526433ef0fbae30e81d81088671dc3ac1..5fbb01335a0f6d7576e1b56caae212068f50316e 100644 (file)
@@ -32,6 +32,7 @@
 
        chosen {
                bootargs = "console=ttySAC2,115200N8 root=/dev/mmcblk0p5 rootwait earlyprintk panic=5";
+               stdout-path = &serial_2;
        };
 
        firmware@0204F000 {
index e34e097b6f9de43598eefc8a4894804be0b51256..705d35708a508cd520d579d888719bc296e563b9 100644 (file)
@@ -136,9 +136,6 @@ extern enum intel_mid_timer_options intel_mid_timer_options;
 #define SFI_MTMR_MAX_NUM 8
 #define SFI_MRTC_MAX   8
 
-extern struct console early_mrst_console;
-extern void mrst_early_console_init(void);
-
 extern struct console early_hsu_console;
 extern void hsu_early_console_init(const char *);
 
index 01d1c187c9f9468f0488fa498d44e814ca9a2179..a62536a1be889019081696b2e7df908b0a4ef9fb 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/usb/ehci_def.h>
 #include <linux/efi.h>
 #include <asm/efi.h>
+#include <asm/pci_x86.h>
 
 /* Simple VGA output */
 #define VGABASE                (__ISA_IO_base + 0xb8000)
@@ -76,7 +77,7 @@ static struct console early_vga_console = {
 
 /* Serial functions loosely based on a similar package from Klaus P. Gerlicher */
 
-static int early_serial_base = 0x3f8;  /* ttyS0 */
+static unsigned long early_serial_base = 0x3f8;  /* ttyS0 */
 
 #define XMTRDY          0x20
 
@@ -94,13 +95,40 @@ static int early_serial_base = 0x3f8;  /* ttyS0 */
 #define DLL             0       /*  Divisor Latch Low         */
 #define DLH             1       /*  Divisor latch High        */
 
+static void mem32_serial_out(unsigned long addr, int offset, int value)
+{
+       uint32_t *vaddr = (uint32_t *)addr;
+       /* shift implied by pointer type */
+       writel(value, vaddr + offset);
+}
+
+static unsigned int mem32_serial_in(unsigned long addr, int offset)
+{
+       uint32_t *vaddr = (uint32_t *)addr;
+       /* shift implied by pointer type */
+       return readl(vaddr + offset);
+}
+
+static unsigned int io_serial_in(unsigned long addr, int offset)
+{
+       return inb(addr + offset);
+}
+
+static void io_serial_out(unsigned long addr, int offset, int value)
+{
+       outb(value, addr + offset);
+}
+
+static unsigned int (*serial_in)(unsigned long addr, int offset) = io_serial_in;
+static void (*serial_out)(unsigned long addr, int offset, int value) = io_serial_out;
+
 static int early_serial_putc(unsigned char ch)
 {
        unsigned timeout = 0xffff;
 
-       while ((inb(early_serial_base + LSR) & XMTRDY) == 0 && --timeout)
+       while ((serial_in(early_serial_base, LSR) & XMTRDY) == 0 && --timeout)
                cpu_relax();
-       outb(ch, early_serial_base + TXR);
+       serial_out(early_serial_base, TXR, ch);
        return timeout ? 0 : -1;
 }
 
@@ -114,13 +142,28 @@ static void early_serial_write(struct console *con, const char *s, unsigned n)
        }
 }
 
+static __init void early_serial_hw_init(unsigned divisor)
+{
+       unsigned char c;
+
+       serial_out(early_serial_base, LCR, 0x3);        /* 8n1 */
+       serial_out(early_serial_base, IER, 0);  /* no interrupt */
+       serial_out(early_serial_base, FCR, 0);  /* no fifo */
+       serial_out(early_serial_base, MCR, 0x3);        /* DTR + RTS */
+
+       c = serial_in(early_serial_base, LCR);
+       serial_out(early_serial_base, LCR, c | DLAB);
+       serial_out(early_serial_base, DLL, divisor & 0xff);
+       serial_out(early_serial_base, DLH, (divisor >> 8) & 0xff);
+       serial_out(early_serial_base, LCR, c & ~DLAB);
+}
+
 #define DEFAULT_BAUD 9600
 
 static __init void early_serial_init(char *s)
 {
-       unsigned char c;
        unsigned divisor;
-       unsigned baud = DEFAULT_BAUD;
+       unsigned long baud = DEFAULT_BAUD;
        char *e;
 
        if (*s == ',')
@@ -145,24 +188,124 @@ static __init void early_serial_init(char *s)
                        s++;
        }
 
-       outb(0x3, early_serial_base + LCR);     /* 8n1 */
-       outb(0, early_serial_base + IER);       /* no interrupt */
-       outb(0, early_serial_base + FCR);       /* no fifo */
-       outb(0x3, early_serial_base + MCR);     /* DTR + RTS */
+       if (*s) {
+               if (kstrtoul(s, 0, &baud) < 0 || baud == 0)
+                       baud = DEFAULT_BAUD;
+       }
+
+       /* Convert from baud to divisor value */
+       divisor = 115200 / baud;
+
+       /* These will always be IO based ports */
+       serial_in = io_serial_in;
+       serial_out = io_serial_out;
+
+       /* Set up the HW */
+       early_serial_hw_init(divisor);
+}
+
+#ifdef CONFIG_PCI
+/*
+ * early_pci_serial_init()
+ *
+ * This function is invoked when the early_printk param starts with "pciserial"
+ * The rest of the param should be ",B:D.F,baud" where B, D & F describe the
+ * location of a PCI device that must be a UART device.
+ */
+static __init void early_pci_serial_init(char *s)
+{
+       unsigned divisor;
+       unsigned long baud = DEFAULT_BAUD;
+       u8 bus, slot, func;
+       uint32_t classcode, bar0;
+       uint16_t cmdreg;
+       char *e;
+
+
+       /*
+        * First, part the param to get the BDF values
+        */
+       if (*s == ',')
+               ++s;
+
+       if (*s == 0)
+               return;
+
+       bus = (u8)simple_strtoul(s, &e, 16);
+       s = e;
+       if (*s != ':')
+               return;
+       ++s;
+       slot = (u8)simple_strtoul(s, &e, 16);
+       s = e;
+       if (*s != '.')
+               return;
+       ++s;
+       func = (u8)simple_strtoul(s, &e, 16);
+       s = e;
+
+       /* A baud might be following */
+       if (*s == ',')
+               s++;
+
+       /*
+        * Second, find the device from the BDF
+        */
+       cmdreg = read_pci_config(bus, slot, func, PCI_COMMAND);
+       classcode = read_pci_config(bus, slot, func, PCI_CLASS_REVISION);
+       bar0 = read_pci_config(bus, slot, func, PCI_BASE_ADDRESS_0);
+
+       /*
+        * Verify it is a UART type device
+        */
+       if (((classcode >> 16 != PCI_CLASS_COMMUNICATION_MODEM) &&
+            (classcode >> 16 != PCI_CLASS_COMMUNICATION_SERIAL)) ||
+          (((classcode >> 8) & 0xff) != 0x02)) /* 16550 I/F at BAR0 */
+               return;
+
+       /*
+        * Determine if it is IO or memory mapped
+        */
+       if (bar0 & 0x01) {
+               /* it is IO mapped */
+               serial_in = io_serial_in;
+               serial_out = io_serial_out;
+               early_serial_base = bar0&0xfffffffc;
+               write_pci_config(bus, slot, func, PCI_COMMAND,
+                                               cmdreg|PCI_COMMAND_IO);
+       } else {
+               /* It is memory mapped - assume 32-bit alignment */
+               serial_in = mem32_serial_in;
+               serial_out = mem32_serial_out;
+               /* WARNING! assuming the address is always in the first 4G */
+               early_serial_base =
+                       (unsigned long)early_ioremap(bar0 & 0xfffffff0, 0x10);
+               write_pci_config(bus, slot, func, PCI_COMMAND,
+                                               cmdreg|PCI_COMMAND_MEMORY);
+       }
 
+       /*
+        * Lastly, initalize the hardware
+        */
        if (*s) {
-               baud = simple_strtoul(s, &e, 0);
-               if (baud == 0 || s == e)
+               if (strcmp(s, "nocfg") == 0)
+                       /* Sometimes, we want to leave the UART alone
+                        * and assume the BIOS has set it up correctly.
+                        * "nocfg" tells us this is the case, and we
+                        * should do no more setup.
+                        */
+                       return;
+               if (kstrtoul(s, 0, &baud) < 0 || baud == 0)
                        baud = DEFAULT_BAUD;
        }
 
+       /* Convert from baud to divisor value */
        divisor = 115200 / baud;
-       c = inb(early_serial_base + LCR);
-       outb(c | DLAB, early_serial_base + LCR);
-       outb(divisor & 0xff, early_serial_base + DLL);
-       outb((divisor >> 8) & 0xff, early_serial_base + DLH);
-       outb(c & ~DLAB, early_serial_base + LCR);
+
+       /* Set up the HW */
+       early_serial_hw_init(divisor);
 }
+#endif
 
 static struct console early_serial_console = {
        .name =         "earlyser",
@@ -210,6 +353,13 @@ static int __init setup_early_printk(char *buf)
                        early_serial_init(buf + 4);
                        early_console_register(&early_serial_console, keep);
                }
+#ifdef CONFIG_PCI
+               if (!strncmp(buf, "pciserial", 9)) {
+                       early_pci_serial_init(buf + 9);
+                       early_console_register(&early_serial_console, keep);
+                       buf += 9; /* Keep from match the above "serial" */
+               }
+#endif
                if (!strncmp(buf, "vga", 3) &&
                    boot_params.screen_info.orig_video_isVGA == 1) {
                        max_xpos = boot_params.screen_info.orig_video_cols;
@@ -226,11 +376,6 @@ static int __init setup_early_printk(char *buf)
                        early_console_register(&xenboot_console, keep);
 #endif
 #ifdef CONFIG_EARLY_PRINTK_INTEL_MID
-               if (!strncmp(buf, "mrst", 4)) {
-                       mrst_early_console_init();
-                       early_console_register(&early_mrst_console, keep);
-               }
-
                if (!strncmp(buf, "hsu", 3)) {
                        hsu_early_console_init(buf + 3);
                        early_console_register(&early_hsu_console, keep);
index af9307f2cc28eec45f3831f60f6284615d70757a..91ec9f8704bfd418683f1fe1c760c10da6d10afe 100644 (file)
@@ -16,8 +16,6 @@ obj-$(subst m,y,$(CONFIG_INPUT_MPU3050)) += platform_mpu3050.o
 obj-$(subst m,y,$(CONFIG_INPUT_BMA150)) += platform_bma023.o
 obj-$(subst m,y,$(CONFIG_GPIO_PCA953X)) += platform_tca6416.o
 obj-$(subst m,y,$(CONFIG_DRM_MEDFIELD)) += platform_tc35876x.o
-# SPI Devices
-obj-$(subst m,y,$(CONFIG_SERIAL_MRST_MAX3110)) += platform_max3111.o
 # MISC Devices
 obj-$(subst m,y,$(CONFIG_KEYBOARD_GPIO)) += platform_gpio_keys.o
 obj-$(subst m,y,$(CONFIG_INTEL_MID_WATCHDOG)) += platform_wdt.o
diff --git a/arch/x86/platform/intel-mid/device_libs/platform_max3111.c b/arch/x86/platform/intel-mid/device_libs/platform_max3111.c
deleted file mode 100644 (file)
index afd1df9..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * platform_max3111.c: max3111 platform data initilization file
- *
- * (C) Copyright 2013 Intel Corporation
- * Author: Sathyanarayanan Kuppuswamy <sathyanarayanan.kuppuswamy@intel.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; version 2
- * of the License.
- */
-
-#include <linux/gpio.h>
-#include <linux/spi/spi.h>
-#include <asm/intel-mid.h>
-
-static void __init *max3111_platform_data(void *info)
-{
-       struct spi_board_info *spi_info = info;
-       int intr = get_gpio_by_name("max3111_int");
-
-       spi_info->mode = SPI_MODE_0;
-       if (intr == -1)
-               return NULL;
-       spi_info->irq = intr + INTEL_MID_IRQ_OFFSET;
-       return NULL;
-}
-
-static const struct devs_id max3111_dev_id __initconst = {
-       .name = "spi_max3111",
-       .type = SFI_DEV_TYPE_SPI,
-       .get_platform_data = &max3111_platform_data,
-};
-
-sfi_device(max3111_dev_id);
index e0bd082a80e00f15db9ab0435818eb35dbd59926..4e720829ab904d151a9cca50193052fc51e97513 100644 (file)
  */
 
 /*
- * This file implements two early consoles named mrst and hsu.
- * mrst is based on Maxim3110 spi-uart device, it exists in both
- * Moorestown and Medfield platforms, while hsu is based on a High
- * Speed UART device which only exists in the Medfield platform
+ * This file implements early console named hsu.
+ * hsu is based on a High Speed UART device which only exists in the Medfield
+ * platform
  */
 
 #include <linux/serial_reg.h>
 #include <linux/serial_mfd.h>
-#include <linux/kmsg_dump.h>
 #include <linux/console.h>
 #include <linux/kernel.h>
 #include <linux/delay.h>
 #include <asm/pgtable.h>
 #include <asm/intel-mid.h>
 
-#define MRST_SPI_TIMEOUT               0x200000
-#define MRST_REGBASE_SPI0              0xff128000
-#define MRST_REGBASE_SPI1              0xff128400
-#define MRST_CLK_SPI0_REG              0xff11d86c
-
-/* Bit fields in CTRLR0 */
-#define SPI_DFS_OFFSET                 0
-
-#define SPI_FRF_OFFSET                 4
-#define SPI_FRF_SPI                    0x0
-#define SPI_FRF_SSP                    0x1
-#define SPI_FRF_MICROWIRE              0x2
-#define SPI_FRF_RESV                   0x3
-
-#define SPI_MODE_OFFSET                        6
-#define SPI_SCPH_OFFSET                        6
-#define SPI_SCOL_OFFSET                        7
-#define SPI_TMOD_OFFSET                        8
-#define        SPI_TMOD_TR                     0x0             /* xmit & recv */
-#define SPI_TMOD_TO                    0x1             /* xmit only */
-#define SPI_TMOD_RO                    0x2             /* recv only */
-#define SPI_TMOD_EPROMREAD             0x3             /* eeprom read mode */
-
-#define SPI_SLVOE_OFFSET               10
-#define SPI_SRL_OFFSET                 11
-#define SPI_CFS_OFFSET                 12
-
-/* Bit fields in SR, 7 bits */
-#define SR_MASK                                0x7f            /* cover 7 bits */
-#define SR_BUSY                                (1 << 0)
-#define SR_TF_NOT_FULL                 (1 << 1)
-#define SR_TF_EMPT                     (1 << 2)
-#define SR_RF_NOT_EMPT                 (1 << 3)
-#define SR_RF_FULL                     (1 << 4)
-#define SR_TX_ERR                      (1 << 5)
-#define SR_DCOL                                (1 << 6)
-
-struct dw_spi_reg {
-       u32     ctrl0;
-       u32     ctrl1;
-       u32     ssienr;
-       u32     mwcr;
-       u32     ser;
-       u32     baudr;
-       u32     txfltr;
-       u32     rxfltr;
-       u32     txflr;
-       u32     rxflr;
-       u32     sr;
-       u32     imr;
-       u32     isr;
-       u32     risr;
-       u32     txoicr;
-       u32     rxoicr;
-       u32     rxuicr;
-       u32     msticr;
-       u32     icr;
-       u32     dmacr;
-       u32     dmatdlr;
-       u32     dmardlr;
-       u32     idr;
-       u32     version;
-
-       /* Currently operates as 32 bits, though only the low 16 bits matter */
-       u32     dr;
-} __packed;
-
-#define dw_readl(dw, name)             __raw_readl(&(dw)->name)
-#define dw_writel(dw, name, val)       __raw_writel((val), &(dw)->name)
-
-/* Default use SPI0 register for mrst, we will detect Penwell and use SPI1 */
-static unsigned long mrst_spi_paddr = MRST_REGBASE_SPI0;
-
-static u32 *pclk_spi0;
-/* Always contains an accessible address, start with 0 */
-static struct dw_spi_reg *pspi;
-
-static struct kmsg_dumper dw_dumper;
-static int dumper_registered;
-
-static void dw_kmsg_dump(struct kmsg_dumper *dumper,
-                        enum kmsg_dump_reason reason)
-{
-       static char line[1024];
-       size_t len;
-
-       /* When run to this, we'd better re-init the HW */
-       mrst_early_console_init();
-
-       while (kmsg_dump_get_line(dumper, true, line, sizeof(line), &len))
-               early_mrst_console.write(&early_mrst_console, line, len);
-}
-
-/* Set the ratio rate to 115200, 8n1, IRQ disabled */
-static void max3110_write_config(void)
-{
-       u16 config;
-
-       config = 0xc001;
-       dw_writel(pspi, dr, config);
-}
-
-/* Translate char to a eligible word and send to max3110 */
-static void max3110_write_data(char c)
-{
-       u16 data;
-
-       data = 0x8000 | c;
-       dw_writel(pspi, dr, data);
-}
-
-void mrst_early_console_init(void)
-{
-       u32 ctrlr0 = 0;
-       u32 spi0_cdiv;
-       u32 freq; /* Freqency info only need be searched once */
-
-       /* Base clk is 100 MHz, the actual clk = 100M / (clk_divider + 1) */
-       pclk_spi0 = (void *)set_fixmap_offset_nocache(FIX_EARLYCON_MEM_BASE,
-                                                       MRST_CLK_SPI0_REG);
-       spi0_cdiv = ((*pclk_spi0) & 0xe00) >> 9;
-       freq = 100000000 / (spi0_cdiv + 1);
-
-       if (intel_mid_identify_cpu() == INTEL_MID_CPU_CHIP_PENWELL)
-               mrst_spi_paddr = MRST_REGBASE_SPI1;
-
-       pspi = (void *)set_fixmap_offset_nocache(FIX_EARLYCON_MEM_BASE,
-                                               mrst_spi_paddr);
-
-       /* Disable SPI controller */
-       dw_writel(pspi, ssienr, 0);
-
-       /* Set control param, 8 bits, transmit only mode */
-       ctrlr0 = dw_readl(pspi, ctrl0);
-
-       ctrlr0 &= 0xfcc0;
-       ctrlr0 |= 0xf | (SPI_FRF_SPI << SPI_FRF_OFFSET)
-                     | (SPI_TMOD_TO << SPI_TMOD_OFFSET);
-       dw_writel(pspi, ctrl0, ctrlr0);
-
-       /*
-        * Change the spi0 clk to comply with 115200 bps, use 100000 to
-        * calculate the clk dividor to make the clock a little slower
-        * than real baud rate.
-        */
-       dw_writel(pspi, baudr, freq/100000);
-
-       /* Disable all INT for early phase */
-       dw_writel(pspi, imr, 0x0);
-
-       /* Set the cs to spi-uart */
-       dw_writel(pspi, ser, 0x2);
-
-       /* Enable the HW, the last step for HW init */
-       dw_writel(pspi, ssienr, 0x1);
-
-       /* Set the default configuration */
-       max3110_write_config();
-
-       /* Register the kmsg dumper */
-       if (!dumper_registered) {
-               dw_dumper.dump = dw_kmsg_dump;
-               kmsg_dump_register(&dw_dumper);
-               dumper_registered = 1;
-       }
-}
-
-/* Slave select should be called in the read/write function */
-static void early_mrst_spi_putc(char c)
-{
-       unsigned int timeout;
-       u32 sr;
-
-       timeout = MRST_SPI_TIMEOUT;
-       /* Early putc needs to make sure the TX FIFO is not full */
-       while (--timeout) {
-               sr = dw_readl(pspi, sr);
-               if (!(sr & SR_TF_NOT_FULL))
-                       cpu_relax();
-               else
-                       break;
-       }
-
-       if (!timeout)
-               pr_warn("MRST earlycon: timed out\n");
-       else
-               max3110_write_data(c);
-}
-
-/* Early SPI only uses polling mode */
-static void early_mrst_spi_write(struct console *con, const char *str,
-                                       unsigned n)
-{
-       int i;
-
-       for (i = 0; i < n && *str; i++) {
-               if (*str == '\n')
-                       early_mrst_spi_putc('\r');
-               early_mrst_spi_putc(*str);
-               str++;
-       }
-}
-
-struct console early_mrst_console = {
-       .name =         "earlymrst",
-       .write =        early_mrst_spi_write,
-       .flags =        CON_PRINTBUFFER,
-       .index =        -1,
-};
-
 /*
  * Following is the early console based on Medfield HSU (High
  * Speed UART) device.
@@ -259,7 +47,7 @@ void hsu_early_console_init(const char *s)
                port = clamp_val(port, 0, 2);
 
        paddr = HSU_PORT_BASE + port * 0x80;
-       phsu = (void *)set_fixmap_offset_nocache(FIX_EARLYCON_MEM_BASE, paddr);
+       phsu = (void __iomem *)set_fixmap_offset_nocache(FIX_EARLYCON_MEM_BASE, paddr);
 
        /* Disable FIFO */
        writeb(0x0, phsu + UART_FCR);
index 2ff6dfd2d3f0657e13853ec13b04a7b51cf765a6..9c4dcf4c62ea763e318ecc41f7ef5db03a9fbe67 100644 (file)
@@ -51,33 +51,22 @@ struct ath_struct {
 
 static int ath_wakeup_ar3k(struct tty_struct *tty)
 {
-       struct ktermios ktermios;
        int status = tty->driver->ops->tiocmget(tty);
 
        if (status & TIOCM_CTS)
                return status;
 
-       /* Disable Automatic RTSCTS */
-       ktermios = tty->termios;
-       ktermios.c_cflag &= ~CRTSCTS;
-       tty_set_termios(tty, &ktermios);
-
        /* Clear RTS first */
-       status = tty->driver->ops->tiocmget(tty);
+       tty->driver->ops->tiocmget(tty);
        tty->driver->ops->tiocmset(tty, 0x00, TIOCM_RTS);
        mdelay(20);
 
        /* Set RTS, wake up board */
-       status = tty->driver->ops->tiocmget(tty);
+       tty->driver->ops->tiocmget(tty);
        tty->driver->ops->tiocmset(tty, TIOCM_RTS, 0x00);
        mdelay(20);
 
        status = tty->driver->ops->tiocmget(tty);
-
-       /* Enable Automatic RTSCTS */
-       ktermios.c_cflag |= CRTSCTS;
-       status = tty_set_termios(tty, &ktermios);
-
        return status;
 }
 
index f748cc8cbb031cf3ad3fd943688fc6172a452bd2..4e57d3370368f597bb27ed4bcc4693153ce0e0de 100644 (file)
@@ -182,7 +182,7 @@ static int __pnp_bus_suspend(struct device *dev, pm_message_t state)
                        return error;
        }
 
-       if (pnp_dev->protocol->suspend)
+       if (pnp_can_suspend(pnp_dev))
                pnp_dev->protocol->suspend(pnp_dev, state);
        return 0;
 }
index d9f85f95eb2a5bcbd24614da72aa5bc47e4af93e..b2d76005595225ebd9f1412bf250ad768132d660 100644 (file)
@@ -931,7 +931,7 @@ static void rs_send_xchar(struct tty_struct *tty, char ch)
        struct serial_state *info = tty->driver_data;
         unsigned long flags;
 
-       if (serial_paranoia_check(info, tty->name, "rs_send_char"))
+       if (serial_paranoia_check(info, tty->name, "rs_send_xchar"))
                return;
 
        info->x_char = ch;
index 3c60923b0957b446f82d271f11380ca58a718801..342b36b9ad35a1d4c77bbfa757d58c8ceaea0c14 100644 (file)
@@ -112,7 +112,6 @@ static void disable_tx_interrupt(struct ehv_bc_data *bc)
 static int find_console_handle(void)
 {
        struct device_node *np = of_stdout;
-       const char *sprop = NULL;
        const uint32_t *iprop;
 
        /* We don't care what the aliased node is actually called.  We only
index 59ed783c4bcda1e34a559193a857a6a41332c6ff..2054427992e0baac5553b7cd6c09a25ee2e6f9be 100644 (file)
@@ -1055,7 +1055,7 @@ static int isicom_send_break(struct tty_struct *tty, int length)
 
        outw(0x8000 | ((port->channel) << (card->shift_count)) | 0x3, base);
        outw((length & 0xff) << 8 | 0x00, base);
-       outw((length & 0xff00), base);
+       outw((length & 0xff00u), base);
        InterruptTheCard(base);
 
        unlock_card(card);
index 4ddfa60c922205513d16ed74a770eefd111fda83..cf6e0f2e1331fd46310a6834d99896e2b943ea19 100644 (file)
@@ -90,6 +90,7 @@
 struct n_tty_data {
        /* producer-published */
        size_t read_head;
+       size_t commit_head;
        size_t canon_head;
        size_t echo_head;
        size_t echo_commit;
@@ -161,36 +162,11 @@ static inline int tty_put_user(struct tty_struct *tty, unsigned char x,
        return put_user(x, ptr);
 }
 
-static int receive_room(struct tty_struct *tty)
-{
-       struct n_tty_data *ldata = tty->disc_data;
-       int left;
-
-       if (I_PARMRK(tty)) {
-               /* Multiply read_cnt by 3, since each byte might take up to
-                * three times as many spaces when PARMRK is set (depending on
-                * its flags, e.g. parity error). */
-               left = N_TTY_BUF_SIZE - read_cnt(ldata) * 3 - 1;
-       } else
-               left = N_TTY_BUF_SIZE - read_cnt(ldata) - 1;
-
-       /*
-        * If we are doing input canonicalization, and there are no
-        * pending newlines, let characters through without limit, so
-        * that erase characters will be handled.  Other excess
-        * characters will be beeped.
-        */
-       if (left <= 0)
-               left = ldata->icanon && ldata->canon_head == ldata->read_tail;
-
-       return left;
-}
-
 /**
- *     n_tty_set_room  -       receive space
+ *     n_tty_kick_worker - start input worker (if required)
  *     @tty: terminal
  *
- *     Re-schedules the flip buffer work if space just became available.
+ *     Re-schedules the flip buffer work if it may have stopped
  *
  *     Caller holds exclusive termios_rwsem
  *        or
@@ -198,12 +174,12 @@ static int receive_room(struct tty_struct *tty)
  *             holds non-exclusive termios_rwsem
  */
 
-static void n_tty_set_room(struct tty_struct *tty)
+static void n_tty_kick_worker(struct tty_struct *tty)
 {
        struct n_tty_data *ldata = tty->disc_data;
 
-       /* Did this open up the receive buffer? We may need to flip */
-       if (unlikely(ldata->no_room) && receive_room(tty)) {
+       /* Did the input worker stop? Restart it */
+       if (unlikely(ldata->no_room)) {
                ldata->no_room = 0;
 
                WARN_RATELIMIT(tty->port->itty == NULL,
@@ -224,7 +200,7 @@ static ssize_t chars_in_buffer(struct tty_struct *tty)
        ssize_t n = 0;
 
        if (!ldata->icanon)
-               n = read_cnt(ldata);
+               n = ldata->commit_head - ldata->read_tail;
        else
                n = ldata->canon_head - ldata->read_tail;
        return n;
@@ -247,17 +223,20 @@ static void n_tty_write_wakeup(struct tty_struct *tty)
 
 static void n_tty_check_throttle(struct tty_struct *tty)
 {
-       if (tty->driver->type == TTY_DRIVER_TYPE_PTY)
-               return;
+       struct n_tty_data *ldata = tty->disc_data;
+
        /*
         * Check the remaining room for the input canonicalization
         * mode.  We don't want to throttle the driver if we're in
         * canonical mode and don't have a newline yet!
         */
+       if (ldata->icanon && ldata->canon_head == ldata->read_tail)
+               return;
+
        while (1) {
                int throttled;
                tty_set_flow_change(tty, TTY_THROTTLE_SAFE);
-               if (receive_room(tty) >= TTY_THRESHOLD_THROTTLE)
+               if (N_TTY_BUF_SIZE - read_cnt(ldata) >= TTY_THRESHOLD_THROTTLE)
                        break;
                throttled = tty_throttle_safe(tty);
                if (!throttled)
@@ -274,7 +253,7 @@ static void n_tty_check_unthrottle(struct tty_struct *tty)
                        return;
                if (!tty->count)
                        return;
-               n_tty_set_room(tty);
+               n_tty_kick_worker(tty);
                n_tty_write_wakeup(tty->link);
                if (waitqueue_active(&tty->link->write_wait))
                        wake_up_interruptible_poll(&tty->link->write_wait, POLLOUT);
@@ -296,7 +275,7 @@ static void n_tty_check_unthrottle(struct tty_struct *tty)
                        break;
                if (!tty->count)
                        break;
-               n_tty_set_room(tty);
+               n_tty_kick_worker(tty);
                unthrottled = tty_unthrottle_safe(tty);
                if (!unthrottled)
                        break;
@@ -313,10 +292,6 @@ static void n_tty_check_unthrottle(struct tty_struct *tty)
  *
  *     n_tty_receive_buf()/producer path:
  *             caller holds non-exclusive termios_rwsem
- *             modifies read_head
- *
- *     read_head is only considered 'published' if canonical mode is
- *     not active.
  */
 
 static inline void put_tty_queue(unsigned char c, struct n_tty_data *ldata)
@@ -340,6 +315,7 @@ static void reset_buffer_flags(struct n_tty_data *ldata)
 {
        ldata->read_head = ldata->canon_head = ldata->read_tail = 0;
        ldata->echo_head = ldata->echo_tail = ldata->echo_commit = 0;
+       ldata->commit_head = 0;
        ldata->echo_mark = 0;
        ldata->line_start = 0;
 
@@ -379,7 +355,7 @@ static void n_tty_flush_buffer(struct tty_struct *tty)
 {
        down_write(&tty->termios_rwsem);
        reset_buffer_flags(tty->disc_data);
-       n_tty_set_room(tty);
+       n_tty_kick_worker(tty);
 
        if (tty->link)
                n_tty_packet_mode_flush(tty);
@@ -987,10 +963,6 @@ static inline void finish_erasing(struct n_tty_data *ldata)
  *
  *     n_tty_receive_buf()/producer path:
  *             caller holds non-exclusive termios_rwsem
- *             modifies read_head
- *
- *     Modifying the read_head is not considered a publish in this context
- *     because canonical mode is active -- only canon_head publishes
  */
 
 static void eraser(unsigned char c, struct tty_struct *tty)
@@ -1118,16 +1090,45 @@ static void eraser(unsigned char c, struct tty_struct *tty)
  *     Called when a signal is being sent due to terminal input.
  *     Called from the driver receive_buf path so serialized.
  *
+ *     Performs input and output flush if !NOFLSH. In this context, the echo
+ *     buffer is 'output'. The signal is processed first to alert any current
+ *     readers or writers to discontinue and exit their i/o loops.
+ *
  *     Locking: ctrl_lock
  */
 
 static void isig(int sig, struct tty_struct *tty)
 {
+       struct n_tty_data *ldata = tty->disc_data;
        struct pid *tty_pgrp = tty_get_pgrp(tty);
        if (tty_pgrp) {
                kill_pgrp(tty_pgrp, sig, 1);
                put_pid(tty_pgrp);
        }
+
+       if (!L_NOFLSH(tty)) {
+               up_read(&tty->termios_rwsem);
+               down_write(&tty->termios_rwsem);
+
+               /* clear echo buffer */
+               mutex_lock(&ldata->output_lock);
+               ldata->echo_head = ldata->echo_tail = 0;
+               ldata->echo_mark = ldata->echo_commit = 0;
+               mutex_unlock(&ldata->output_lock);
+
+               /* clear output buffer */
+               tty_driver_flush_buffer(tty);
+
+               /* clear input buffer */
+               reset_buffer_flags(tty->disc_data);
+
+               /* notify pty master of flush */
+               if (tty->link)
+                       n_tty_packet_mode_flush(tty);
+
+               up_write(&tty->termios_rwsem);
+               down_read(&tty->termios_rwsem);
+       }
 }
 
 /**
@@ -1139,7 +1140,6 @@ static void isig(int sig, struct tty_struct *tty)
  *
  *     n_tty_receive_buf()/producer path:
  *             caller holds non-exclusive termios_rwsem
- *             publishes read_head via put_tty_queue()
  *
  *     Note: may get exclusive termios_rwsem if flushing input buffer
  */
@@ -1152,13 +1152,6 @@ static void n_tty_receive_break(struct tty_struct *tty)
                return;
        if (I_BRKINT(tty)) {
                isig(SIGINT, tty);
-               if (!L_NOFLSH(tty)) {
-                       /* flushing needs exclusive termios_rwsem */
-                       up_read(&tty->termios_rwsem);
-                       n_tty_flush_buffer(tty);
-                       tty_driver_flush_buffer(tty);
-                       down_read(&tty->termios_rwsem);
-               }
                return;
        }
        if (I_PARMRK(tty)) {
@@ -1209,7 +1202,6 @@ static void n_tty_receive_overrun(struct tty_struct *tty)
  *
  *     n_tty_receive_buf()/producer path:
  *             caller holds non-exclusive termios_rwsem
- *             publishes read_head via put_tty_queue()
  */
 static void n_tty_receive_parity_error(struct tty_struct *tty, unsigned char c)
 {
@@ -1233,13 +1225,7 @@ static void n_tty_receive_parity_error(struct tty_struct *tty, unsigned char c)
 static void
 n_tty_receive_signal_char(struct tty_struct *tty, int signal, unsigned char c)
 {
-       if (!L_NOFLSH(tty)) {
-               /* flushing needs exclusive termios_rwsem */
-               up_read(&tty->termios_rwsem);
-               n_tty_flush_buffer(tty);
-               tty_driver_flush_buffer(tty);
-               down_read(&tty->termios_rwsem);
-       }
+       isig(signal, tty);
        if (I_IXON(tty))
                start_tty(tty);
        if (L_ECHO(tty)) {
@@ -1247,7 +1233,6 @@ n_tty_receive_signal_char(struct tty_struct *tty, int signal, unsigned char c)
                commit_echoes(tty);
        } else
                process_echoes(tty);
-       isig(signal, tty);
        return;
 }
 
@@ -1263,7 +1248,6 @@ n_tty_receive_signal_char(struct tty_struct *tty, int signal, unsigned char c)
  *     n_tty_receive_buf()/producer path:
  *             caller holds non-exclusive termios_rwsem
  *             publishes canon_head if canonical mode is active
- *             otherwise, publishes read_head via put_tty_queue()
  *
  *     Returns 1 if LNEXT was received, else returns 0
  */
@@ -1376,7 +1360,7 @@ n_tty_receive_char_special(struct tty_struct *tty, unsigned char c)
 handle_newline:
                        set_bit(ldata->read_head & (N_TTY_BUF_SIZE - 1), ldata->read_flags);
                        put_tty_queue(c, ldata);
-                       ldata->canon_head = ldata->read_head;
+                       smp_store_release(&ldata->canon_head, ldata->read_head);
                        kill_fasync(&tty->fasync, SIGIO, POLL_IN);
                        if (waitqueue_active(&tty->read_wait))
                                wake_up_interruptible_poll(&tty->read_wait, POLLIN);
@@ -1512,23 +1496,6 @@ n_tty_receive_char_lnext(struct tty_struct *tty, unsigned char c, char flag)
                n_tty_receive_char_flagged(tty, c, flag);
 }
 
-/**
- *     n_tty_receive_buf       -       data receive
- *     @tty: terminal device
- *     @cp: buffer
- *     @fp: flag buffer
- *     @count: characters
- *
- *     Called by the terminal driver when a block of characters has
- *     been received. This function must be called from soft contexts
- *     not from interrupt context. The driver is responsible for making
- *     calls one at a time and in order (or using flush_to_ldisc)
- *
- *     n_tty_receive_buf()/producer path:
- *             claims non-exclusive termios_rwsem
- *             publishes read_head and canon_head
- */
-
 static void
 n_tty_receive_buf_real_raw(struct tty_struct *tty, const unsigned char *cp,
                           char *fp, int count)
@@ -1537,16 +1504,14 @@ n_tty_receive_buf_real_raw(struct tty_struct *tty, const unsigned char *cp,
        size_t n, head;
 
        head = ldata->read_head & (N_TTY_BUF_SIZE - 1);
-       n = N_TTY_BUF_SIZE - max(read_cnt(ldata), head);
-       n = min_t(size_t, count, n);
+       n = min_t(size_t, count, N_TTY_BUF_SIZE - head);
        memcpy(read_buf_addr(ldata, head), cp, n);
        ldata->read_head += n;
        cp += n;
        count -= n;
 
        head = ldata->read_head & (N_TTY_BUF_SIZE - 1);
-       n = N_TTY_BUF_SIZE - max(read_cnt(ldata), head);
-       n = min_t(size_t, count, n);
+       n = min_t(size_t, count, N_TTY_BUF_SIZE - head);
        memcpy(read_buf_addr(ldata, head), cp, n);
        ldata->read_head += n;
 }
@@ -1676,32 +1641,98 @@ static void __receive_buf(struct tty_struct *tty, const unsigned char *cp,
                        tty->ops->flush_chars(tty);
        }
 
-       if ((!ldata->icanon && (read_cnt(ldata) >= ldata->minimum_to_wake)) ||
-               L_EXTPROC(tty)) {
+       if (ldata->icanon && !L_EXTPROC(tty))
+               return;
+
+       /* publish read_head to consumer */
+       smp_store_release(&ldata->commit_head, ldata->read_head);
+
+       if ((read_cnt(ldata) >= ldata->minimum_to_wake) || L_EXTPROC(tty)) {
                kill_fasync(&tty->fasync, SIGIO, POLL_IN);
                if (waitqueue_active(&tty->read_wait))
                        wake_up_interruptible_poll(&tty->read_wait, POLLIN);
        }
 }
 
+/**
+ *     n_tty_receive_buf_common        -       process input
+ *     @tty: device to receive input
+ *     @cp: input chars
+ *     @fp: flags for each char (if NULL, all chars are TTY_NORMAL)
+ *     @count: number of input chars in @cp
+ *
+ *     Called by the terminal driver when a block of characters has
+ *     been received. This function must be called from soft contexts
+ *     not from interrupt context. The driver is responsible for making
+ *     calls one at a time and in order (or using flush_to_ldisc)
+ *
+ *     Returns the # of input chars from @cp which were processed.
+ *
+ *     In canonical mode, the maximum line length is 4096 chars (including
+ *     the line termination char); lines longer than 4096 chars are
+ *     truncated. After 4095 chars, input data is still processed but
+ *     not stored. Overflow processing ensures the tty can always
+ *     receive more input until at least one line can be read.
+ *
+ *     In non-canonical mode, the read buffer will only accept 4095 chars;
+ *     this provides the necessary space for a newline char if the input
+ *     mode is switched to canonical.
+ *
+ *     Note it is possible for the read buffer to _contain_ 4096 chars
+ *     in non-canonical mode: the read buffer could already contain the
+ *     maximum canon line of 4096 chars when the mode is switched to
+ *     non-canonical.
+ *
+ *     n_tty_receive_buf()/producer path:
+ *             claims non-exclusive termios_rwsem
+ *             publishes commit_head or canon_head
+ */
 static int
 n_tty_receive_buf_common(struct tty_struct *tty, const unsigned char *cp,
                         char *fp, int count, int flow)
 {
        struct n_tty_data *ldata = tty->disc_data;
-       int room, n, rcvd = 0;
+       int room, n, rcvd = 0, overflow;
 
        down_read(&tty->termios_rwsem);
 
        while (1) {
-               room = receive_room(tty);
+               /*
+                * When PARMRK is set, each input char may take up to 3 chars
+                * in the read buf; reduce the buffer space avail by 3x
+                *
+                * If we are doing input canonicalization, and there are no
+                * pending newlines, let characters through without limit, so
+                * that erase characters will be handled.  Other excess
+                * characters will be beeped.
+                *
+                * paired with store in *_copy_from_read_buf() -- guarantees
+                * the consumer has loaded the data in read_buf up to the new
+                * read_tail (so this producer will not overwrite unread data)
+                */
+               size_t tail = smp_load_acquire(&ldata->read_tail);
+
+               room = N_TTY_BUF_SIZE - (ldata->read_head - tail);
+               if (I_PARMRK(tty))
+                       room = (room + 2) / 3;
+               room--;
+               if (room <= 0) {
+                       overflow = ldata->icanon && ldata->canon_head == tail;
+                       if (overflow && room < 0)
+                               ldata->read_head--;
+                       room = overflow;
+                       ldata->no_room = flow && !room;
+               } else
+                       overflow = 0;
+
                n = min(count, room);
-               if (!n) {
-                       if (flow && !room)
-                               ldata->no_room = 1;
+               if (!n)
                        break;
-               }
-               __receive_buf(tty, cp, fp, n);
+
+               /* ignore parity errors if handling overflow */
+               if (!overflow || !fp || *fp != TTY_PARITY)
+                       __receive_buf(tty, cp, fp, n);
+
                cp += n;
                if (fp)
                        fp += n;
@@ -1710,7 +1741,17 @@ n_tty_receive_buf_common(struct tty_struct *tty, const unsigned char *cp,
        }
 
        tty->receive_room = room;
-       n_tty_check_throttle(tty);
+
+       /* Unthrottle if handling overflow on pty */
+       if (tty->driver->type == TTY_DRIVER_TYPE_PTY) {
+               if (overflow) {
+                       tty_set_flow_change(tty, TTY_UNTHROTTLE_SAFE);
+                       tty_unthrottle_safe(tty);
+                       __tty_set_flow_change(tty, 0);
+               }
+       } else
+               n_tty_check_throttle(tty);
+
        up_read(&tty->termios_rwsem);
 
        return rcvd;
@@ -1764,6 +1805,7 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old)
                        ldata->canon_head = ldata->read_head;
                        ldata->push = 1;
                }
+               ldata->commit_head = ldata->read_head;
                ldata->erasing = 0;
                ldata->lnext = 0;
        }
@@ -1817,7 +1859,6 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old)
                else
                        ldata->real_raw = 0;
        }
-       n_tty_set_room(tty);
        /*
         * Fix tty hang when I_IXON(tty) is cleared, but the tty
         * been stopped by STOP_CHAR(tty) before it.
@@ -1905,7 +1946,7 @@ static inline int input_available_p(struct tty_struct *tty, int poll)
        if (ldata->icanon && !L_EXTPROC(tty))
                return ldata->canon_head != ldata->read_tail;
        else
-               return read_cnt(ldata) >= amt;
+               return ldata->commit_head - ldata->read_tail >= amt;
 }
 
 /**
@@ -1937,10 +1978,11 @@ static int copy_from_read_buf(struct tty_struct *tty,
        int retval;
        size_t n;
        bool is_eof;
+       size_t head = smp_load_acquire(&ldata->commit_head);
        size_t tail = ldata->read_tail & (N_TTY_BUF_SIZE - 1);
 
        retval = 0;
-       n = min(read_cnt(ldata), N_TTY_BUF_SIZE - tail);
+       n = min(head - ldata->read_tail, N_TTY_BUF_SIZE - tail);
        n = min(*nr, n);
        if (n) {
                retval = copy_to_user(*b, read_buf_addr(ldata, tail), n);
@@ -1948,9 +1990,10 @@ static int copy_from_read_buf(struct tty_struct *tty,
                is_eof = n == 1 && read_buf(ldata, tail) == EOF_CHAR(tty);
                tty_audit_add_data(tty, read_buf_addr(ldata, tail), n,
                                ldata->icanon);
-               ldata->read_tail += n;
+               smp_store_release(&ldata->read_tail, ldata->read_tail + n);
                /* Turn single EOF into zero-length read */
-               if (L_EXTPROC(tty) && ldata->icanon && is_eof && !read_cnt(ldata))
+               if (L_EXTPROC(tty) && ldata->icanon && is_eof &&
+                   (head == ldata->read_tail))
                        n = 0;
                *b += n;
                *nr -= n;
@@ -1993,7 +2036,7 @@ static int canon_copy_from_read_buf(struct tty_struct *tty,
        bool eof_push = 0;
 
        /* N.B. avoid overrun if nr == 0 */
-       n = min(*nr, read_cnt(ldata));
+       n = min(*nr, smp_load_acquire(&ldata->canon_head) - ldata->read_tail);
        if (!n)
                return 0;
 
@@ -2043,8 +2086,7 @@ static int canon_copy_from_read_buf(struct tty_struct *tty,
 
        if (found)
                clear_bit(eol, ldata->read_flags);
-       smp_mb__after_atomic();
-       ldata->read_tail += c;
+       smp_store_release(&ldata->read_tail, ldata->read_tail + c);
 
        if (found) {
                if (!ldata->push)
@@ -2130,6 +2172,7 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
        ssize_t retval = 0;
        long timeout;
        int packet;
+       size_t tail;
 
        c = job_control(tty, file);
        if (c < 0)
@@ -2166,6 +2209,7 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
        }
 
        packet = tty->packet;
+       tail = ldata->read_tail;
 
        add_wait_queue(&tty->read_wait, &wait);
        while (nr) {
@@ -2208,7 +2252,6 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
                                retval = -ERESTARTSYS;
                                break;
                        }
-                       n_tty_set_room(tty);
                        up_read(&tty->termios_rwsem);
 
                        timeout = wait_woken(&wait, TASK_INTERRUPTIBLE,
@@ -2253,7 +2296,8 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
                if (time)
                        timeout = time;
        }
-       n_tty_set_room(tty);
+       if (tail != ldata->read_tail)
+               n_tty_kick_worker(tty);
        up_read(&tty->termios_rwsem);
 
        remove_wait_queue(&tty->read_wait, &wait);
index a9d256d6e909c1aecfc854732694d608256aeb15..e72ee629cead1b0af93c54b96395cbfc98b69975 100644 (file)
@@ -87,19 +87,6 @@ static void pty_unthrottle(struct tty_struct *tty)
        set_bit(TTY_THROTTLED, &tty->flags);
 }
 
-/**
- *     pty_space       -       report space left for writing
- *     @to: tty we are writing into
- *
- *     Limit the buffer space used by ptys to 8k.
- */
-
-static int pty_space(struct tty_struct *to)
-{
-       int n = tty_buffer_space_avail(to->port);
-       return min(n, 8192);
-}
-
 /**
  *     pty_write               -       write to a pty
  *     @tty: the tty we write from
@@ -141,7 +128,7 @@ static int pty_write_room(struct tty_struct *tty)
 {
        if (tty->stopped)
                return 0;
-       return pty_space(tty->link);
+       return tty_buffer_space_avail(tty->link->port);
 }
 
 /**
@@ -210,6 +197,9 @@ static int pty_signal(struct tty_struct *tty, int sig)
 {
        struct pid *pgrp;
 
+       if (sig != SIGINT && sig != SIGQUIT && sig != SIGTSTP)
+               return -EINVAL;
+
        if (tty->link) {
                pgrp = tty_get_pgrp(tty->link);
                if (pgrp)
@@ -222,10 +212,16 @@ static int pty_signal(struct tty_struct *tty, int sig)
 static void pty_flush_buffer(struct tty_struct *tty)
 {
        struct tty_struct *to = tty->link;
+       struct tty_ldisc *ld;
 
        if (!to)
                return;
-       /* tty_buffer_flush(to); FIXME */
+
+       ld = tty_ldisc_ref(to);
+       tty_buffer_flush(to, ld);
+       if (ld)
+               tty_ldisc_deref(ld);
+
        if (to->packet) {
                spin_lock_irq(&tty->ctrl_lock);
                tty->ctrl_status |= TIOCPKT_FLUSHWRITE;
@@ -399,6 +395,7 @@ static int pty_common_install(struct tty_driver *driver, struct tty_struct *tty,
                goto err_put_module;
 
        tty_set_lock_subclass(o_tty);
+       lockdep_set_subclass(&o_tty->termios_rwsem, TTY_LOCK_SLAVE);
 
        if (legacy) {
                /* We always use new tty termios data so we can do this
@@ -429,10 +426,14 @@ static int pty_common_install(struct tty_driver *driver, struct tty_struct *tty,
        o_tty->link = tty;
        tty_port_init(ports[0]);
        tty_port_init(ports[1]);
+       tty_buffer_set_limit(ports[0], 8192);
+       tty_buffer_set_limit(ports[1], 8192);
        o_tty->port = ports[0];
        tty->port = ports[1];
        o_tty->port->itty = o_tty;
 
+       tty_buffer_set_lock_subclass(o_tty->port);
+
        tty_driver_kref_get(driver);
        tty->count++;
        o_tty->count++;
index 383c4c796637713e12bede50677803a0d639a855..c8dd8dc31086ee5d373427dc5a7f04bee7cce1bb 100644 (file)
@@ -1390,7 +1390,7 @@ static void rp_unthrottle(struct tty_struct *tty)
               tty->ldisc.chars_in_buffer(tty));
 #endif
 
-       if (rocket_paranoia_check(info, "rp_throttle"))
+       if (rocket_paranoia_check(info, "rp_unthrottle"))
                return;
 
        if (I_IXOFF(tty))
@@ -1458,7 +1458,7 @@ static void rp_wait_until_sent(struct tty_struct *tty, int timeout)
 
        orig_jiffies = jiffies;
 #ifdef ROCKET_DEBUG_WAIT_UNTIL_SENT
-       printk(KERN_INFO "In RP_wait_until_sent(%d) (jiff=%lu)...\n", timeout,
+       printk(KERN_INFO "In %s(%d) (jiff=%lu)...\n", __func__, timeout,
               jiffies);
        printk(KERN_INFO "cps=%d...\n", info->cps);
 #endif
index 11c66856ba2fcb59edf96bba1459b0a9b5e4f9db..e3b9570a1eff8aa467ec7dabbe93369c47940e87 100644 (file)
@@ -329,6 +329,17 @@ static const struct serial8250_config uart_config[] = {
                .fcr            = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
                .flags          = UART_CAP_FIFO | UART_CAP_AFE,
        },
+/* tx_loadsz is set to 63-bytes instead of 64-bytes to implement
+workaround of errata A-008006 which states that tx_loadsz should  be
+configured less than Maximum supported fifo bytes */
+       [PORT_16550A_FSL64] = {
+               .name           = "16550A_FSL64",
+               .fifo_size      = 64,
+               .tx_loadsz      = 63,
+               .fcr            = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10 |
+                                 UART_FCR7_64BYTE,
+               .flags          = UART_CAP_FIFO,
+       },
 };
 
 /* Uart divisor latch read */
@@ -956,7 +967,17 @@ static void autoconfig_16550a(struct uart_8250_port *up)
                        up->port.type = PORT_16650;
                        up->capabilities |= UART_CAP_EFR | UART_CAP_SLEEP;
                } else {
-                       DEBUG_AUTOCONF("Motorola 8xxx DUART ");
+                       serial_out(up, UART_LCR, 0);
+                       serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO |
+                                  UART_FCR7_64BYTE);
+                       status1 = serial_in(up, UART_IIR) >> 5;
+                       serial_out(up, UART_FCR, 0);
+                       serial_out(up, UART_LCR, 0);
+
+                       if (status1 == 7)
+                               up->port.type = PORT_16550A_FSL64;
+                       else
+                               DEBUG_AUTOCONF("Motorola 8xxx DUART ");
                }
                serial_out(up, UART_EFR, 0);
                return;
@@ -1355,9 +1376,11 @@ static void serial8250_start_tx(struct uart_port *port)
        struct uart_8250_port *up = up_to_u8250p(port);
 
        serial8250_rpm_get_tx(up);
-       if (up->dma && !up->dma->tx_dma(up)) {
+
+       if (up->dma && !up->dma->tx_dma(up))
                return;
-       } else if (!(up->ier & UART_IER_THRI)) {
+
+       if (!(up->ier & UART_IER_THRI)) {
                up->ier |= UART_IER_THRI;
                serial_port_out(port, UART_IER, up->ier);
 
@@ -1365,7 +1388,7 @@ static void serial8250_start_tx(struct uart_port *port)
                        unsigned char lsr;
                        lsr = serial_in(up, UART_LSR);
                        up->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS;
-                       if (lsr & UART_LSR_TEMT)
+                       if (lsr & UART_LSR_THRE)
                                serial8250_tx_chars(up);
                }
        }
@@ -1924,7 +1947,7 @@ static unsigned int serial8250_get_mctrl(struct uart_port *port)
        return ret;
 }
 
-static void serial8250_set_mctrl(struct uart_port *port, unsigned int mctrl)
+void serial8250_do_set_mctrl(struct uart_port *port, unsigned int mctrl)
 {
        struct uart_8250_port *up = up_to_u8250p(port);
        unsigned char mcr = 0;
@@ -1944,6 +1967,14 @@ static void serial8250_set_mctrl(struct uart_port *port, unsigned int mctrl)
 
        serial_port_out(port, UART_MCR, mcr);
 }
+EXPORT_SYMBOL_GPL(serial8250_do_set_mctrl);
+
+static void serial8250_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+       if (port->set_mctrl)
+               return port->set_mctrl(port, mctrl);
+       return serial8250_do_set_mctrl(port, mctrl);
+}
 
 static void serial8250_break_ctl(struct uart_port *port, int break_state)
 {
@@ -2382,13 +2413,34 @@ static void serial8250_shutdown(struct uart_port *port)
                serial8250_do_shutdown(port);
 }
 
-static unsigned int serial8250_get_divisor(struct uart_port *port, unsigned int baud)
+/*
+ * XR17V35x UARTs have an extra fractional divisor register (DLD)
+ * Calculate divisor with extra 4-bit fractional portion
+ */
+static unsigned int xr17v35x_get_divisor(struct uart_8250_port *up,
+                                        unsigned int baud,
+                                        unsigned int *frac)
+{
+       struct uart_port *port = &up->port;
+       unsigned int quot_16;
+
+       quot_16 = DIV_ROUND_CLOSEST(port->uartclk, baud);
+       *frac = quot_16 & 0x0f;
+
+       return quot_16 >> 4;
+}
+
+static unsigned int serial8250_get_divisor(struct uart_8250_port *up,
+                                          unsigned int baud,
+                                          unsigned int *frac)
 {
+       struct uart_port *port = &up->port;
        unsigned int quot;
 
        /*
         * Handle magic divisors for baud rates above baud_base on
         * SMSC SuperIO chips.
+        *
         */
        if ((port->flags & UPF_MAGIC_MULTIPLIER) &&
            baud == (port->uartclk/4))
@@ -2396,22 +2448,26 @@ static unsigned int serial8250_get_divisor(struct uart_port *port, unsigned int
        else if ((port->flags & UPF_MAGIC_MULTIPLIER) &&
                 baud == (port->uartclk/8))
                quot = 0x8002;
+       else if (up->port.type == PORT_XR17V35X)
+               quot = xr17v35x_get_divisor(up, baud, frac);
        else
                quot = uart_get_divisor(port, baud);
 
+       /*
+        * Oxford Semi 952 rev B workaround
+        */
+       if (up->bugs & UART_BUG_QUOT && (quot & 0xff) == 0)
+               quot++;
+
        return quot;
 }
 
-void
-serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
-                         struct ktermios *old)
+static unsigned char serial8250_compute_lcr(struct uart_8250_port *up,
+                                           tcflag_t c_cflag)
 {
-       struct uart_8250_port *up = up_to_u8250p(port);
        unsigned char cval;
-       unsigned long flags;
-       unsigned int baud, quot;
 
-       switch (termios->c_cflag & CSIZE) {
+       switch (c_cflag & CSIZE) {
        case CS5:
                cval = UART_LCR_WLEN5;
                break;
@@ -2427,33 +2483,80 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
                break;
        }
 
-       if (termios->c_cflag & CSTOPB)
+       if (c_cflag & CSTOPB)
                cval |= UART_LCR_STOP;
-       if (termios->c_cflag & PARENB) {
+       if (c_cflag & PARENB) {
                cval |= UART_LCR_PARITY;
                if (up->bugs & UART_BUG_PARITY)
                        up->fifo_bug = true;
        }
-       if (!(termios->c_cflag & PARODD))
+       if (!(c_cflag & PARODD))
                cval |= UART_LCR_EPAR;
 #ifdef CMSPAR
-       if (termios->c_cflag & CMSPAR)
+       if (c_cflag & CMSPAR)
                cval |= UART_LCR_SPAR;
 #endif
 
+       return cval;
+}
+
+static void serial8250_set_divisor(struct uart_port *port, unsigned int baud,
+                           unsigned int quot, unsigned int quot_frac)
+{
+       struct uart_8250_port *up = up_to_u8250p(port);
+
+       /* Workaround to enable 115200 baud on OMAP1510 internal ports */
+       if (is_omap1510_8250(up)) {
+               if (baud == 115200) {
+                       quot = 1;
+                       serial_port_out(port, UART_OMAP_OSC_12M_SEL, 1);
+               } else
+                       serial_port_out(port, UART_OMAP_OSC_12M_SEL, 0);
+       }
+
+       /*
+        * For NatSemi, switch to bank 2 not bank 1, to avoid resetting EXCR2,
+        * otherwise just set DLAB
+        */
+       if (up->capabilities & UART_NATSEMI)
+               serial_port_out(port, UART_LCR, 0xe0);
+       else
+               serial_port_out(port, UART_LCR, up->lcr | UART_LCR_DLAB);
+
+       serial_dl_write(up, quot);
+
+       /* XR17V35x UARTs have an extra fractional divisor register (DLD) */
+       if (up->port.type == PORT_XR17V35X)
+               serial_port_out(port, 0x2, quot_frac);
+}
+
+void
+serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
+                         struct ktermios *old)
+{
+       struct uart_8250_port *up = up_to_u8250p(port);
+       unsigned char cval;
+       unsigned long flags;
+       unsigned int baud, quot, frac = 0;
+
+       cval = serial8250_compute_lcr(up, termios->c_cflag);
+
        /*
         * Ask the core to calculate the divisor for us.
         */
        baud = uart_get_baud_rate(port, termios, old,
                                  port->uartclk / 16 / 0xffff,
                                  port->uartclk / 16);
-       quot = serial8250_get_divisor(port, baud);
+       quot = serial8250_get_divisor(up, baud, &frac);
 
        /*
-        * Oxford Semi 952 rev B workaround
+        * Ok, we're now changing the port state.  Do it with
+        * interrupts disabled.
         */
-       if (up->bugs & UART_BUG_QUOT && (quot & 0xff) == 0)
-               quot++;
+       serial8250_rpm_get(up);
+       spin_lock_irqsave(&port->lock, flags);
+
+       up->lcr = cval;                                 /* Save computed LCR */
 
        if (up->capabilities & UART_CAP_FIFO && port->fifosize > 1) {
                /* NOTE: If fifo_bug is not set, a user can set RX_trigger. */
@@ -2477,13 +2580,6 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
                        up->mcr |= UART_MCR_AFE;
        }
 
-       /*
-        * Ok, we're now changing the port state.  Do it with
-        * interrupts disabled.
-        */
-       serial8250_rpm_get(up);
-       spin_lock_irqsave(&port->lock, flags);
-
        /*
         * Update the per-port timeout.
         */
@@ -2548,43 +2644,7 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
                        serial_port_out(port, UART_EFR, efr);
        }
 
-       /* Workaround to enable 115200 baud on OMAP1510 internal ports */
-       if (is_omap1510_8250(up)) {
-               if (baud == 115200) {
-                       quot = 1;
-                       serial_port_out(port, UART_OMAP_OSC_12M_SEL, 1);
-               } else
-                       serial_port_out(port, UART_OMAP_OSC_12M_SEL, 0);
-       }
-
-       /*
-        * For NatSemi, switch to bank 2 not bank 1, to avoid resetting EXCR2,
-        * otherwise just set DLAB
-        */
-       if (up->capabilities & UART_NATSEMI)
-               serial_port_out(port, UART_LCR, 0xe0);
-       else
-               serial_port_out(port, UART_LCR, cval | UART_LCR_DLAB);
-
-       serial_dl_write(up, quot);
-
-       /*
-        * XR17V35x UARTs have an extra fractional divisor register (DLD)
-        *
-        * We need to recalculate all of the registers, because DLM and DLL
-        * are already rounded to a whole integer.
-        *
-        * When recalculating we use a 32x clock instead of a 16x clock to
-        * allow 1-bit for rounding in the fractional part.
-        */
-       if (up->port.type == PORT_XR17V35X) {
-               unsigned int baud_x32 = (port->uartclk * 2) / baud;
-               u16 quot = baud_x32 / 32;
-               u8 quot_frac = DIV_ROUND_CLOSEST(baud_x32 % 32, 2);
-
-               serial_dl_write(up, quot);
-               serial_port_out(port, 0x2, quot_frac & 0xf);
-       }
+       serial8250_set_divisor(port, baud, quot, frac);
 
        /*
         * LCR DLAB must be set to enable 64-byte FIFO mode. If the FCR
@@ -2593,8 +2653,7 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
        if (port->type == PORT_16750)
                serial_port_out(port, UART_FCR, up->fcr);
 
-       serial_port_out(port, UART_LCR, cval);          /* reset DLAB */
-       up->lcr = cval;                                 /* Save LCR */
+       serial_port_out(port, UART_LCR, up->lcr);       /* reset DLAB */
        if (port->type != PORT_16750) {
                /* emulated UARTs (Lucent Venus 167x) need two steps */
                if (up->fcr & UART_FCR_ENABLE_FIFO)
@@ -3208,6 +3267,27 @@ serial8250_console_write(struct console *co, const char *s, unsigned int count)
        else
                serial_port_out(port, UART_IER, 0);
 
+       /* check scratch reg to see if port powered off during system sleep */
+       if (up->canary && (up->canary != serial_port_in(port, UART_SCR))) {
+               struct ktermios termios;
+               unsigned int baud, quot, frac = 0;
+
+               termios.c_cflag = port->cons->cflag;
+               if (port->state->port.tty && termios.c_cflag == 0)
+                       termios.c_cflag = port->state->port.tty->termios.c_cflag;
+
+               baud = uart_get_baud_rate(port, &termios, NULL,
+                                         port->uartclk / 16 / 0xffff,
+                                         port->uartclk / 16);
+               quot = serial8250_get_divisor(up, baud, &frac);
+
+               serial8250_set_divisor(port, baud, quot, frac);
+               serial_port_out(port, UART_LCR, up->lcr);
+               serial_port_out(port, UART_MCR, UART_MCR_DTR | UART_MCR_RTS);
+
+               up->canary = 0;
+       }
+
        uart_console_write(port, s, count, serial8250_console_putchar);
 
        /*
@@ -3358,7 +3438,17 @@ int __init early_serial_setup(struct uart_port *port)
  */
 void serial8250_suspend_port(int line)
 {
-       uart_suspend_port(&serial8250_reg, &serial8250_ports[line].port);
+       struct uart_8250_port *up = &serial8250_ports[line];
+       struct uart_port *port = &up->port;
+
+       if (!console_suspend_enabled && uart_console(port) &&
+           port->type != PORT_8250) {
+               unsigned char canary = 0xa5;
+               serial_out(up, UART_SCR, canary);
+               up->canary = canary;
+       }
+
+       uart_suspend_port(&serial8250_reg, port);
 }
 
 /**
@@ -3372,6 +3462,8 @@ void serial8250_resume_port(int line)
        struct uart_8250_port *up = &serial8250_ports[line];
        struct uart_port *port = &up->port;
 
+       up->canary = 0;
+
        if (up->capabilities & UART_NATSEMI) {
                /* Ensure it's still in high speed mode */
                serial_port_out(port, UART_LCR, 0xE0);
@@ -3605,6 +3697,8 @@ int serial8250_register_8250_port(struct uart_8250_port *up)
                /*  Possibly override set_termios call */
                if (up->port.set_termios)
                        uart->port.set_termios = up->port.set_termios;
+               if (up->port.set_mctrl)
+                       uart->port.set_mctrl = up->port.set_mctrl;
                if (up->port.startup)
                        uart->port.startup = up->port.startup;
                if (up->port.shutdown)
index fcd7ac6af2fc25339600b5059a658ea052f14b91..21d01a491405a2c52cd1ec02f3198fa4c6b8f10c 100644 (file)
@@ -59,7 +59,6 @@ static void __dma_rx_complete(void *param)
 
        dma->rx_running = 0;
        dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state);
-       dmaengine_terminate_all(dma->rxchan);
 
        count = dma->rx_size - state.residue;
 
@@ -81,6 +80,10 @@ int serial8250_tx_dma(struct uart_8250_port *p)
                return 0;
 
        dma->tx_size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
+       if (dma->tx_size < p->port.fifosize) {
+               ret = -EINVAL;
+               goto err;
+       }
 
        desc = dmaengine_prep_slave_single(dma->txchan,
                                           dma->tx_addr + xmit->tail,
@@ -131,6 +134,7 @@ int serial8250_rx_dma(struct uart_8250_port *p, unsigned int iir)
                if (dma->rx_running) {
                        dmaengine_pause(dma->rxchan);
                        __dma_rx_complete(p);
+                       dmaengine_terminate_all(dma->rxchan);
                }
                return -ETIMEDOUT;
        default:
index 555de07db593a14adc74a2eda46a15847e8f3c07..e60116235836498e5b20ce56219b7d8c949acd25 100644 (file)
@@ -351,10 +351,20 @@ static int dw8250_probe_of(struct uart_port *p,
 static int dw8250_probe_acpi(struct uart_8250_port *up,
                             struct dw8250_data *data)
 {
+       const struct acpi_device_id *id;
        struct uart_port *p = &up->port;
 
        dw8250_setup_port(up);
 
+       id = acpi_match_device(p->dev->driver->acpi_match_table, p->dev);
+       if (!id)
+               return -ENODEV;
+
+       if (!p->uartclk)
+               if (device_property_read_u32(p->dev, "clock-frequency",
+                                            &p->uartclk))
+                       return -EINVAL;
+
        p->iotype = UPIO_MEM32;
        p->serial_in = dw8250_serial_in32;
        p->serial_out = dw8250_serial_out32;
@@ -577,6 +587,7 @@ static const struct acpi_device_id dw8250_acpi_match[] = {
        { "INT3435", 0 },
        { "80860F0A", 0 },
        { "8086228A", 0 },
+       { "APMC0D08", 0},
        { },
 };
 MODULE_DEVICE_TABLE(acpi, dw8250_acpi_match);
index 4858b8a99d3b4fcdbd4702c08a89442c6fb795fa..c31a22b4f84524a0226343cab01532da884d1bac 100644 (file)
@@ -93,15 +93,18 @@ static void __init early_serial8250_write(struct console *console,
        struct uart_port *port = &early_device->port;
        unsigned int ier;
 
-       /* Save the IER and disable interrupts */
+       /* Save the IER and disable interrupts preserving the UUE bit */
        ier = serial8250_early_in(port, UART_IER);
-       serial8250_early_out(port, UART_IER, 0);
+       if (ier)
+               serial8250_early_out(port, UART_IER, ier & UART_IER_UUE);
 
        uart_console_write(port, s, count, serial_putc);
 
        /* Wait for transmitter to become empty and restore the IER */
        wait_for_xmitr(port);
-       serial8250_early_out(port, UART_IER, ier);
+
+       if (ier)
+               serial8250_early_out(port, UART_IER, ier);
 }
 
 static unsigned int __init probe_baud(struct uart_port *port)
@@ -124,9 +127,11 @@ static void __init init_port(struct earlycon_device *device)
        struct uart_port *port = &device->port;
        unsigned int divisor;
        unsigned char c;
+       unsigned int ier;
 
        serial8250_early_out(port, UART_LCR, 0x3);      /* 8n1 */
-       serial8250_early_out(port, UART_IER, 0);        /* no interrupt */
+       ier = serial8250_early_in(port, UART_IER);
+       serial8250_early_out(port, UART_IER, ier & UART_IER_UUE); /* no interrupt */
        serial8250_early_out(port, UART_FCR, 0);        /* no fifo */
        serial8250_early_out(port, UART_MCR, 0x3);      /* DTR + RTS */
 
index 96b69bfd773f025f6e86cff1cbfb1e2566c427ce..fe6d2e51da09266324d0ac93af72da3578280c85 100644 (file)
@@ -106,6 +106,28 @@ static u32 uart_read(struct uart_8250_port *up, u32 reg)
        return readl(up->port.membase + (reg << up->port.regshift));
 }
 
+static void omap8250_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+       struct uart_8250_port *up = up_to_u8250p(port);
+       struct omap8250_priv *priv = up->port.private_data;
+       u8 lcr;
+
+       serial8250_do_set_mctrl(port, mctrl);
+
+       /*
+        * Turn off autoRTS if RTS is lowered and restore autoRTS setting
+        * if RTS is raised
+        */
+       lcr = serial_in(up, UART_LCR);
+       serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
+       if ((mctrl & TIOCM_RTS) && (port->status & UPSTAT_AUTORTS))
+               priv->efr |= UART_EFR_RTS;
+       else
+               priv->efr &= ~UART_EFR_RTS;
+       serial_out(up, UART_EFR, priv->efr);
+       serial_out(up, UART_LCR, lcr);
+}
+
 /*
  * Work Around for Errata i202 (2430, 3430, 3630, 4430 and 4460)
  * The access to uart register after MDR1 Access
@@ -397,12 +419,12 @@ static void omap_8250_set_termios(struct uart_port *port,
 
        priv->efr = 0;
        up->mcr &= ~(UART_MCR_RTS | UART_MCR_XONANY);
-       if (termios->c_cflag & CRTSCTS && up->port.flags & UPF_HARD_FLOW) {
-               /* Enable AUTORTS and AUTOCTS */
-               priv->efr |= UART_EFR_CTS | UART_EFR_RTS;
+       up->port.status &= ~(UPSTAT_AUTOCTS | UPSTAT_AUTORTS | UPSTAT_AUTOXOFF);
 
-               /* Ensure MCR RTS is asserted */
-               up->mcr |= UART_MCR_RTS;
+       if (termios->c_cflag & CRTSCTS && up->port.flags & UPF_HARD_FLOW) {
+               /* Enable AUTOCTS (autoRTS is enabled when RTS is raised) */
+               up->port.status |= UPSTAT_AUTOCTS | UPSTAT_AUTORTS;
+               priv->efr |= UART_EFR_CTS;
        } else  if (up->port.flags & UPF_SOFT_FLOW) {
                /*
                 * IXON Flag:
@@ -417,8 +439,10 @@ static void omap_8250_set_termios(struct uart_port *port,
                 * Enable XON/XOFF flow control on output.
                 * Transmit XON1, XOFF1
                 */
-               if (termios->c_iflag & IXOFF)
+               if (termios->c_iflag & IXOFF) {
+                       up->port.status |= UPSTAT_AUTOXOFF;
                        priv->efr |= OMAP_UART_SW_TX;
+               }
 
                /*
                 * IXANY Flag:
@@ -450,18 +474,18 @@ static void omap_8250_set_termios(struct uart_port *port,
 static void omap_8250_pm(struct uart_port *port, unsigned int state,
                         unsigned int oldstate)
 {
-       struct uart_8250_port *up =
-               container_of(port, struct uart_8250_port, port);
-       struct omap8250_priv *priv = up->port.private_data;
+       struct uart_8250_port *up = up_to_u8250p(port);
+       u8 efr;
 
        pm_runtime_get_sync(port->dev);
        serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
-       serial_out(up, UART_EFR, priv->efr | UART_EFR_ECB);
+       efr = serial_in(up, UART_EFR);
+       serial_out(up, UART_EFR, efr | UART_EFR_ECB);
        serial_out(up, UART_LCR, 0);
 
        serial_out(up, UART_IER, (state != 0) ? UART_IERX_SLEEP : 0);
        serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
-       serial_out(up, UART_EFR, priv->efr);
+       serial_out(up, UART_EFR, efr);
        serial_out(up, UART_LCR, 0);
 
        pm_runtime_mark_last_busy(port->dev);
@@ -1007,6 +1031,7 @@ static int omap8250_probe(struct platform_device *pdev)
        up.capabilities |= UART_CAP_RPM;
 #endif
        up.port.set_termios = omap_8250_set_termios;
+       up.port.set_mctrl = omap8250_set_mctrl;
        up.port.pm = omap_8250_pm;
        up.port.startup = omap_8250_startup;
        up.port.shutdown = omap_8250_shutdown;
@@ -1248,6 +1273,46 @@ static int omap8250_runtime_resume(struct device *dev)
 }
 #endif
 
+#ifdef CONFIG_SERIAL_8250_OMAP_TTYO_FIXUP
+static int __init omap8250_console_fixup(void)
+{
+       char *omap_str;
+       char *options;
+       u8 idx;
+
+       if (strstr(boot_command_line, "console=ttyS"))
+               /* user set a ttyS based name for the console */
+               return 0;
+
+       omap_str = strstr(boot_command_line, "console=ttyO");
+       if (!omap_str)
+               /* user did not set ttyO based console, so we don't care */
+               return 0;
+
+       omap_str += 12;
+       if ('0' <= *omap_str && *omap_str <= '9')
+               idx = *omap_str - '0';
+       else
+               return 0;
+
+       omap_str++;
+       if (omap_str[0] == ',') {
+               omap_str++;
+               options = omap_str;
+       } else {
+               options = NULL;
+       }
+
+       add_preferred_console("ttyS", idx, options);
+       pr_err("WARNING: Your 'console=ttyO%d' has been replaced by 'ttyS%d'\n",
+              idx, idx);
+       pr_err("This ensures that you still see kernel messages. Please\n");
+       pr_err("update your kernel commandline.\n");
+       return 0;
+}
+console_initcall(omap8250_console_fixup);
+#endif
+
 static const struct dev_pm_ops omap8250_dev_pm_ops = {
        SET_SYSTEM_SLEEP_PM_OPS(omap8250_suspend, omap8250_resume)
        SET_RUNTIME_PM_OPS(omap8250_runtime_suspend,
@@ -1269,7 +1334,6 @@ static struct platform_driver omap8250_platform_driver = {
                .name           = "omap8250",
                .pm             = &omap8250_dev_pm_ops,
                .of_match_table = omap8250_dt_ids,
-               .owner          = THIS_MODULE,
        },
        .probe                  = omap8250_probe,
        .remove                 = omap8250_remove,
index d1f8dc6aabcbe5bca9b6b6bbfc14e6f3f75e60c5..daf2c82984e95d94d806b175bc59944ac1cd9713 100644 (file)
@@ -221,13 +221,13 @@ pci_hp_diva_setup(struct serial_private *priv,
  */
 static int pci_inteli960ni_init(struct pci_dev *dev)
 {
-       unsigned long oldval;
+       u32 oldval;
 
        if (!(dev->subsystem_device & 0x1000))
                return -ENODEV;
 
        /* is firmware started? */
-       pci_read_config_dword(dev, 0x44, (void *)&oldval);
+       pci_read_config_dword(dev, 0x44, &oldval);
        if (oldval == 0x00001000L) { /* RESET value */
                dev_dbg(&dev->dev, "Local i960 firmware missing\n");
                return -ENODEV;
index 682a2fbe5c065be61c350088c86f16ba5a89ceff..50a09cd76d50a1d0b4709f5e487361ab76ac0ded 100644 (file)
@@ -426,7 +426,7 @@ static int serial_pnp_guess_board(struct pnp_dev *dev)
 static int
 serial_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id)
 {
-       struct uart_8250_port uart;
+       struct uart_8250_port uart, *port;
        int ret, line, flags = dev_id->driver_data;
 
        if (flags & UNKNOWN_DEV) {
@@ -471,6 +471,10 @@ serial_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id)
        if (line < 0 || (flags & CIR_PORT))
                return -ENODEV;
 
+       port = serial8250_get_port(line);
+       if (uart_console(&port->port))
+               dev->capabilities |= PNP_CONSOLE;
+
        pnp_set_drvdata(dev, (void *)((long)line + 1));
        return 0;
 }
@@ -478,6 +482,8 @@ serial_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id)
 static void serial_pnp_remove(struct pnp_dev *dev)
 {
        long line = (long)pnp_get_drvdata(dev);
+
+       dev->capabilities &= ~PNP_CONSOLE;
        if (line)
                serial8250_unregister_port(line - 1);
 }
index 0fcbcd29502fc0adda09ccef7227006e82ff1c49..6f7f2d753defe9695925ea9aaa0f503066858e55 100644 (file)
@@ -308,6 +308,25 @@ config SERIAL_8250_OMAP
 
          This driver uses ttyS instead of ttyO.
 
+config SERIAL_8250_OMAP_TTYO_FIXUP
+       bool "Replace ttyO with ttyS"
+       depends on SERIAL_8250_OMAP=y && SERIAL_8250_CONSOLE
+       default y
+       help
+         This option replaces the "console=ttyO" argument with the matching
+         ttyS argument if the user did not specified it on the command line.
+         This ensures that the user can see the kernel output during boot
+         which he wouldn't see otherwise. The getty has still to be configured
+         for ttyS instead of ttyO regardless of this option.
+         This option is intended for people who "automatically" enable this
+         driver without knowing that this driver requires a different console=
+         argument. If you read this, please keep this option disabled and
+         instead update your kernel command line. If you prepare a kernel for a
+         distribution or other kind of larger user base then you probably want
+         to keep this option enabled. Otherwise people might complain about a
+         not booting kernel because the serial console remains silent in case
+         they forgot to update the command line.
+
 config SERIAL_8250_FINTEK
        tristate "Support for Fintek F81216A LPC to 4 UART"
        depends on SERIAL_8250 && PNP
index c79b43cd6014a9916494230940b548f432b2e290..5d916c7a216b86829d4f10190787583258f3852e 100644 (file)
@@ -241,6 +241,7 @@ config SERIAL_SAMSUNG
        tristate "Samsung SoC serial support"
        depends on PLAT_SAMSUNG || ARCH_EXYNOS
        select SERIAL_CORE
+       select SERIAL_EARLYCON
        help
          Support for the on-chip UARTs on the Samsung S3C24XX series CPUs,
          providing /dev/ttySAC0, 1 and 2 (note, some machines may not
@@ -482,16 +483,6 @@ config SERIAL_SA1100_CONSOLE
          your boot loader (lilo or loadlin) about how to pass options to the
          kernel at boot time.)
 
-config SERIAL_MRST_MAX3110
-       tristate "SPI UART driver for Max3110"
-       depends on SPI_DW_PCI
-       select SERIAL_CORE
-       select SERIAL_CORE_CONSOLE
-       help
-         This is the UART protocol driver for the MAX3110 device on
-         the Intel Moorestown platform. On other systems use the max3100
-         driver.
-
 config SERIAL_MFD_HSU
        tristate "Medfield High Speed UART support"
        depends on PCI
@@ -1094,6 +1085,16 @@ config SERIAL_VT8500_CONSOLE
        depends on SERIAL_VT8500=y
        select SERIAL_CORE_CONSOLE
 
+config SERIAL_ETRAXFS
+       bool "ETRAX FS serial port support"
+       depends on ETRAX_ARCH_V32 && OF
+       select SERIAL_CORE
+
+config SERIAL_ETRAXFS_CONSOLE
+       bool "ETRAX FS serial console support"
+       depends on SERIAL_ETRAXFS
+       select SERIAL_CORE_CONSOLE
+
 config SERIAL_NETX
        tristate "NetX serial port support"
        depends on ARCH_NETX
@@ -1549,6 +1550,21 @@ config SERIAL_FSL_LPUART_CONSOLE
          If you have enabled the lpuart serial port on the Freescale SoCs,
          you can make it the console by answering Y to this option.
 
+config SERIAL_CONEXANT_DIGICOLOR
+       tristate "Conexant Digicolor CX92xxx USART serial port support"
+       depends on OF
+       select SERIAL_CORE
+       help
+         Support for the on-chip USART on Conexant Digicolor SoCs.
+
+config SERIAL_CONEXANT_DIGICOLOR_CONSOLE
+       bool "Console on Conexant Digicolor serial port"
+       depends on SERIAL_CONEXANT_DIGICOLOR=y
+       select SERIAL_CORE_CONSOLE
+       help
+         If you have enabled the USART serial port on Conexant Digicolor
+         SoCs, you can make it the console by answering Y to this option.
+
 config SERIAL_ST_ASC
        tristate "ST ASC serial port support"
        select SERIAL_CORE
@@ -1577,6 +1593,24 @@ config SERIAL_MEN_Z135
          This driver can also be build as a module. If so, the module will be called
          men_z135_uart.ko
 
+config SERIAL_SPRD
+       tristate "Support for Spreadtrum serial"
+       depends on ARCH_SPRD
+       select SERIAL_CORE
+       help
+         This enables the driver for the Spreadtrum's serial.
+
+config SERIAL_SPRD_CONSOLE
+       bool "Spreadtrum UART console support"
+       depends on SERIAL_SPRD=y
+       select SERIAL_CORE_CONSOLE
+       select SERIAL_EARLYCON
+       help
+         Support for early debug console using Spreadtrum's serial. This enables
+         the console before standard serial driver is probed. This is enabled
+         with "earlycon" on the kernel command line. The console is
+         enabled when early_param is processed.
+
 endmenu
 
 config SERIAL_MCTRL_GPIO
index 9a548acf5fdc99eaae1bb767d80b041cfb674adb..599be4b05a268801d31d94b6fb539e45f95af665 100644 (file)
@@ -51,6 +51,7 @@ obj-$(CONFIG_SERIAL_MPSC) += mpsc.o
 obj-$(CONFIG_SERIAL_MESON) += meson_uart.o
 obj-$(CONFIG_SERIAL_SB1250_DUART) += sb1250-duart.o
 obj-$(CONFIG_ETRAX_SERIAL) += crisv10.o
+obj-$(CONFIG_SERIAL_ETRAXFS) += etraxfs-uart.o
 obj-$(CONFIG_SERIAL_SCCNXP) += sccnxp.o
 obj-$(CONFIG_SERIAL_SC16IS7XX) += sc16is7xx.o
 obj-$(CONFIG_SERIAL_JSM) += jsm/
@@ -77,7 +78,6 @@ obj-$(CONFIG_SERIAL_TIMBERDALE)       += timbuart.o
 obj-$(CONFIG_SERIAL_GRLIB_GAISLER_APBUART) += apbuart.o
 obj-$(CONFIG_SERIAL_ALTERA_JTAGUART) += altera_jtaguart.o
 obj-$(CONFIG_SERIAL_VT8500) += vt8500_serial.o
-obj-$(CONFIG_SERIAL_MRST_MAX3110)      += mrst_max3110.o
 obj-$(CONFIG_SERIAL_MFD_HSU)   += mfd.o
 obj-$(CONFIG_SERIAL_IFX6X60)   += ifx6x60.o
 obj-$(CONFIG_SERIAL_PCH_UART)  += pch_uart.o
@@ -92,7 +92,9 @@ obj-$(CONFIG_SERIAL_EFM32_UART) += efm32-uart.o
 obj-$(CONFIG_SERIAL_ARC)       += arc_uart.o
 obj-$(CONFIG_SERIAL_RP2)       += rp2.o
 obj-$(CONFIG_SERIAL_FSL_LPUART)        += fsl_lpuart.o
+obj-$(CONFIG_SERIAL_CONEXANT_DIGICOLOR)        += digicolor-usart.o
 obj-$(CONFIG_SERIAL_MEN_Z135)  += men_z135_uart.o
+obj-$(CONFIG_SERIAL_SPRD) += sprd_serial.o
 
 # GPIOLIB helpers for modem control lines
 obj-$(CONFIG_SERIAL_MCTRL_GPIO)        += serial_mctrl_gpio.o
index 192d0435bb8683180f29d50f8aa9f39c28ac5e15..0fefdd8931a2a29ab77ee4a39433a35b2eb31637 100644 (file)
@@ -441,6 +441,7 @@ static int altera_jtaguart_probe(struct platform_device *pdev)
        port->iotype = SERIAL_IO_MEM;
        port->ops = &altera_jtaguart_ops;
        port->flags = UPF_BOOT_AUTOCONF;
+       port->dev = &pdev->dev;
 
        uart_add_one_port(&altera_jtaguart_driver, port);
 
index eb15a50623cb381ce9d98b9a1f56d78c14255284..b2859fe07e141a2a578e741412a2bd867f2c5ba2 100644 (file)
@@ -589,6 +589,7 @@ static int altera_uart_probe(struct platform_device *pdev)
        port->iotype = SERIAL_IO_MEM;
        port->ops = &altera_uart_ops;
        port->flags = UPF_BOOT_AUTOCONF;
+       port->dev = &pdev->dev;
 
        platform_set_drvdata(pdev, port);
 
index 4d848a29e2234588ca2095118afb702ee55a69e7..846552bff67d6f005c3966e80368134dcd9ab27f 100644 (file)
@@ -341,13 +341,37 @@ static u_int atmel_tx_empty(struct uart_port *port)
 static void atmel_set_mctrl(struct uart_port *port, u_int mctrl)
 {
        unsigned int control = 0;
-       unsigned int mode;
+       unsigned int mode = UART_GET_MR(port);
+       unsigned int rts_paused, rts_ready;
        struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
 
+       /* override mode to RS485 if needed, otherwise keep the current mode */
+       if (port->rs485.flags & SER_RS485_ENABLED) {
+               if ((port->rs485.delay_rts_after_send) > 0)
+                       UART_PUT_TTGR(port, port->rs485.delay_rts_after_send);
+               mode &= ~ATMEL_US_USMODE;
+               mode |= ATMEL_US_USMODE_RS485;
+       }
+
+       /* set the RTS line state according to the mode */
+       if ((mode & ATMEL_US_USMODE) == ATMEL_US_USMODE_HWHS) {
+               /* force RTS line to high level */
+               rts_paused = ATMEL_US_RTSEN;
+
+               /* give the control of the RTS line back to the hardware */
+               rts_ready = ATMEL_US_RTSDIS;
+       } else {
+               /* force RTS line to high level */
+               rts_paused = ATMEL_US_RTSDIS;
+
+               /* force RTS line to low level */
+               rts_ready = ATMEL_US_RTSEN;
+       }
+
        if (mctrl & TIOCM_RTS)
-               control |= ATMEL_US_RTSEN;
+               control |= rts_ready;
        else
-               control |= ATMEL_US_RTSDIS;
+               control |= rts_paused;
 
        if (mctrl & TIOCM_DTR)
                control |= ATMEL_US_DTREN;
@@ -359,23 +383,12 @@ static void atmel_set_mctrl(struct uart_port *port, u_int mctrl)
        mctrl_gpio_set(atmel_port->gpios, mctrl);
 
        /* Local loopback mode? */
-       mode = UART_GET_MR(port) & ~ATMEL_US_CHMODE;
+       mode &= ~ATMEL_US_CHMODE;
        if (mctrl & TIOCM_LOOP)
                mode |= ATMEL_US_CHMODE_LOC_LOOP;
        else
                mode |= ATMEL_US_CHMODE_NORMAL;
 
-       /* Resetting serial mode to RS232 (0x0) */
-       mode &= ~ATMEL_US_USMODE;
-
-       if (port->rs485.flags & SER_RS485_ENABLED) {
-               dev_dbg(port->dev, "Setting UART to RS485\n");
-               if ((port->rs485.delay_rts_after_send) > 0)
-                       UART_PUT_TTGR(port, port->rs485.delay_rts_after_send);
-               mode |= ATMEL_US_USMODE_RS485;
-       } else {
-               dev_dbg(port->dev, "Setting UART to RS232\n");
-       }
        UART_PUT_MR(port, mode);
 }
 
@@ -725,7 +738,11 @@ static void atmel_complete_tx_dma(void *arg)
        if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
                uart_write_wakeup(port);
 
-       /* Do we really need this? */
+       /*
+        * xmit is a circular buffer so, if we have just send data from
+        * xmit->tail to the end of xmit->buf, now we have to transmit the
+        * remaining data from the beginning of xmit->buf to xmit->head.
+        */
        if (!uart_circ_empty(xmit))
                tasklet_schedule(&atmel_port->tasklet);
 
@@ -784,17 +801,17 @@ static void atmel_tx_dma(struct uart_port *port)
                BUG_ON(!sg_dma_len(sg));
 
                desc = dmaengine_prep_slave_sg(chan,
-                                               sg,
-                                               1,
-                                               DMA_MEM_TO_DEV,
-                                               DMA_PREP_INTERRUPT |
-                                               DMA_CTRL_ACK);
+                                              sg,
+                                              1,
+                                              DMA_MEM_TO_DEV,
+                                              DMA_PREP_INTERRUPT |
+                                              DMA_CTRL_ACK);
                if (!desc) {
                        dev_err(port->dev, "Failed to send via dma!\n");
                        return;
                }
 
-               dma_sync_sg_for_device(port->dev, sg, 1, DMA_MEM_TO_DEV);
+               dma_sync_sg_for_device(port->dev, sg, 1, DMA_TO_DEVICE);
 
                atmel_port->desc_tx = desc;
                desc->callback = atmel_complete_tx_dma;
@@ -927,7 +944,7 @@ static void atmel_rx_from_dma(struct uart_port *port)
        dma_sync_sg_for_cpu(port->dev,
                            &atmel_port->sg_rx,
                            1,
-                           DMA_DEV_TO_MEM);
+                           DMA_FROM_DEVICE);
 
        /*
         * ring->head points to the end of data already written by the DMA.
@@ -974,7 +991,7 @@ static void atmel_rx_from_dma(struct uart_port *port)
        dma_sync_sg_for_device(port->dev,
                               &atmel_port->sg_rx,
                               1,
-                              DMA_DEV_TO_MEM);
+                              DMA_FROM_DEVICE);
 
        /*
         * Drop the lock here since it might end up calling
@@ -1012,13 +1029,13 @@ static int atmel_prepare_rx_dma(struct uart_port *port)
        /* UART circular rx buffer is an aligned page. */
        BUG_ON((int)port->state->xmit.buf & ~PAGE_MASK);
        sg_set_page(&atmel_port->sg_rx,
-                       virt_to_page(ring->buf),
-                       ATMEL_SERIAL_RINGSIZE,
-                       (int)ring->buf & ~PAGE_MASK);
-                       nent = dma_map_sg(port->dev,
-                                       &atmel_port->sg_rx,
-                                       1,
-                                       DMA_FROM_DEVICE);
+                   virt_to_page(ring->buf),
+                   ATMEL_SERIAL_RINGSIZE,
+                   (int)ring->buf & ~PAGE_MASK);
+       nent = dma_map_sg(port->dev,
+                         &atmel_port->sg_rx,
+                         1,
+                         DMA_FROM_DEVICE);
 
        if (!nent) {
                dev_dbg(port->dev, "need to release resource of dma\n");
@@ -1047,11 +1064,11 @@ static int atmel_prepare_rx_dma(struct uart_port *port)
         * each one is half ring buffer size
         */
        desc = dmaengine_prep_dma_cyclic(atmel_port->chan_rx,
-                               sg_dma_address(&atmel_port->sg_rx),
-                               sg_dma_len(&atmel_port->sg_rx),
-                               sg_dma_len(&atmel_port->sg_rx)/2,
-                               DMA_DEV_TO_MEM,
-                               DMA_PREP_INTERRUPT);
+                                        sg_dma_address(&atmel_port->sg_rx),
+                                        sg_dma_len(&atmel_port->sg_rx),
+                                        sg_dma_len(&atmel_port->sg_rx)/2,
+                                        DMA_DEV_TO_MEM,
+                                        DMA_PREP_INTERRUPT);
        desc->callback = atmel_complete_rx_dma;
        desc->callback_param = port;
        atmel_port->desc_rx = desc;
@@ -1921,12 +1938,14 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios,
                              struct ktermios *old)
 {
        unsigned long flags;
-       unsigned int mode, imr, quot, baud;
+       unsigned int old_mode, mode, imr, quot, baud;
 
-       /* Get current mode register */
-       mode = UART_GET_MR(port) & ~(ATMEL_US_USCLKS | ATMEL_US_CHRL
-                                       | ATMEL_US_NBSTOP | ATMEL_US_PAR
-                                       | ATMEL_US_USMODE);
+       /* save the current mode register */
+       mode = old_mode = UART_GET_MR(port);
+
+       /* reset the mode, clock divisor, parity, stop bits and data size */
+       mode &= ~(ATMEL_US_USCLKS | ATMEL_US_CHRL | ATMEL_US_NBSTOP |
+                 ATMEL_US_PAR | ATMEL_US_USMODE);
 
        baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16);
        quot = uart_get_divisor(port, baud);
@@ -1971,12 +1990,6 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios,
        } else
                mode |= ATMEL_US_PAR_NONE;
 
-       /* hardware handshake (RTS/CTS) */
-       if (termios->c_cflag & CRTSCTS)
-               mode |= ATMEL_US_USMODE_HWHS;
-       else
-               mode |= ATMEL_US_USMODE_NORMAL;
-
        spin_lock_irqsave(&port->lock, flags);
 
        port->read_status_mask = ATMEL_US_OVRE;
@@ -2020,18 +2033,40 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios,
        /* disable receiver and transmitter */
        UART_PUT_CR(port, ATMEL_US_TXDIS | ATMEL_US_RXDIS);
 
-       /* Resetting serial mode to RS232 (0x0) */
-       mode &= ~ATMEL_US_USMODE;
-
+       /* mode */
        if (port->rs485.flags & SER_RS485_ENABLED) {
                if ((port->rs485.delay_rts_after_send) > 0)
                        UART_PUT_TTGR(port, port->rs485.delay_rts_after_send);
                mode |= ATMEL_US_USMODE_RS485;
+       } else if (termios->c_cflag & CRTSCTS) {
+               /* RS232 with hardware handshake (RTS/CTS) */
+               mode |= ATMEL_US_USMODE_HWHS;
+       } else {
+               /* RS232 without hadware handshake */
+               mode |= ATMEL_US_USMODE_NORMAL;
        }
 
-       /* set the parity, stop bits and data size */
+       /* set the mode, clock divisor, parity, stop bits and data size */
        UART_PUT_MR(port, mode);
 
+       /*
+        * when switching the mode, set the RTS line state according to the
+        * new mode, otherwise keep the former state
+        */
+       if ((old_mode & ATMEL_US_USMODE) != (mode & ATMEL_US_USMODE)) {
+               unsigned int rts_state;
+
+               if ((mode & ATMEL_US_USMODE) == ATMEL_US_USMODE_HWHS) {
+                       /* let the hardware control the RTS line */
+                       rts_state = ATMEL_US_RTSDIS;
+               } else {
+                       /* force RTS line to low level */
+                       rts_state = ATMEL_US_RTSEN;
+               }
+
+               UART_PUT_CR(port, rts_state);
+       }
+
        /* set the baud rate */
        UART_PUT_BRGR(port, quot);
        UART_PUT_CR(port, ATMEL_US_RSTSTA | ATMEL_US_RSTRX);
@@ -2565,7 +2600,7 @@ static int atmel_serial_probe(struct platform_device *pdev)
 
        ret = atmel_init_port(port, pdev);
        if (ret)
-               goto err;
+               goto err_clear_bit;
 
        if (!atmel_use_pdc_rx(&port->uart)) {
                ret = -ENOMEM;
@@ -2596,6 +2631,12 @@ static int atmel_serial_probe(struct platform_device *pdev)
        device_init_wakeup(&pdev->dev, 1);
        platform_set_drvdata(pdev, port);
 
+       /*
+        * The peripheral clock has been disabled by atmel_init_port():
+        * enable it before accessing I/O registers
+        */
+       clk_prepare_enable(port->clk);
+
        if (rs485_enabled) {
                UART_PUT_MR(&port->uart, ATMEL_US_USMODE_NORMAL);
                UART_PUT_CR(&port->uart, ATMEL_US_RTSEN);
@@ -2606,6 +2647,12 @@ static int atmel_serial_probe(struct platform_device *pdev)
         */
        atmel_get_ip_name(&port->uart);
 
+       /*
+        * The peripheral clock can now safely be disabled till the port
+        * is used
+        */
+       clk_disable_unprepare(port->clk);
+
        return 0;
 
 err_add_port:
@@ -2616,6 +2663,8 @@ err_alloc_ring:
                clk_put(port->clk);
                port->clk = NULL;
        }
+err_clear_bit:
+       clear_bit(port->uart.line, atmel_ports_in_use);
 err:
        return ret;
 }
diff --git a/drivers/tty/serial/digicolor-usart.c b/drivers/tty/serial/digicolor-usart.c
new file mode 100644 (file)
index 0000000..a80cdad
--- /dev/null
@@ -0,0 +1,560 @@
+/*
+ *  Driver for Conexant Digicolor serial ports (USART)
+ *
+ * Author: Baruch Siach <baruch@tkos.co.il>
+ *
+ * Copyright (C) 2014 Paradox Innovation Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/console.h>
+#include <linux/serial_core.h>
+#include <linux/serial.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/workqueue.h>
+
+#define UA_ENABLE                      0x00
+#define UA_ENABLE_ENABLE               BIT(0)
+
+#define UA_CONTROL                     0x01
+#define UA_CONTROL_RX_ENABLE           BIT(0)
+#define UA_CONTROL_TX_ENABLE           BIT(1)
+#define UA_CONTROL_SOFT_RESET          BIT(2)
+
+#define UA_STATUS                      0x02
+#define UA_STATUS_PARITY_ERR           BIT(0)
+#define UA_STATUS_FRAME_ERR            BIT(1)
+#define UA_STATUS_OVERRUN_ERR          BIT(2)
+#define UA_STATUS_TX_READY             BIT(6)
+
+#define UA_CONFIG                      0x03
+#define UA_CONFIG_CHAR_LEN             BIT(0)
+#define UA_CONFIG_STOP_BITS            BIT(1)
+#define UA_CONFIG_PARITY               BIT(2)
+#define UA_CONFIG_ODD_PARITY           BIT(4)
+
+#define UA_EMI_REC                     0x04
+
+#define UA_HBAUD_LO                    0x08
+#define UA_HBAUD_HI                    0x09
+
+#define UA_STATUS_FIFO                 0x0a
+#define UA_STATUS_FIFO_RX_EMPTY                BIT(2)
+#define UA_STATUS_FIFO_RX_INT_ALMOST   BIT(3)
+#define UA_STATUS_FIFO_TX_FULL         BIT(4)
+#define UA_STATUS_FIFO_TX_INT_ALMOST   BIT(7)
+
+#define UA_CONFIG_FIFO                 0x0b
+#define UA_CONFIG_FIFO_RX_THRESH       7
+#define UA_CONFIG_FIFO_RX_FIFO_MODE    BIT(3)
+#define UA_CONFIG_FIFO_TX_FIFO_MODE    BIT(7)
+
+#define UA_INTFLAG_CLEAR               0x1c
+#define UA_INTFLAG_SET                 0x1d
+#define UA_INT_ENABLE                  0x1e
+#define UA_INT_STATUS                  0x1f
+
+#define UA_INT_TX                      BIT(0)
+#define UA_INT_RX                      BIT(1)
+
+#define DIGICOLOR_USART_NR             3
+
+/*
+ * We use the 16 bytes hardware FIFO to buffer Rx traffic. Rx interrupt is
+ * only produced when the FIFO is filled more than a certain configurable
+ * threshold. Unfortunately, there is no way to set this threshold below half
+ * FIFO. This means that we must periodically poll the FIFO status register to
+ * see whether there are waiting Rx bytes.
+ */
+
+struct digicolor_port {
+       struct uart_port port;
+       struct delayed_work rx_poll_work;
+};
+
+static struct uart_port *digicolor_ports[DIGICOLOR_USART_NR];
+
+static bool digicolor_uart_tx_full(struct uart_port *port)
+{
+       return !!(readb_relaxed(port->membase + UA_STATUS_FIFO) &
+                 UA_STATUS_FIFO_TX_FULL);
+}
+
+static bool digicolor_uart_rx_empty(struct uart_port *port)
+{
+       return !!(readb_relaxed(port->membase + UA_STATUS_FIFO) &
+                 UA_STATUS_FIFO_RX_EMPTY);
+}
+
+static void digicolor_uart_stop_tx(struct uart_port *port)
+{
+       u8 int_enable = readb_relaxed(port->membase + UA_INT_ENABLE);
+
+       int_enable &= ~UA_INT_TX;
+       writeb_relaxed(int_enable, port->membase + UA_INT_ENABLE);
+}
+
+static void digicolor_uart_start_tx(struct uart_port *port)
+{
+       u8 int_enable = readb_relaxed(port->membase + UA_INT_ENABLE);
+
+       int_enable |= UA_INT_TX;
+       writeb_relaxed(int_enable, port->membase + UA_INT_ENABLE);
+}
+
+static void digicolor_uart_stop_rx(struct uart_port *port)
+{
+       u8 int_enable = readb_relaxed(port->membase + UA_INT_ENABLE);
+
+       int_enable &= ~UA_INT_RX;
+       writeb_relaxed(int_enable, port->membase + UA_INT_ENABLE);
+}
+
+static void digicolor_rx_poll(struct work_struct *work)
+{
+       struct digicolor_port *dp =
+               container_of(to_delayed_work(work),
+                            struct digicolor_port, rx_poll_work);
+
+       if (!digicolor_uart_rx_empty(&dp->port))
+               /* force RX interrupt */
+               writeb_relaxed(UA_INT_RX, dp->port.membase + UA_INTFLAG_SET);
+
+       schedule_delayed_work(&dp->rx_poll_work, msecs_to_jiffies(100));
+}
+
+static void digicolor_uart_rx(struct uart_port *port)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&port->lock, flags);
+
+       while (1) {
+               u8 status, ch;
+               unsigned int ch_flag;
+
+               if (digicolor_uart_rx_empty(port))
+                       break;
+
+               ch = readb_relaxed(port->membase + UA_EMI_REC);
+               status = readb_relaxed(port->membase + UA_STATUS);
+
+               port->icount.rx++;
+               ch_flag = TTY_NORMAL;
+
+               if (status) {
+                       if (status & UA_STATUS_PARITY_ERR)
+                               port->icount.parity++;
+                       else if (status & UA_STATUS_FRAME_ERR)
+                               port->icount.frame++;
+                       else if (status & UA_STATUS_OVERRUN_ERR)
+                               port->icount.overrun++;
+
+                       status &= port->read_status_mask;
+
+                       if (status & UA_STATUS_PARITY_ERR)
+                               ch_flag = TTY_PARITY;
+                       else if (status & UA_STATUS_FRAME_ERR)
+                               ch_flag = TTY_FRAME;
+                       else if (status & UA_STATUS_OVERRUN_ERR)
+                               ch_flag = TTY_OVERRUN;
+               }
+
+               if (status & port->ignore_status_mask)
+                       continue;
+
+               uart_insert_char(port, status, UA_STATUS_OVERRUN_ERR, ch,
+                                ch_flag);
+       }
+
+       spin_unlock_irqrestore(&port->lock, flags);
+
+       tty_flip_buffer_push(&port->state->port);
+}
+
+static void digicolor_uart_tx(struct uart_port *port)
+{
+       struct circ_buf *xmit = &port->state->xmit;
+       unsigned long flags;
+
+       if (digicolor_uart_tx_full(port))
+               return;
+
+       spin_lock_irqsave(&port->lock, flags);
+
+       if (port->x_char) {
+               writeb_relaxed(port->x_char, port->membase + UA_EMI_REC);
+               port->icount.tx++;
+               port->x_char = 0;
+               goto out;
+       }
+
+       if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
+               digicolor_uart_stop_tx(port);
+               goto out;
+       }
+
+       while (!uart_circ_empty(xmit)) {
+               writeb(xmit->buf[xmit->tail], port->membase + UA_EMI_REC);
+               xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+               port->icount.tx++;
+
+               if (digicolor_uart_tx_full(port))
+                       break;
+       }
+
+       if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+               uart_write_wakeup(port);
+
+out:
+       spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static irqreturn_t digicolor_uart_int(int irq, void *dev_id)
+{
+       struct uart_port *port = dev_id;
+       u8 int_status = readb_relaxed(port->membase + UA_INT_STATUS);
+
+       writeb_relaxed(UA_INT_RX | UA_INT_TX,
+                      port->membase + UA_INTFLAG_CLEAR);
+
+       if (int_status & UA_INT_RX)
+               digicolor_uart_rx(port);
+       if (int_status & UA_INT_TX)
+               digicolor_uart_tx(port);
+
+       return IRQ_HANDLED;
+}
+
+static unsigned int digicolor_uart_tx_empty(struct uart_port *port)
+{
+       u8 status = readb_relaxed(port->membase + UA_STATUS);
+
+       return (status & UA_STATUS_TX_READY) ? TIOCSER_TEMT : 0;
+}
+
+static unsigned int digicolor_uart_get_mctrl(struct uart_port *port)
+{
+       return TIOCM_CTS;
+}
+
+static void digicolor_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+}
+
+static void digicolor_uart_break_ctl(struct uart_port *port, int state)
+{
+}
+
+static int digicolor_uart_startup(struct uart_port *port)
+{
+       struct digicolor_port *dp =
+               container_of(port, struct digicolor_port, port);
+
+       writeb_relaxed(UA_ENABLE_ENABLE, port->membase + UA_ENABLE);
+       writeb_relaxed(UA_CONTROL_SOFT_RESET, port->membase + UA_CONTROL);
+       writeb_relaxed(0, port->membase + UA_CONTROL);
+
+       writeb_relaxed(UA_CONFIG_FIFO_RX_FIFO_MODE
+                      | UA_CONFIG_FIFO_TX_FIFO_MODE | UA_CONFIG_FIFO_RX_THRESH,
+                      port->membase + UA_CONFIG_FIFO);
+       writeb_relaxed(UA_STATUS_FIFO_RX_INT_ALMOST,
+                      port->membase + UA_STATUS_FIFO);
+       writeb_relaxed(UA_CONTROL_RX_ENABLE | UA_CONTROL_TX_ENABLE,
+                      port->membase + UA_CONTROL);
+       writeb_relaxed(UA_INT_TX | UA_INT_RX,
+                      port->membase + UA_INT_ENABLE);
+
+       schedule_delayed_work(&dp->rx_poll_work, msecs_to_jiffies(100));
+
+       return 0;
+}
+
+static void digicolor_uart_shutdown(struct uart_port *port)
+{
+       struct digicolor_port *dp =
+               container_of(port, struct digicolor_port, port);
+
+       writeb_relaxed(0, port->membase + UA_ENABLE);
+       cancel_delayed_work_sync(&dp->rx_poll_work);
+}
+
+static void digicolor_uart_set_termios(struct uart_port *port,
+                                      struct ktermios *termios,
+                                      struct ktermios *old)
+{
+       unsigned int baud, divisor;
+       u8 config = 0;
+       unsigned long flags;
+
+       /* Mask termios capabilities we don't support */
+       termios->c_cflag &= ~CMSPAR;
+       termios->c_iflag &= ~(BRKINT | IGNBRK);
+
+       /* Limit baud rates so that we don't need the fractional divider */
+       baud = uart_get_baud_rate(port, termios, old,
+                                 port->uartclk / (0x10000*16),
+                                 port->uartclk / 256);
+       divisor = uart_get_divisor(port, baud) - 1;
+
+       switch (termios->c_cflag & CSIZE) {
+       case CS7:
+               break;
+       case CS8:
+       default:
+               config |= UA_CONFIG_CHAR_LEN;
+               break;
+       }
+
+       if (termios->c_cflag & CSTOPB)
+               config |= UA_CONFIG_STOP_BITS;
+
+       if (termios->c_cflag & PARENB) {
+               config |= UA_CONFIG_PARITY;
+               if (termios->c_cflag & PARODD)
+                       config |= UA_CONFIG_ODD_PARITY;
+       }
+
+       /* Set read status mask */
+       port->read_status_mask = UA_STATUS_OVERRUN_ERR;
+       if (termios->c_iflag & INPCK)
+               port->read_status_mask |= UA_STATUS_PARITY_ERR
+                       | UA_STATUS_FRAME_ERR;
+
+       /* Set status ignore mask */
+       port->ignore_status_mask = 0;
+       if (!(termios->c_cflag & CREAD))
+               port->ignore_status_mask |= UA_STATUS_OVERRUN_ERR
+                       | UA_STATUS_PARITY_ERR | UA_STATUS_FRAME_ERR;
+
+       spin_lock_irqsave(&port->lock, flags);
+
+       uart_update_timeout(port, termios->c_cflag, baud);
+
+       writeb_relaxed(config, port->membase + UA_CONFIG);
+       writeb_relaxed(divisor & 0xff, port->membase + UA_HBAUD_LO);
+       writeb_relaxed(divisor >> 8, port->membase + UA_HBAUD_HI);
+
+       spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static const char *digicolor_uart_type(struct uart_port *port)
+{
+       return (port->type == PORT_DIGICOLOR) ? "DIGICOLOR USART" : NULL;
+}
+
+static void digicolor_uart_config_port(struct uart_port *port, int flags)
+{
+       if (flags & UART_CONFIG_TYPE)
+               port->type = PORT_DIGICOLOR;
+}
+
+static void digicolor_uart_release_port(struct uart_port *port)
+{
+}
+
+static int digicolor_uart_request_port(struct uart_port *port)
+{
+       return 0;
+}
+
+static const struct uart_ops digicolor_uart_ops = {
+       .tx_empty       = digicolor_uart_tx_empty,
+       .set_mctrl      = digicolor_uart_set_mctrl,
+       .get_mctrl      = digicolor_uart_get_mctrl,
+       .stop_tx        = digicolor_uart_stop_tx,
+       .start_tx       = digicolor_uart_start_tx,
+       .stop_rx        = digicolor_uart_stop_rx,
+       .break_ctl      = digicolor_uart_break_ctl,
+       .startup        = digicolor_uart_startup,
+       .shutdown       = digicolor_uart_shutdown,
+       .set_termios    = digicolor_uart_set_termios,
+       .type           = digicolor_uart_type,
+       .config_port    = digicolor_uart_config_port,
+       .release_port   = digicolor_uart_release_port,
+       .request_port   = digicolor_uart_request_port,
+};
+
+static void digicolor_uart_console_putchar(struct uart_port *port, int ch)
+{
+       while (digicolor_uart_tx_full(port))
+               cpu_relax();
+
+       writeb_relaxed(ch, port->membase + UA_EMI_REC);
+}
+
+static void digicolor_uart_console_write(struct console *co, const char *c,
+                                        unsigned n)
+{
+       struct uart_port *port = digicolor_ports[co->index];
+       u8 status;
+       unsigned long flags;
+       int locked = 1;
+
+       if (oops_in_progress)
+               locked = spin_trylock_irqsave(&port->lock, flags);
+       else
+               spin_lock_irqsave(&port->lock, flags);
+
+       uart_console_write(port, c, n, digicolor_uart_console_putchar);
+
+       if (locked)
+               spin_unlock_irqrestore(&port->lock, flags);
+
+       /* Wait for transmitter to become empty */
+       do {
+               status = readb_relaxed(port->membase + UA_STATUS);
+       } while ((status & UA_STATUS_TX_READY) == 0);
+}
+
+static int digicolor_uart_console_setup(struct console *co, char *options)
+{
+       int baud = 115200, bits = 8, parity = 'n', flow = 'n';
+       struct uart_port *port;
+
+       if (co->index < 0 || co->index >= DIGICOLOR_USART_NR)
+               return -EINVAL;
+
+       port = digicolor_ports[co->index];
+       if (!port)
+               return -ENODEV;
+
+       if (options)
+               uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+       return uart_set_options(port, co, baud, parity, bits, flow);
+}
+
+static struct console digicolor_console = {
+       .name   = "ttyS",
+       .device = uart_console_device,
+       .write  = digicolor_uart_console_write,
+       .setup  = digicolor_uart_console_setup,
+       .flags  = CON_PRINTBUFFER,
+       .index  = -1,
+};
+
+static struct uart_driver digicolor_uart = {
+       .driver_name    = "digicolor-usart",
+       .dev_name       = "ttyS",
+       .nr             = DIGICOLOR_USART_NR,
+};
+
+static int digicolor_uart_probe(struct platform_device *pdev)
+{
+       struct device_node *np = pdev->dev.of_node;
+       int ret, index;
+       struct digicolor_port *dp;
+       struct resource *res;
+       struct clk *uart_clk;
+
+       if (!np) {
+               dev_err(&pdev->dev, "Missing device tree node\n");
+               return -ENXIO;
+       }
+
+       index = of_alias_get_id(np, "serial");
+       if (index < 0 || index >= DIGICOLOR_USART_NR)
+               return -EINVAL;
+
+       dp = devm_kzalloc(&pdev->dev, sizeof(*dp), GFP_KERNEL);
+       if (!dp)
+               return -ENOMEM;
+
+       uart_clk = devm_clk_get(&pdev->dev, NULL);
+       if (IS_ERR(uart_clk))
+               return PTR_ERR(uart_clk);
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       dp->port.mapbase = res->start;
+       dp->port.membase = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(dp->port.membase))
+               return PTR_ERR(dp->port.membase);
+
+       dp->port.irq = platform_get_irq(pdev, 0);
+       if (IS_ERR_VALUE(dp->port.irq))
+               return dp->port.irq;
+
+       dp->port.iotype = UPIO_MEM;
+       dp->port.uartclk = clk_get_rate(uart_clk);
+       dp->port.fifosize = 16;
+       dp->port.dev = &pdev->dev;
+       dp->port.ops = &digicolor_uart_ops;
+       dp->port.line = index;
+       dp->port.type = PORT_DIGICOLOR;
+       spin_lock_init(&dp->port.lock);
+
+       digicolor_ports[index] = &dp->port;
+       platform_set_drvdata(pdev, &dp->port);
+
+       INIT_DELAYED_WORK(&dp->rx_poll_work, digicolor_rx_poll);
+
+       ret = devm_request_irq(&pdev->dev, dp->port.irq, digicolor_uart_int, 0,
+                              dev_name(&pdev->dev), &dp->port);
+       if (ret)
+               return ret;
+
+       return uart_add_one_port(&digicolor_uart, &dp->port);
+}
+
+static int digicolor_uart_remove(struct platform_device *pdev)
+{
+       struct uart_port *port = platform_get_drvdata(pdev);
+
+       uart_remove_one_port(&digicolor_uart, port);
+
+       return 0;
+}
+
+static const struct of_device_id digicolor_uart_dt_ids[] = {
+       { .compatible = "cnxt,cx92755-usart", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, digicolor_uart_dt_ids);
+
+static struct platform_driver digicolor_uart_platform = {
+       .driver = {
+               .name           = "digicolor-usart",
+               .of_match_table = of_match_ptr(digicolor_uart_dt_ids),
+       },
+       .probe  = digicolor_uart_probe,
+       .remove = digicolor_uart_remove,
+};
+
+static int __init digicolor_uart_init(void)
+{
+       int ret;
+
+       if (IS_ENABLED(CONFIG_SERIAL_CONEXANT_DIGICOLOR_CONSOLE)) {
+               digicolor_uart.cons = &digicolor_console;
+               digicolor_console.data = &digicolor_uart;
+       }
+
+       ret = uart_register_driver(&digicolor_uart);
+       if (ret)
+               return ret;
+
+       return platform_driver_register(&digicolor_uart_platform);
+}
+module_init(digicolor_uart_init);
+
+static void __exit digicolor_uart_exit(void)
+{
+       platform_driver_unregister(&digicolor_uart_platform);
+       uart_unregister_driver(&digicolor_uart);
+}
+module_exit(digicolor_uart_exit);
+
+MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>");
+MODULE_DESCRIPTION("Conexant Digicolor USART serial driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/tty/serial/etraxfs-uart.c b/drivers/tty/serial/etraxfs-uart.c
new file mode 100644 (file)
index 0000000..a57301a
--- /dev/null
@@ -0,0 +1,996 @@
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/platform_device.h>
+#include <linux/serial_core.h>
+#include <linux/tty_flip.h>
+#include <linux/of.h>
+#include <linux/gpio.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
+#include <hwregs/ser_defs.h>
+
+#define DRV_NAME "etraxfs-uart"
+#define UART_NR CONFIG_ETRAX_SERIAL_PORTS
+
+#define MODIFY_REG(instance, reg, var)                         \
+       do {                                                    \
+               if (REG_RD_INT(ser, instance, reg) !=           \
+                   REG_TYPE_CONV(int, reg_ser_##reg, var))     \
+                       REG_WR(ser, instance, reg, var);        \
+       } while (0)
+
+struct uart_cris_port {
+       struct uart_port port;
+
+       int initialized;
+       int irq;
+
+       void __iomem *regi_ser;
+
+       struct gpio_desc *dtr_pin;
+       struct gpio_desc *dsr_pin;
+       struct gpio_desc *ri_pin;
+       struct gpio_desc *cd_pin;
+
+       int write_ongoing;
+};
+
+static struct uart_driver etraxfs_uart_driver;
+static struct uart_port *console_port;
+static int console_baud = 115200;
+static struct uart_cris_port *etraxfs_uart_ports[UART_NR];
+
+static void cris_serial_port_init(struct uart_port *port, int line);
+static void etraxfs_uart_stop_rx(struct uart_port *port);
+static inline void etraxfs_uart_start_tx_bottom(struct uart_port *port);
+
+#ifdef CONFIG_SERIAL_ETRAXFS_CONSOLE
+static void
+cris_console_write(struct console *co, const char *s, unsigned int count)
+{
+       struct uart_cris_port *up;
+       int i;
+       reg_ser_r_stat_din stat;
+       reg_ser_rw_tr_dma_en tr_dma_en, old;
+
+       up = etraxfs_uart_ports[co->index];
+
+       if (!up)
+               return;
+
+       /* Switch to manual mode. */
+       tr_dma_en = old = REG_RD(ser, up->regi_ser, rw_tr_dma_en);
+       if (tr_dma_en.en == regk_ser_yes) {
+               tr_dma_en.en = regk_ser_no;
+               REG_WR(ser, up->regi_ser, rw_tr_dma_en, tr_dma_en);
+       }
+
+       /* Send data. */
+       for (i = 0; i < count; i++) {
+               /* LF -> CRLF */
+               if (s[i] == '\n') {
+                       do {
+                               stat = REG_RD(ser, up->regi_ser, r_stat_din);
+                       } while (!stat.tr_rdy);
+                       REG_WR_INT(ser, up->regi_ser, rw_dout, '\r');
+               }
+               /* Wait until transmitter is ready and send. */
+               do {
+                       stat = REG_RD(ser, up->regi_ser, r_stat_din);
+               } while (!stat.tr_rdy);
+               REG_WR_INT(ser, up->regi_ser, rw_dout, s[i]);
+       }
+
+       /* Restore mode. */
+       if (tr_dma_en.en != old.en)
+               REG_WR(ser, up->regi_ser, rw_tr_dma_en, old);
+}
+
+static int __init
+cris_console_setup(struct console *co, char *options)
+{
+       struct uart_port *port;
+       int baud = 115200;
+       int bits = 8;
+       int parity = 'n';
+       int flow = 'n';
+
+       if (co->index < 0 || co->index >= UART_NR)
+               co->index = 0;
+       port = &etraxfs_uart_ports[co->index]->port;
+       console_port = port;
+
+       co->flags |= CON_CONSDEV;
+
+       if (options)
+               uart_parse_options(options, &baud, &parity, &bits, &flow);
+       console_baud = baud;
+       cris_serial_port_init(port, co->index);
+       uart_set_options(port, co, baud, parity, bits, flow);
+
+       return 0;
+}
+
+static struct tty_driver *cris_console_device(struct console *co, int *index)
+{
+       struct uart_driver *p = co->data;
+       *index = co->index;
+       return p->tty_driver;
+}
+
+static struct console cris_console = {
+       .name = "ttyS",
+       .write = cris_console_write,
+       .device = cris_console_device,
+       .setup = cris_console_setup,
+       .flags = CON_PRINTBUFFER,
+       .index = -1,
+       .data = &etraxfs_uart_driver,
+};
+#endif /* CONFIG_SERIAL_ETRAXFS_CONSOLE */
+
+static struct uart_driver etraxfs_uart_driver = {
+       .owner = THIS_MODULE,
+       .driver_name = "serial",
+       .dev_name = "ttyS",
+       .major = TTY_MAJOR,
+       .minor = 64,
+       .nr = UART_NR,
+#ifdef CONFIG_SERIAL_ETRAXFS_CONSOLE
+       .cons = &cris_console,
+#endif /* CONFIG_SERIAL_ETRAXFS_CONSOLE */
+};
+
+static inline int crisv32_serial_get_rts(struct uart_cris_port *up)
+{
+       void __iomem *regi_ser = up->regi_ser;
+       /*
+        * Return what the user has controlled rts to or
+        * what the pin is? (if auto_rts is used it differs during tx)
+        */
+       reg_ser_r_stat_din rstat = REG_RD(ser, regi_ser, r_stat_din);
+
+       return !(rstat.rts_n == regk_ser_active);
+}
+
+/*
+ * A set = 0 means 3.3V on the pin, bitvalue: 0=active, 1=inactive
+ *                                            0=0V    , 1=3.3V
+ */
+static inline void crisv32_serial_set_rts(struct uart_cris_port *up,
+                                         int set, int force)
+{
+       void __iomem *regi_ser = up->regi_ser;
+
+       unsigned long flags;
+       reg_ser_rw_rec_ctrl rec_ctrl;
+
+       local_irq_save(flags);
+       rec_ctrl = REG_RD(ser, regi_ser, rw_rec_ctrl);
+
+       if (set)
+               rec_ctrl.rts_n = regk_ser_active;
+       else
+               rec_ctrl.rts_n = regk_ser_inactive;
+       REG_WR(ser, regi_ser, rw_rec_ctrl, rec_ctrl);
+       local_irq_restore(flags);
+}
+
+static inline int crisv32_serial_get_cts(struct uart_cris_port *up)
+{
+       void __iomem *regi_ser = up->regi_ser;
+       reg_ser_r_stat_din rstat = REG_RD(ser, regi_ser, r_stat_din);
+
+       return (rstat.cts_n == regk_ser_active);
+}
+
+/*
+ * Send a single character for XON/XOFF purposes.  We do it in this separate
+ * function instead of the alternative support port.x_char, in the ...start_tx
+ * function, so we don't mix up this case with possibly enabling transmission
+ * of queued-up data (in case that's disabled after *receiving* an XOFF or
+ * negative CTS).  This function is used for both DMA and non-DMA case; see HW
+ * docs specifically blessing sending characters manually when DMA for
+ * transmission is enabled and running.  We may be asked to transmit despite
+ * the transmitter being disabled by a ..._stop_tx call so we need to enable
+ * it temporarily but restore the state afterwards.
+ */
+static void etraxfs_uart_send_xchar(struct uart_port *port, char ch)
+{
+       struct uart_cris_port *up = (struct uart_cris_port *)port;
+       reg_ser_rw_dout dout = { .data = ch };
+       reg_ser_rw_ack_intr ack_intr = { .tr_rdy = regk_ser_yes };
+       reg_ser_r_stat_din rstat;
+       reg_ser_rw_tr_ctrl prev_tr_ctrl, tr_ctrl;
+       void __iomem *regi_ser = up->regi_ser;
+       unsigned long flags;
+
+       /*
+        * Wait for tr_rdy in case a character is already being output.  Make
+        * sure we have integrity between the register reads and the writes
+        * below, but don't busy-wait with interrupts off and the port lock
+        * taken.
+        */
+       spin_lock_irqsave(&port->lock, flags);
+       do {
+               spin_unlock_irqrestore(&port->lock, flags);
+               spin_lock_irqsave(&port->lock, flags);
+               prev_tr_ctrl = tr_ctrl = REG_RD(ser, regi_ser, rw_tr_ctrl);
+               rstat = REG_RD(ser, regi_ser, r_stat_din);
+       } while (!rstat.tr_rdy);
+
+       /*
+        * Ack an interrupt if one was just issued for the previous character
+        * that was output.  This is required for non-DMA as the interrupt is
+        * used as the only indicator that the transmitter is ready and it
+        * isn't while this x_char is being transmitted.
+        */
+       REG_WR(ser, regi_ser, rw_ack_intr, ack_intr);
+
+       /* Enable the transmitter in case it was disabled. */
+       tr_ctrl.stop = 0;
+       REG_WR(ser, regi_ser, rw_tr_ctrl, tr_ctrl);
+
+       /*
+        * Finally, send the blessed character; nothing should stop it now,
+        * except for an xoff-detected state, which we'll handle below.
+        */
+       REG_WR(ser, regi_ser, rw_dout, dout);
+       up->port.icount.tx++;
+
+       /* There might be an xoff state to clear. */
+       rstat = REG_RD(ser, up->regi_ser, r_stat_din);
+
+       /*
+        * Clear any xoff state that *may* have been there to
+        * inhibit transmission of the character.
+        */
+       if (rstat.xoff_detect) {
+               reg_ser_rw_xoff_clr xoff_clr = { .clr = 1 };
+               reg_ser_rw_tr_dma_en tr_dma_en;
+
+               REG_WR(ser, regi_ser, rw_xoff_clr, xoff_clr);
+               tr_dma_en = REG_RD(ser, regi_ser, rw_tr_dma_en);
+
+               /*
+                * If we had an xoff state but cleared it, instead sneak in a
+                * disabled state for the transmitter, after the character we
+                * sent.  Thus we keep the port disabled, just as if the xoff
+                * state was still in effect (or actually, as if stop_tx had
+                * been called, as we stop DMA too).
+                */
+               prev_tr_ctrl.stop = 1;
+
+               tr_dma_en.en = 0;
+               REG_WR(ser, regi_ser, rw_tr_dma_en, tr_dma_en);
+       }
+
+       /* Restore "previous" enabled/disabled state of the transmitter. */
+       REG_WR(ser, regi_ser, rw_tr_ctrl, prev_tr_ctrl);
+
+       spin_unlock_irqrestore(&port->lock, flags);
+}
+
+/*
+ * Do not spin_lock_irqsave or disable interrupts by other means here; it's
+ * already done by the caller.
+ */
+static void etraxfs_uart_start_tx(struct uart_port *port)
+{
+       struct uart_cris_port *up = (struct uart_cris_port *)port;
+
+       /* we have already done below if a write is ongoing */
+       if (up->write_ongoing)
+               return;
+
+       /* Signal that write is ongoing */
+       up->write_ongoing = 1;
+
+       etraxfs_uart_start_tx_bottom(port);
+}
+
+static inline void etraxfs_uart_start_tx_bottom(struct uart_port *port)
+{
+       struct uart_cris_port *up = (struct uart_cris_port *)port;
+       void __iomem *regi_ser = up->regi_ser;
+       reg_ser_rw_tr_ctrl tr_ctrl;
+       reg_ser_rw_intr_mask intr_mask;
+
+       tr_ctrl = REG_RD(ser, regi_ser, rw_tr_ctrl);
+       tr_ctrl.stop = regk_ser_no;
+       REG_WR(ser, regi_ser, rw_tr_ctrl, tr_ctrl);
+       intr_mask = REG_RD(ser, regi_ser, rw_intr_mask);
+       intr_mask.tr_rdy = regk_ser_yes;
+       REG_WR(ser, regi_ser, rw_intr_mask, intr_mask);
+}
+
+/*
+ * This function handles both the DMA and non-DMA case by ordering the
+ * transmitter to stop of after the current character.  We don't need to wait
+ * for any such character to be completely transmitted; we do that where it
+ * matters, like in etraxfs_uart_set_termios.  Don't busy-wait here; see
+ * Documentation/serial/driver: this function is called within
+ * spin_lock_irq{,save} and thus separate ones would be disastrous (when SMP).
+ * There's no documented need to set the txd pin to any particular value;
+ * break setting is controlled solely by etraxfs_uart_break_ctl.
+ */
+static void etraxfs_uart_stop_tx(struct uart_port *port)
+{
+       struct uart_cris_port *up = (struct uart_cris_port *)port;
+       void __iomem *regi_ser = up->regi_ser;
+       reg_ser_rw_tr_ctrl tr_ctrl;
+       reg_ser_rw_intr_mask intr_mask;
+       reg_ser_rw_tr_dma_en tr_dma_en = {0};
+       reg_ser_rw_xoff_clr xoff_clr = {0};
+
+       /*
+        * For the non-DMA case, we'd get a tr_rdy interrupt that we're not
+        * interested in as we're not transmitting any characters.  For the
+        * DMA case, that interrupt is already turned off, but no reason to
+        * waste code on conditionals here.
+        */
+       intr_mask = REG_RD(ser, regi_ser, rw_intr_mask);
+       intr_mask.tr_rdy = regk_ser_no;
+       REG_WR(ser, regi_ser, rw_intr_mask, intr_mask);
+
+       tr_ctrl = REG_RD(ser, regi_ser, rw_tr_ctrl);
+       tr_ctrl.stop = 1;
+       REG_WR(ser, regi_ser, rw_tr_ctrl, tr_ctrl);
+
+       /*
+        * Always clear possible hardware xoff-detected state here, no need to
+        * unnecessary consider mctrl settings and when they change.  We clear
+        * it here rather than in start_tx: both functions are called as the
+        * effect of XOFF processing, but start_tx is also called when upper
+        * levels tell the driver that there are more characters to send, so
+        * avoid adding code there.
+        */
+       xoff_clr.clr = 1;
+       REG_WR(ser, regi_ser, rw_xoff_clr, xoff_clr);
+
+       /*
+        * Disable transmitter DMA, so that if we're in XON/XOFF, we can send
+        * those single characters without also giving go-ahead for queued up
+        * DMA data.
+        */
+       tr_dma_en.en = 0;
+       REG_WR(ser, regi_ser, rw_tr_dma_en, tr_dma_en);
+
+       /*
+        * Make sure that write_ongoing is reset when stopping tx.
+        */
+       up->write_ongoing = 0;
+}
+
+static void etraxfs_uart_stop_rx(struct uart_port *port)
+{
+       struct uart_cris_port *up = (struct uart_cris_port *)port;
+       void __iomem *regi_ser = up->regi_ser;
+       reg_ser_rw_rec_ctrl rec_ctrl = REG_RD(ser, regi_ser, rw_rec_ctrl);
+
+       rec_ctrl.en = regk_ser_no;
+       REG_WR(ser, regi_ser, rw_rec_ctrl, rec_ctrl);
+}
+
+static void etraxfs_uart_enable_ms(struct uart_port *port)
+{
+}
+
+static void check_modem_status(struct uart_cris_port *up)
+{
+}
+
+static unsigned int etraxfs_uart_tx_empty(struct uart_port *port)
+{
+       struct uart_cris_port *up = (struct uart_cris_port *)port;
+       unsigned long flags;
+       unsigned int ret;
+       reg_ser_r_stat_din rstat = {0};
+
+       spin_lock_irqsave(&up->port.lock, flags);
+
+       rstat = REG_RD(ser, up->regi_ser, r_stat_din);
+       ret = rstat.tr_empty ? TIOCSER_TEMT : 0;
+
+       spin_unlock_irqrestore(&up->port.lock, flags);
+       return ret;
+}
+static unsigned int etraxfs_uart_get_mctrl(struct uart_port *port)
+{
+       struct uart_cris_port *up = (struct uart_cris_port *)port;
+       unsigned int ret;
+
+       ret = 0;
+       if (crisv32_serial_get_rts(up))
+               ret |= TIOCM_RTS;
+       /* DTR is active low */
+       if (up->dtr_pin && !gpiod_get_raw_value(up->dtr_pin))
+               ret |= TIOCM_DTR;
+       /* CD is active low */
+       if (up->cd_pin && !gpiod_get_raw_value(up->cd_pin))
+               ret |= TIOCM_CD;
+       /* RI is active low */
+       if (up->ri_pin && !gpiod_get_raw_value(up->ri_pin))
+               ret |= TIOCM_RI;
+       /* DSR is active low */
+       if (up->dsr_pin && !gpiod_get_raw_value(up->dsr_pin))
+               ret |= TIOCM_DSR;
+       if (crisv32_serial_get_cts(up))
+               ret |= TIOCM_CTS;
+       return ret;
+}
+
+static void etraxfs_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+       struct uart_cris_port *up = (struct uart_cris_port *)port;
+
+       crisv32_serial_set_rts(up, mctrl & TIOCM_RTS ? 1 : 0, 0);
+       /* DTR is active low */
+       if (up->dtr_pin)
+               gpiod_set_raw_value(up->dtr_pin, mctrl & TIOCM_DTR ? 0 : 1);
+       /* RI is active low */
+       if (up->ri_pin)
+               gpiod_set_raw_value(up->ri_pin, mctrl & TIOCM_RNG ? 0 : 1);
+       /* CD is active low */
+       if (up->cd_pin)
+               gpiod_set_raw_value(up->cd_pin, mctrl & TIOCM_CD ? 0 : 1);
+}
+
+static void etraxfs_uart_break_ctl(struct uart_port *port, int break_state)
+{
+       struct uart_cris_port *up = (struct uart_cris_port *)port;
+       unsigned long flags;
+       reg_ser_rw_tr_ctrl tr_ctrl;
+       reg_ser_rw_tr_dma_en tr_dma_en;
+       reg_ser_rw_intr_mask intr_mask;
+
+       spin_lock_irqsave(&up->port.lock, flags);
+       tr_ctrl = REG_RD(ser, up->regi_ser, rw_tr_ctrl);
+       tr_dma_en = REG_RD(ser, up->regi_ser, rw_tr_dma_en);
+       intr_mask = REG_RD(ser, up->regi_ser, rw_intr_mask);
+
+       if (break_state != 0) { /* Send break */
+               /*
+                * We need to disable DMA (if used) or tr_rdy interrupts if no
+                * DMA.  No need to make this conditional on use of DMA;
+                * disabling will be a no-op for the other mode.
+                */
+               intr_mask.tr_rdy = regk_ser_no;
+               tr_dma_en.en = 0;
+
+               /*
+                * Stop transmission and set the txd pin to 0 after the
+                * current character.  The txd setting will take effect after
+                * any current transmission has completed.
+                */
+               tr_ctrl.stop = 1;
+               tr_ctrl.txd = 0;
+       } else {
+               /* Re-enable the serial interrupt. */
+               intr_mask.tr_rdy = regk_ser_yes;
+
+               tr_ctrl.stop = 0;
+               tr_ctrl.txd = 1;
+       }
+       REG_WR(ser, up->regi_ser, rw_tr_ctrl, tr_ctrl);
+       REG_WR(ser, up->regi_ser, rw_tr_dma_en, tr_dma_en);
+       REG_WR(ser, up->regi_ser, rw_intr_mask, intr_mask);
+
+       spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+static void
+transmit_chars_no_dma(struct uart_cris_port *up)
+{
+       int max_count;
+       struct circ_buf *xmit = &up->port.state->xmit;
+
+       void __iomem *regi_ser = up->regi_ser;
+       reg_ser_r_stat_din rstat;
+       reg_ser_rw_ack_intr ack_intr = { .tr_rdy = regk_ser_yes };
+
+       if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) {
+               /* No more to send, so disable the interrupt. */
+               reg_ser_rw_intr_mask intr_mask;
+
+               intr_mask = REG_RD(ser, regi_ser, rw_intr_mask);
+               intr_mask.tr_rdy = 0;
+               intr_mask.tr_empty = 0;
+               REG_WR(ser, regi_ser, rw_intr_mask, intr_mask);
+               up->write_ongoing = 0;
+               return;
+       }
+
+       /* If the serport is fast, we send up to max_count bytes before
+          exiting the loop.  */
+       max_count = 64;
+       do {
+               reg_ser_rw_dout dout = { .data = xmit->buf[xmit->tail] };
+
+               REG_WR(ser, regi_ser, rw_dout, dout);
+               REG_WR(ser, regi_ser, rw_ack_intr, ack_intr);
+               xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE-1);
+               up->port.icount.tx++;
+               if (xmit->head == xmit->tail)
+                       break;
+               rstat = REG_RD(ser, regi_ser, r_stat_din);
+       } while ((--max_count > 0) && rstat.tr_rdy);
+
+       if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+               uart_write_wakeup(&up->port);
+}
+
+static void receive_chars_no_dma(struct uart_cris_port *up)
+{
+       reg_ser_rs_stat_din stat_din;
+       reg_ser_r_stat_din rstat;
+       struct tty_port *port;
+       struct uart_icount *icount;
+       int max_count = 16;
+       char flag;
+       reg_ser_rw_ack_intr ack_intr = { 0 };
+
+       rstat = REG_RD(ser, up->regi_ser, r_stat_din);
+       icount = &up->port.icount;
+       port = &up->port.state->port;
+
+       do {
+               stat_din = REG_RD(ser, up->regi_ser, rs_stat_din);
+
+               flag = TTY_NORMAL;
+               ack_intr.dav = 1;
+               REG_WR(ser, up->regi_ser, rw_ack_intr, ack_intr);
+               icount->rx++;
+
+               if (stat_din.framing_err | stat_din.par_err | stat_din.orun) {
+                       if (stat_din.data == 0x00 &&
+                           stat_din.framing_err) {
+                               /* Most likely a break. */
+                               flag = TTY_BREAK;
+                               icount->brk++;
+                       } else if (stat_din.par_err) {
+                               flag = TTY_PARITY;
+                               icount->parity++;
+                       } else if (stat_din.orun) {
+                               flag = TTY_OVERRUN;
+                               icount->overrun++;
+                       } else if (stat_din.framing_err) {
+                               flag = TTY_FRAME;
+                               icount->frame++;
+                       }
+               }
+
+               /*
+                * If this becomes important, we probably *could* handle this
+                * gracefully by keeping track of the unhandled character.
+                */
+               if (!tty_insert_flip_char(port, stat_din.data, flag))
+                       panic("%s: No tty buffer space", __func__);
+               rstat = REG_RD(ser, up->regi_ser, r_stat_din);
+       } while (rstat.dav && (max_count-- > 0));
+       spin_unlock(&up->port.lock);
+       tty_flip_buffer_push(port);
+       spin_lock(&up->port.lock);
+}
+
+static irqreturn_t
+ser_interrupt(int irq, void *dev_id)
+{
+       struct uart_cris_port *up = (struct uart_cris_port *)dev_id;
+       void __iomem *regi_ser;
+       int handled = 0;
+
+       spin_lock(&up->port.lock);
+
+       regi_ser = up->regi_ser;
+
+       if (regi_ser) {
+               reg_ser_r_masked_intr masked_intr;
+
+               masked_intr = REG_RD(ser, regi_ser, r_masked_intr);
+               /*
+                * Check what interrupts are active before taking
+                * actions. If DMA is used the interrupt shouldn't
+                * be enabled.
+                */
+               if (masked_intr.dav) {
+                       receive_chars_no_dma(up);
+                       handled = 1;
+               }
+               check_modem_status(up);
+
+               if (masked_intr.tr_rdy) {
+                       transmit_chars_no_dma(up);
+                       handled = 1;
+               }
+       }
+       spin_unlock(&up->port.lock);
+       return IRQ_RETVAL(handled);
+}
+
+#ifdef CONFIG_CONSOLE_POLL
+static int etraxfs_uart_get_poll_char(struct uart_port *port)
+{
+       reg_ser_rs_stat_din stat;
+       reg_ser_rw_ack_intr ack_intr = { 0 };
+       struct uart_cris_port *up = (struct uart_cris_port *)port;
+
+       do {
+               stat = REG_RD(ser, up->regi_ser, rs_stat_din);
+       } while (!stat.dav);
+
+       /* Ack the data_avail interrupt. */
+       ack_intr.dav = 1;
+       REG_WR(ser, up->regi_ser, rw_ack_intr, ack_intr);
+
+       return stat.data;
+}
+
+static void etraxfs_uart_put_poll_char(struct uart_port *port,
+                                       unsigned char c)
+{
+       reg_ser_r_stat_din stat;
+       struct uart_cris_port *up = (struct uart_cris_port *)port;
+
+       do {
+               stat = REG_RD(ser, up->regi_ser, r_stat_din);
+       } while (!stat.tr_rdy);
+       REG_WR_INT(ser, up->regi_ser, rw_dout, c);
+}
+#endif /* CONFIG_CONSOLE_POLL */
+
+static int etraxfs_uart_startup(struct uart_port *port)
+{
+       struct uart_cris_port *up = (struct uart_cris_port *)port;
+       unsigned long flags;
+       reg_ser_rw_intr_mask ser_intr_mask = {0};
+
+       ser_intr_mask.dav = regk_ser_yes;
+
+       if (request_irq(etraxfs_uart_ports[port->line]->irq, ser_interrupt,
+                       0, DRV_NAME, etraxfs_uart_ports[port->line]))
+               panic("irq ser%d", port->line);
+
+       spin_lock_irqsave(&up->port.lock, flags);
+
+       REG_WR(ser, up->regi_ser, rw_intr_mask, ser_intr_mask);
+
+       etraxfs_uart_set_mctrl(&up->port, up->port.mctrl);
+
+       spin_unlock_irqrestore(&up->port.lock, flags);
+
+       return 0;
+}
+
+static void etraxfs_uart_shutdown(struct uart_port *port)
+{
+       struct uart_cris_port *up = (struct uart_cris_port *)port;
+       unsigned long flags;
+
+       spin_lock_irqsave(&up->port.lock, flags);
+
+       etraxfs_uart_stop_tx(port);
+       etraxfs_uart_stop_rx(port);
+
+       free_irq(etraxfs_uart_ports[port->line]->irq,
+                etraxfs_uart_ports[port->line]);
+
+       etraxfs_uart_set_mctrl(&up->port, up->port.mctrl);
+
+       spin_unlock_irqrestore(&up->port.lock, flags);
+
+}
+
+static void
+etraxfs_uart_set_termios(struct uart_port *port, struct ktermios *termios,
+                        struct ktermios *old)
+{
+       struct uart_cris_port *up = (struct uart_cris_port *)port;
+       unsigned long flags;
+       reg_ser_rw_xoff xoff;
+       reg_ser_rw_xoff_clr xoff_clr = {0};
+       reg_ser_rw_tr_ctrl tx_ctrl = {0};
+       reg_ser_rw_tr_dma_en tx_dma_en = {0};
+       reg_ser_rw_rec_ctrl rx_ctrl = {0};
+       reg_ser_rw_tr_baud_div tx_baud_div = {0};
+       reg_ser_rw_rec_baud_div rx_baud_div = {0};
+       int baud;
+
+       if (old &&
+           termios->c_cflag == old->c_cflag &&
+           termios->c_iflag == old->c_iflag)
+               return;
+
+       /* Tx: 8 bit, no/even parity, 1 stop bit, no cts. */
+       tx_ctrl.base_freq = regk_ser_f29_493;
+       tx_ctrl.en = 0;
+       tx_ctrl.stop = 0;
+       tx_ctrl.auto_rts = regk_ser_no;
+       tx_ctrl.txd = 1;
+       tx_ctrl.auto_cts = 0;
+       /* Rx: 8 bit, no/even parity. */
+       rx_ctrl.dma_err = regk_ser_stop;
+       rx_ctrl.sampling = regk_ser_majority;
+       rx_ctrl.timeout = 1;
+
+       rx_ctrl.rts_n = regk_ser_inactive;
+
+       /* Common for tx and rx: 8N1. */
+       tx_ctrl.data_bits = regk_ser_bits8;
+       rx_ctrl.data_bits = regk_ser_bits8;
+       tx_ctrl.par = regk_ser_even;
+       rx_ctrl.par = regk_ser_even;
+       tx_ctrl.par_en = regk_ser_no;
+       rx_ctrl.par_en = regk_ser_no;
+
+       tx_ctrl.stop_bits = regk_ser_bits1;
+
+       /*
+        * Change baud-rate and write it to the hardware.
+        *
+        * baud_clock = base_freq / (divisor*8)
+        * divisor = base_freq / (baud_clock * 8)
+        * base_freq is either:
+        * off, ext, 29.493MHz, 32.000 MHz, 32.768 MHz or 100 MHz
+        * 20.493MHz is used for standard baudrates
+        */
+
+       /*
+        * For the console port we keep the original baudrate here.  Not very
+        * beautiful.
+        */
+       if ((port != console_port) || old)
+               baud = uart_get_baud_rate(port, termios, old, 0,
+                                         port->uartclk / 8);
+       else
+               baud = console_baud;
+
+       tx_baud_div.div = 29493000 / (8 * baud);
+       /* Rx uses same as tx. */
+       rx_baud_div.div = tx_baud_div.div;
+       rx_ctrl.base_freq = tx_ctrl.base_freq;
+
+       if ((termios->c_cflag & CSIZE) == CS7) {
+               /* Set 7 bit mode. */
+               tx_ctrl.data_bits = regk_ser_bits7;
+               rx_ctrl.data_bits = regk_ser_bits7;
+       }
+
+       if (termios->c_cflag & CSTOPB) {
+               /* Set 2 stop bit mode. */
+               tx_ctrl.stop_bits = regk_ser_bits2;
+       }
+
+       if (termios->c_cflag & PARENB) {
+               /* Enable parity. */
+               tx_ctrl.par_en = regk_ser_yes;
+               rx_ctrl.par_en = regk_ser_yes;
+       }
+
+       if (termios->c_cflag & CMSPAR) {
+               if (termios->c_cflag & PARODD) {
+                       /* Set mark parity if PARODD and CMSPAR. */
+                       tx_ctrl.par = regk_ser_mark;
+                       rx_ctrl.par = regk_ser_mark;
+               } else {
+                       tx_ctrl.par = regk_ser_space;
+                       rx_ctrl.par = regk_ser_space;
+               }
+       } else {
+               if (termios->c_cflag & PARODD) {
+                       /* Set odd parity. */
+                      tx_ctrl.par = regk_ser_odd;
+                      rx_ctrl.par = regk_ser_odd;
+               }
+       }
+
+       if (termios->c_cflag & CRTSCTS) {
+               /* Enable automatic CTS handling. */
+               tx_ctrl.auto_cts = regk_ser_yes;
+       }
+
+       /* Make sure the tx and rx are enabled. */
+       tx_ctrl.en = regk_ser_yes;
+       rx_ctrl.en = regk_ser_yes;
+
+       spin_lock_irqsave(&port->lock, flags);
+
+       tx_dma_en.en = 0;
+       REG_WR(ser, up->regi_ser, rw_tr_dma_en, tx_dma_en);
+
+       /* Actually write the control regs (if modified) to the hardware. */
+       uart_update_timeout(port, termios->c_cflag, port->uartclk/8);
+       MODIFY_REG(up->regi_ser, rw_rec_baud_div, rx_baud_div);
+       MODIFY_REG(up->regi_ser, rw_rec_ctrl, rx_ctrl);
+
+       MODIFY_REG(up->regi_ser, rw_tr_baud_div, tx_baud_div);
+       MODIFY_REG(up->regi_ser, rw_tr_ctrl, tx_ctrl);
+
+       tx_dma_en.en = 0;
+       REG_WR(ser, up->regi_ser, rw_tr_dma_en, tx_dma_en);
+
+       xoff = REG_RD(ser, up->regi_ser, rw_xoff);
+
+       if (up->port.state && up->port.state->port.tty &&
+           (up->port.state->port.tty->termios.c_iflag & IXON)) {
+               xoff.chr = STOP_CHAR(up->port.state->port.tty);
+               xoff.automatic = regk_ser_yes;
+       } else
+               xoff.automatic = regk_ser_no;
+
+       MODIFY_REG(up->regi_ser, rw_xoff, xoff);
+
+       /*
+        * Make sure we don't start in an automatically shut-off state due to
+        * a previous early exit.
+        */
+       xoff_clr.clr = 1;
+       REG_WR(ser, up->regi_ser, rw_xoff_clr, xoff_clr);
+
+       etraxfs_uart_set_mctrl(&up->port, up->port.mctrl);
+       spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+static const char *
+etraxfs_uart_type(struct uart_port *port)
+{
+       return "CRISv32";
+}
+
+static void etraxfs_uart_release_port(struct uart_port *port)
+{
+}
+
+static int etraxfs_uart_request_port(struct uart_port *port)
+{
+       return 0;
+}
+
+static void etraxfs_uart_config_port(struct uart_port *port, int flags)
+{
+       struct uart_cris_port *up = (struct uart_cris_port *)port;
+
+       up->port.type = PORT_CRIS;
+}
+
+static const struct uart_ops etraxfs_uart_pops = {
+       .tx_empty = etraxfs_uart_tx_empty,
+       .set_mctrl = etraxfs_uart_set_mctrl,
+       .get_mctrl = etraxfs_uart_get_mctrl,
+       .stop_tx = etraxfs_uart_stop_tx,
+       .start_tx = etraxfs_uart_start_tx,
+       .send_xchar = etraxfs_uart_send_xchar,
+       .stop_rx = etraxfs_uart_stop_rx,
+       .enable_ms = etraxfs_uart_enable_ms,
+       .break_ctl = etraxfs_uart_break_ctl,
+       .startup = etraxfs_uart_startup,
+       .shutdown = etraxfs_uart_shutdown,
+       .set_termios = etraxfs_uart_set_termios,
+       .type = etraxfs_uart_type,
+       .release_port = etraxfs_uart_release_port,
+       .request_port = etraxfs_uart_request_port,
+       .config_port = etraxfs_uart_config_port,
+#ifdef CONFIG_CONSOLE_POLL
+       .poll_get_char = etraxfs_uart_get_poll_char,
+       .poll_put_char = etraxfs_uart_put_poll_char,
+#endif
+};
+
+static void cris_serial_port_init(struct uart_port *port, int line)
+{
+       struct uart_cris_port *up = (struct uart_cris_port *)port;
+
+       if (up->initialized)
+               return;
+       up->initialized = 1;
+       port->line = line;
+       spin_lock_init(&port->lock);
+       port->ops = &etraxfs_uart_pops;
+       port->irq = up->irq;
+       port->iobase = (unsigned long) up->regi_ser;
+       port->uartclk = 29493000;
+
+       /*
+        * We can't fit any more than 255 here (unsigned char), though
+        * actually UART_XMIT_SIZE characters could be pending output.
+        * At time of this writing, the definition of "fifosize" is here the
+        * amount of characters that can be pending output after a start_tx call
+        * until tx_empty returns 1: see serial_core.c:uart_wait_until_sent.
+        * This matters for timeout calculations unfortunately, but keeping
+        * larger amounts at the DMA wouldn't win much so let's just play nice.
+        */
+       port->fifosize = 255;
+       port->flags = UPF_BOOT_AUTOCONF;
+}
+
+static int etraxfs_uart_probe(struct platform_device *pdev)
+{
+       struct device_node *np = pdev->dev.of_node;
+       struct uart_cris_port *up;
+       int dev_id;
+
+       if (!np)
+               return -ENODEV;
+
+       dev_id = of_alias_get_id(np, "serial");
+       if (dev_id < 0)
+               dev_id = 0;
+
+       if (dev_id >= UART_NR)
+               return -EINVAL;
+
+       if (etraxfs_uart_ports[dev_id])
+               return -EBUSY;
+
+       up = devm_kzalloc(&pdev->dev, sizeof(struct uart_cris_port),
+                         GFP_KERNEL);
+       if (!up)
+               return -ENOMEM;
+
+       up->irq = irq_of_parse_and_map(np, 0);
+       up->regi_ser = of_iomap(np, 0);
+       up->dtr_pin = devm_gpiod_get_optional(&pdev->dev, "dtr");
+       up->dsr_pin = devm_gpiod_get_optional(&pdev->dev, "dsr");
+       up->ri_pin = devm_gpiod_get_optional(&pdev->dev, "ri");
+       up->cd_pin = devm_gpiod_get_optional(&pdev->dev, "cd");
+       up->port.dev = &pdev->dev;
+       cris_serial_port_init(&up->port, dev_id);
+
+       etraxfs_uart_ports[dev_id] = up;
+       platform_set_drvdata(pdev, &up->port);
+       uart_add_one_port(&etraxfs_uart_driver, &up->port);
+
+       return 0;
+}
+
+static int etraxfs_uart_remove(struct platform_device *pdev)
+{
+       struct uart_port *port;
+
+       port = platform_get_drvdata(pdev);
+       uart_remove_one_port(&etraxfs_uart_driver, port);
+       etraxfs_uart_ports[pdev->id] = NULL;
+
+       return 0;
+}
+
+static const struct of_device_id etraxfs_uart_dt_ids[] = {
+       { .compatible = "axis,etraxfs-uart" },
+       { /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, etraxfs_uart_dt_ids);
+
+static struct platform_driver etraxfs_uart_platform_driver = {
+       .driver = {
+               .name   = DRV_NAME,
+               .of_match_table = of_match_ptr(etraxfs_uart_dt_ids),
+       },
+       .probe          = etraxfs_uart_probe,
+       .remove         = etraxfs_uart_remove,
+};
+
+static int __init etraxfs_uart_init(void)
+{
+       int ret;
+
+       ret = uart_register_driver(&etraxfs_uart_driver);
+       if (ret)
+               return ret;
+
+       ret = platform_driver_register(&etraxfs_uart_platform_driver);
+       if (ret)
+               uart_unregister_driver(&etraxfs_uart_driver);
+
+       return ret;
+}
+
+static void __exit etraxfs_uart_exit(void)
+{
+       platform_driver_unregister(&etraxfs_uart_platform_driver);
+       uart_unregister_driver(&etraxfs_uart_driver);
+}
+
+module_init(etraxfs_uart_init);
+module_exit(etraxfs_uart_exit);
index e7cde3a9566d988963532bc2daecd1cbd06940dd..b1893f3f88f1c6b6fdb2e8e42ad565ba23e18fd1 100644 (file)
@@ -237,7 +237,8 @@ struct lpuart_port {
        unsigned int            rxfifo_size;
        bool                    lpuart32;
 
-       bool                    lpuart_dma_use;
+       bool                    lpuart_dma_tx_use;
+       bool                    lpuart_dma_rx_use;
        struct dma_chan         *dma_tx_chan;
        struct dma_chan         *dma_rx_chan;
        struct dma_async_tx_descriptor  *dma_tx_desc;
@@ -454,6 +455,15 @@ static int lpuart_dma_rx(struct lpuart_port *sport)
        return 0;
 }
 
+static void lpuart_flush_buffer(struct uart_port *port)
+{
+       struct lpuart_port *sport = container_of(port, struct lpuart_port, port);
+       if (sport->lpuart_dma_tx_use) {
+               dmaengine_terminate_all(sport->dma_tx_chan);
+               sport->dma_tx_in_progress = 0;
+       }
+}
+
 static void lpuart_dma_rx_complete(void *arg)
 {
        struct lpuart_port *sport = arg;
@@ -461,6 +471,7 @@ static void lpuart_dma_rx_complete(void *arg)
        unsigned long flags;
 
        async_tx_ack(sport->dma_rx_desc);
+       mod_timer(&sport->lpuart_timer, jiffies + sport->dma_rx_timeout);
 
        spin_lock_irqsave(&sport->port.lock, flags);
 
@@ -506,9 +517,6 @@ static inline void lpuart_prepare_rx(struct lpuart_port *sport)
 
        spin_lock_irqsave(&sport->port.lock, flags);
 
-       init_timer(&sport->lpuart_timer);
-       sport->lpuart_timer.function = lpuart_timer_func;
-       sport->lpuart_timer.data = (unsigned long)sport;
        sport->lpuart_timer.expires = jiffies + sport->dma_rx_timeout;
        add_timer(&sport->lpuart_timer);
 
@@ -571,7 +579,7 @@ static void lpuart_start_tx(struct uart_port *port)
        temp = readb(port->membase + UARTCR2);
        writeb(temp | UARTCR2_TIE, port->membase + UARTCR2);
 
-       if (sport->lpuart_dma_use) {
+       if (sport->lpuart_dma_tx_use) {
                if (!uart_circ_empty(xmit) && !sport->dma_tx_in_progress)
                        lpuart_prepare_tx(sport);
        } else {
@@ -758,19 +766,19 @@ out:
 static irqreturn_t lpuart_int(int irq, void *dev_id)
 {
        struct lpuart_port *sport = dev_id;
-       unsigned char sts;
+       unsigned char sts, crdma;
 
        sts = readb(sport->port.membase + UARTSR1);
+       crdma = readb(sport->port.membase + UARTCR5);
 
-       if (sts & UARTSR1_RDRF) {
-               if (sport->lpuart_dma_use)
+       if (sts & UARTSR1_RDRF && !(crdma & UARTCR5_RDMAS)) {
+               if (sport->lpuart_dma_rx_use)
                        lpuart_prepare_rx(sport);
                else
                        lpuart_rxint(irq, dev_id);
        }
-       if (sts & UARTSR1_TDRE &&
-               !(readb(sport->port.membase + UARTCR5) & UARTCR5_TDMAS)) {
-               if (sport->lpuart_dma_use)
+       if (sts & UARTSR1_TDRE && !(crdma & UARTCR5_TDMAS)) {
+               if (sport->lpuart_dma_tx_use)
                        lpuart_pio_tx(sport);
                else
                        lpuart_txint(irq, dev_id);
@@ -953,26 +961,17 @@ static int lpuart_dma_tx_request(struct uart_port *port)
 {
        struct lpuart_port *sport = container_of(port,
                                        struct lpuart_port, port);
-       struct dma_chan *tx_chan;
        struct dma_slave_config dma_tx_sconfig;
        dma_addr_t dma_bus;
        unsigned char *dma_buf;
        int ret;
 
-       tx_chan  = dma_request_slave_channel(sport->port.dev, "tx");
-
-       if (!tx_chan) {
-               dev_err(sport->port.dev, "Dma tx channel request failed!\n");
-               return -ENODEV;
-       }
-
-       dma_bus = dma_map_single(tx_chan->device->dev,
+       dma_bus = dma_map_single(sport->dma_tx_chan->device->dev,
                                sport->port.state->xmit.buf,
                                UART_XMIT_SIZE, DMA_TO_DEVICE);
 
-       if (dma_mapping_error(tx_chan->device->dev, dma_bus)) {
+       if (dma_mapping_error(sport->dma_tx_chan->device->dev, dma_bus)) {
                dev_err(sport->port.dev, "dma_map_single tx failed\n");
-               dma_release_channel(tx_chan);
                return -ENOMEM;
        }
 
@@ -981,16 +980,14 @@ static int lpuart_dma_tx_request(struct uart_port *port)
        dma_tx_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
        dma_tx_sconfig.dst_maxburst = sport->txfifo_size;
        dma_tx_sconfig.direction = DMA_MEM_TO_DEV;
-       ret = dmaengine_slave_config(tx_chan, &dma_tx_sconfig);
+       ret = dmaengine_slave_config(sport->dma_tx_chan, &dma_tx_sconfig);
 
        if (ret < 0) {
                dev_err(sport->port.dev,
                                "Dma slave config failed, err = %d\n", ret);
-               dma_release_channel(tx_chan);
                return ret;
        }
 
-       sport->dma_tx_chan = tx_chan;
        sport->dma_tx_buf_virt = dma_buf;
        sport->dma_tx_buf_bus = dma_bus;
        sport->dma_tx_in_progress = 0;
@@ -1002,34 +999,24 @@ static int lpuart_dma_rx_request(struct uart_port *port)
 {
        struct lpuart_port *sport = container_of(port,
                                        struct lpuart_port, port);
-       struct dma_chan *rx_chan;
        struct dma_slave_config dma_rx_sconfig;
        dma_addr_t dma_bus;
        unsigned char *dma_buf;
        int ret;
 
-       rx_chan  = dma_request_slave_channel(sport->port.dev, "rx");
-
-       if (!rx_chan) {
-               dev_err(sport->port.dev, "Dma rx channel request failed!\n");
-               return -ENODEV;
-       }
-
        dma_buf = devm_kzalloc(sport->port.dev,
                                FSL_UART_RX_DMA_BUFFER_SIZE, GFP_KERNEL);
 
        if (!dma_buf) {
                dev_err(sport->port.dev, "Dma rx alloc failed\n");
-               dma_release_channel(rx_chan);
                return -ENOMEM;
        }
 
-       dma_bus = dma_map_single(rx_chan->device->dev, dma_buf,
+       dma_bus = dma_map_single(sport->dma_rx_chan->device->dev, dma_buf,
                                FSL_UART_RX_DMA_BUFFER_SIZE, DMA_FROM_DEVICE);
 
-       if (dma_mapping_error(rx_chan->device->dev, dma_bus)) {
+       if (dma_mapping_error(sport->dma_rx_chan->device->dev, dma_bus)) {
                dev_err(sport->port.dev, "dma_map_single rx failed\n");
-               dma_release_channel(rx_chan);
                return -ENOMEM;
        }
 
@@ -1037,16 +1024,14 @@ static int lpuart_dma_rx_request(struct uart_port *port)
        dma_rx_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
        dma_rx_sconfig.src_maxburst = 1;
        dma_rx_sconfig.direction = DMA_DEV_TO_MEM;
-       ret = dmaengine_slave_config(rx_chan, &dma_rx_sconfig);
+       ret = dmaengine_slave_config(sport->dma_rx_chan, &dma_rx_sconfig);
 
        if (ret < 0) {
                dev_err(sport->port.dev,
                                "Dma slave config failed, err = %d\n", ret);
-               dma_release_channel(rx_chan);
                return ret;
        }
 
-       sport->dma_rx_chan = rx_chan;
        sport->dma_rx_buf_virt = dma_buf;
        sport->dma_rx_buf_bus = dma_bus;
        sport->dma_rx_in_progress = 0;
@@ -1058,31 +1043,24 @@ static void lpuart_dma_tx_free(struct uart_port *port)
 {
        struct lpuart_port *sport = container_of(port,
                                        struct lpuart_port, port);
-       struct dma_chan *dma_chan;
 
        dma_unmap_single(sport->port.dev, sport->dma_tx_buf_bus,
                        UART_XMIT_SIZE, DMA_TO_DEVICE);
-       dma_chan = sport->dma_tx_chan;
-       sport->dma_tx_chan = NULL;
+
        sport->dma_tx_buf_bus = 0;
        sport->dma_tx_buf_virt = NULL;
-       dma_release_channel(dma_chan);
 }
 
 static void lpuart_dma_rx_free(struct uart_port *port)
 {
        struct lpuart_port *sport = container_of(port,
                                        struct lpuart_port, port);
-       struct dma_chan *dma_chan;
 
        dma_unmap_single(sport->port.dev, sport->dma_rx_buf_bus,
                        FSL_UART_RX_DMA_BUFFER_SIZE, DMA_FROM_DEVICE);
 
-       dma_chan = sport->dma_rx_chan;
-       sport->dma_rx_chan = NULL;
        sport->dma_rx_buf_bus = 0;
        sport->dma_rx_buf_virt = NULL;
-       dma_release_channel(dma_chan);
 }
 
 static int lpuart_startup(struct uart_port *port)
@@ -1101,14 +1079,21 @@ static int lpuart_startup(struct uart_port *port)
        sport->rxfifo_size = 0x1 << (((temp >> UARTPFIFO_RXSIZE_OFF) &
                UARTPFIFO_FIFOSIZE_MASK) + 1);
 
-       /* Whether use dma support by dma request results */
-       if (lpuart_dma_tx_request(port) || lpuart_dma_rx_request(port)) {
-               sport->lpuart_dma_use = false;
-       } else {
-               sport->lpuart_dma_use = true;
+       if (sport->dma_rx_chan && !lpuart_dma_rx_request(port)) {
+               sport->lpuart_dma_rx_use = true;
+               setup_timer(&sport->lpuart_timer, lpuart_timer_func,
+                           (unsigned long)sport);
+       } else
+               sport->lpuart_dma_rx_use = false;
+
+
+       if (sport->dma_tx_chan && !lpuart_dma_tx_request(port)) {
+               sport->lpuart_dma_tx_use = true;
                temp = readb(port->membase + UARTCR5);
+               temp &= ~UARTCR5_RDMAS;
                writeb(temp | UARTCR5_TDMAS, port->membase + UARTCR5);
-       }
+       } else
+               sport->lpuart_dma_tx_use = false;
 
        ret = devm_request_irq(port->dev, port->irq, lpuart_int, 0,
                                DRIVER_NAME, sport);
@@ -1179,10 +1164,13 @@ static void lpuart_shutdown(struct uart_port *port)
 
        devm_free_irq(port->dev, port->irq, sport);
 
-       if (sport->lpuart_dma_use) {
-               lpuart_dma_tx_free(port);
-               lpuart_dma_rx_free(port);
+       if (sport->lpuart_dma_rx_use) {
+               lpuart_dma_rx_free(&sport->port);
+               del_timer_sync(&sport->lpuart_timer);
        }
+
+       if (sport->lpuart_dma_tx_use)
+               lpuart_dma_tx_free(&sport->port);
 }
 
 static void lpuart32_shutdown(struct uart_port *port)
@@ -1304,7 +1292,7 @@ lpuart_set_termios(struct uart_port *port, struct ktermios *termios,
        /* update the per-port timeout */
        uart_update_timeout(port, termios->c_cflag, baud);
 
-       if (sport->lpuart_dma_use) {
+       if (sport->lpuart_dma_rx_use) {
                /* Calculate delay for 1.5 DMA buffers */
                sport->dma_rx_timeout = (sport->port.timeout - HZ / 50) *
                                        FSL_UART_RX_DMA_BUFFER_SIZE * 3 /
@@ -1517,6 +1505,7 @@ static struct uart_ops lpuart_pops = {
        .release_port   = lpuart_release_port,
        .config_port    = lpuart_config_port,
        .verify_port    = lpuart_verify_port,
+       .flush_buffer   = lpuart_flush_buffer,
 };
 
 static struct uart_ops lpuart32_pops = {
@@ -1535,6 +1524,7 @@ static struct uart_ops lpuart32_pops = {
        .release_port   = lpuart_release_port,
        .config_port    = lpuart_config_port,
        .verify_port    = lpuart_verify_port,
+       .flush_buffer   = lpuart_flush_buffer,
 };
 
 static struct lpuart_port *lpuart_ports[UART_NR];
@@ -1833,6 +1823,16 @@ static int lpuart_probe(struct platform_device *pdev)
                return ret;
        }
 
+       sport->dma_tx_chan = dma_request_slave_channel(sport->port.dev, "tx");
+       if (!sport->dma_tx_chan)
+               dev_info(sport->port.dev, "DMA tx channel request failed, "
+                               "operating without tx DMA\n");
+
+       sport->dma_rx_chan = dma_request_slave_channel(sport->port.dev, "rx");
+       if (!sport->dma_rx_chan)
+               dev_info(sport->port.dev, "DMA rx channel request failed, "
+                               "operating without rx DMA\n");
+
        return 0;
 }
 
@@ -1844,6 +1844,12 @@ static int lpuart_remove(struct platform_device *pdev)
 
        clk_disable_unprepare(sport->clk);
 
+       if (sport->dma_tx_chan)
+               dma_release_channel(sport->dma_tx_chan);
+
+       if (sport->dma_rx_chan)
+               dma_release_channel(sport->dma_rx_chan);
+
        return 0;
 }
 
@@ -1851,6 +1857,19 @@ static int lpuart_remove(struct platform_device *pdev)
 static int lpuart_suspend(struct device *dev)
 {
        struct lpuart_port *sport = dev_get_drvdata(dev);
+       unsigned long temp;
+
+       if (sport->lpuart32) {
+               /* disable Rx/Tx and interrupts */
+               temp = lpuart32_read(sport->port.membase + UARTCTRL);
+               temp &= ~(UARTCTRL_TE | UARTCTRL_TIE | UARTCTRL_TCIE);
+               lpuart32_write(temp, sport->port.membase + UARTCTRL);
+       } else {
+               /* disable Rx/Tx and interrupts */
+               temp = readb(sport->port.membase + UARTCR2);
+               temp &= ~(UARTCR2_TE | UARTCR2_TIE | UARTCR2_TCIE);
+               writeb(temp, sport->port.membase + UARTCR2);
+       }
 
        uart_suspend_port(&lpuart_reg, &sport->port);
 
index 4c5e9092e2d76751265143044b9f5d0b432baf34..0eb29b1c47ac8ea313456df38a45f6720e62246b 100644 (file)
@@ -74,6 +74,7 @@
 #define IMX21_UTS 0xb4 /* UART Test Register on all other i.mx*/
 
 /* UART Control Register Bit Fields.*/
+#define URXD_DUMMY_READ (1<<16)
 #define URXD_CHARRDY   (1<<15)
 #define URXD_ERR       (1<<14)
 #define URXD_OVRRUN    (1<<13)
@@ -463,13 +464,17 @@ static void imx_enable_ms(struct uart_port *port)
        mod_timer(&sport->timer, jiffies);
 }
 
+static void imx_dma_tx(struct imx_port *sport);
 static inline void imx_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);
+               sport->port.icount.tx++;
+               sport->port.x_char = 0;
                return;
        }
 
@@ -478,6 +483,22 @@ static inline void imx_transmit_buffer(struct imx_port *sport)
                return;
        }
 
+       if (sport->dma_is_enabled) {
+               /*
+                * 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;
+               if (sport->dma_is_txing) {
+                       temp |= UCR1_TDMAEN;
+                       writel(temp, sport->port.membase + UCR1);
+               } else {
+                       writel(temp, sport->port.membase + UCR1);
+                       imx_dma_tx(sport);
+               }
+       }
+
        while (!uart_circ_empty(xmit) &&
               !(readl(sport->port.membase + uts_reg(sport)) & UTS_TXFULL)) {
                /* send xmit->buf[xmit->tail]
@@ -500,26 +521,39 @@ static void dma_tx_callback(void *data)
        struct scatterlist *sgl = &sport->tx_sgl[0];
        struct circ_buf *xmit = &sport->port.state->xmit;
        unsigned long flags;
+       unsigned long temp;
+
+       spin_lock_irqsave(&sport->port.lock, flags);
 
        dma_unmap_sg(sport->port.dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE);
 
-       sport->dma_is_txing = 0;
+       temp = readl(sport->port.membase + UCR1);
+       temp &= ~UCR1_TDMAEN;
+       writel(temp, sport->port.membase + UCR1);
 
        /* update the stat */
-       spin_lock_irqsave(&sport->port.lock, flags);
        xmit->tail = (xmit->tail + sport->tx_bytes) & (UART_XMIT_SIZE - 1);
        sport->port.icount.tx += sport->tx_bytes;
-       spin_unlock_irqrestore(&sport->port.lock, flags);
 
        dev_dbg(sport->port.dev, "we finish the TX DMA.\n");
 
-       uart_write_wakeup(&sport->port);
+       sport->dma_is_txing = 0;
+
+       spin_unlock_irqrestore(&sport->port.lock, flags);
+
+       if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+               uart_write_wakeup(&sport->port);
 
        if (waitqueue_active(&sport->dma_wait)) {
                wake_up(&sport->dma_wait);
                dev_dbg(sport->port.dev, "exit in %s.\n", __func__);
                return;
        }
+
+       spin_lock_irqsave(&sport->port.lock, flags);
+       if (!uart_circ_empty(xmit) && !uart_tx_stopped(&sport->port))
+               imx_dma_tx(sport);
+       spin_unlock_irqrestore(&sport->port.lock, flags);
 }
 
 static void imx_dma_tx(struct imx_port *sport)
@@ -529,24 +563,23 @@ static void imx_dma_tx(struct imx_port *sport)
        struct dma_async_tx_descriptor *desc;
        struct dma_chan *chan = sport->dma_chan_tx;
        struct device *dev = sport->port.dev;
-       enum dma_status status;
+       unsigned long temp;
        int ret;
 
-       status = dmaengine_tx_status(chan, (dma_cookie_t)0, NULL);
-       if (DMA_IN_PROGRESS == status)
+       if (sport->dma_is_txing)
                return;
 
        sport->tx_bytes = uart_circ_chars_pending(xmit);
 
-       if (xmit->tail > xmit->head && xmit->head > 0) {
+       if (xmit->tail < xmit->head) {
+               sport->dma_tx_nents = 1;
+               sg_init_one(sgl, xmit->buf + xmit->tail, sport->tx_bytes);
+       } else {
                sport->dma_tx_nents = 2;
                sg_init_table(sgl, 2);
                sg_set_buf(sgl, xmit->buf + xmit->tail,
                                UART_XMIT_SIZE - xmit->tail);
                sg_set_buf(sgl + 1, xmit->buf, xmit->head);
-       } else {
-               sport->dma_tx_nents = 1;
-               sg_init_one(sgl, xmit->buf + xmit->tail, sport->tx_bytes);
        }
 
        ret = dma_map_sg(dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE);
@@ -557,6 +590,8 @@ static void imx_dma_tx(struct imx_port *sport)
        desc = dmaengine_prep_slave_sg(chan, sgl, sport->dma_tx_nents,
                                        DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT);
        if (!desc) {
+               dma_unmap_sg(dev, sgl, sport->dma_tx_nents,
+                            DMA_TO_DEVICE);
                dev_err(dev, "We cannot prepare for the TX slave dma!\n");
                return;
        }
@@ -565,6 +600,11 @@ static void imx_dma_tx(struct imx_port *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);
+
        /* fire it */
        sport->dma_is_txing = 1;
        dmaengine_submit(desc);
@@ -590,13 +630,6 @@ static void imx_start_tx(struct uart_port *port)
                temp &= ~(UCR1_RRDYEN);
                writel(temp, sport->port.membase + UCR1);
        }
-       /* Clear any pending ORE flag before enabling interrupt */
-       temp = readl(sport->port.membase + USR2);
-       writel(temp | USR2_ORE, sport->port.membase + USR2);
-
-       temp = readl(sport->port.membase + UCR4);
-       temp |= UCR4_OREN;
-       writel(temp, sport->port.membase + UCR4);
 
        if (!sport->dma_is_enabled) {
                temp = readl(sport->port.membase + UCR1);
@@ -614,15 +647,21 @@ static void imx_start_tx(struct uart_port *port)
        }
 
        if (sport->dma_is_enabled) {
-               /* FIXME: port->x_char must be transmitted if != 0 */
+               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);
+                       return;
+               }
+
                if (!uart_circ_empty(&port->state->xmit) &&
                    !uart_tx_stopped(port))
                        imx_dma_tx(sport);
                return;
        }
-
-       if (readl(sport->port.membase + uts_reg(sport)) & UTS_TXEMPTY)
-               imx_transmit_buffer(sport);
 }
 
 static irqreturn_t imx_rtsint(int irq, void *dev_id)
@@ -694,7 +733,7 @@ static irqreturn_t imx_rxint(int irq, void *dev_id)
                                continue;
                        }
 
-                       rx &= sport->port.read_status_mask;
+                       rx &= (sport->port.read_status_mask | 0xFF);
 
                        if (rx & URXD_BRK)
                                flg = TTY_BREAK;
@@ -710,6 +749,9 @@ static irqreturn_t imx_rxint(int irq, void *dev_id)
 #endif
                }
 
+               if (sport->port.ignore_status_mask & URXD_DUMMY_READ)
+                       goto out;
+
                tty_insert_flip_char(port, rx, flg);
        }
 
@@ -727,6 +769,9 @@ static int start_rx_dma(struct imx_port *sport);
 static void imx_dma_rxint(struct imx_port *sport)
 {
        unsigned long temp;
+       unsigned long flags;
+
+       spin_lock_irqsave(&sport->port.lock, flags);
 
        temp = readl(sport->port.membase + USR2);
        if ((temp & USR2_RDR) && !sport->dma_is_rxing) {
@@ -740,6 +785,8 @@ static void imx_dma_rxint(struct imx_port *sport)
                /* tell the DMA to receive the data. */
                start_rx_dma(sport);
        }
+
+       spin_unlock_irqrestore(&sport->port.lock, flags);
 }
 
 static irqreturn_t imx_int(int irq, void *dev_id)
@@ -869,6 +916,9 @@ static int imx_setup_ufcr(struct imx_port *sport, unsigned int mode)
 static void imx_rx_dma_done(struct imx_port *sport)
 {
        unsigned long temp;
+       unsigned long flags;
+
+       spin_lock_irqsave(&sport->port.lock, flags);
 
        /* Enable this interrupt when the RXFIFO is empty. */
        temp = readl(sport->port.membase + UCR1);
@@ -880,6 +930,8 @@ static void imx_rx_dma_done(struct imx_port *sport)
        /* Is the shutdown waiting for us? */
        if (waitqueue_active(&sport->dma_wait))
                wake_up(&sport->dma_wait);
+
+       spin_unlock_irqrestore(&sport->port.lock, flags);
 }
 
 /*
@@ -910,12 +962,26 @@ static void dma_rx_callback(void *data)
        dev_dbg(sport->port.dev, "We get %d bytes.\n", count);
 
        if (count) {
-               tty_insert_flip_string(port, sport->rx_buf, count);
+               if (!(sport->port.ignore_status_mask & URXD_DUMMY_READ))
+                       tty_insert_flip_string(port, sport->rx_buf, count);
                tty_flip_buffer_push(port);
 
                start_rx_dma(sport);
-       } else
+       } else if (readl(sport->port.membase + USR2) & USR2_RDR) {
+               /*
+                * start rx_dma directly once data in RXFIFO, more efficient
+                * than before:
+                *      1. call imx_rx_dma_done to stop dma if no data received
+                *      2. wait next  RDR interrupt to start dma transfer.
+                */
+               start_rx_dma(sport);
+       } else {
+               /*
+                * stop dma to prevent too many IDLE event trigged if no data
+                * in RXFIFO
+                */
                imx_rx_dma_done(sport);
+       }
 }
 
 static int start_rx_dma(struct imx_port *sport)
@@ -935,6 +1001,7 @@ static int start_rx_dma(struct imx_port *sport)
        desc = dmaengine_prep_slave_sg(chan, sgl, 1, DMA_DEV_TO_MEM,
                                        DMA_PREP_INTERRUPT);
        if (!desc) {
+               dma_unmap_sg(dev, sgl, 1, DMA_FROM_DEVICE);
                dev_err(dev, "We cannot prepare for the RX slave dma!\n");
                return -EINVAL;
        }
@@ -1108,12 +1175,20 @@ static int imx_startup(struct uart_port *port)
        while (!(readl(sport->port.membase + UCR2) & UCR2_SRST) && (--i > 0))
                udelay(1);
 
+       /* Can we enable the DMA support? */
+       if (is_imx6q_uart(sport) && !uart_console(port) &&
+           !sport->dma_is_inited)
+               imx_uart_dma_init(sport);
+
        spin_lock_irqsave(&sport->port.lock, flags);
        /*
         * Finally, clear and enable interrupts
         */
        writel(USR1_RTSD, sport->port.membase + USR1);
 
+       if (sport->dma_is_inited && !sport->dma_is_enabled)
+               imx_enable_dma(sport);
+
        temp = readl(sport->port.membase + UCR1);
        temp |= UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN;
 
@@ -1124,6 +1199,14 @@ static int imx_startup(struct uart_port *port)
 
        writel(temp, sport->port.membase + UCR1);
 
+       /* Clear any pending ORE flag before enabling interrupt */
+       temp = readl(sport->port.membase + USR2);
+       writel(temp | USR2_ORE, sport->port.membase + USR2);
+
+       temp = readl(sport->port.membase + UCR4);
+       temp |= UCR4_OREN;
+       writel(temp, sport->port.membase + UCR4);
+
        temp = readl(sport->port.membase + UCR2);
        temp |= (UCR2_RXEN | UCR2_TXEN);
        if (!sport->have_rtscts)
@@ -1189,9 +1272,11 @@ static void imx_shutdown(struct uart_port *port)
                        dmaengine_terminate_all(sport->dma_chan_tx);
                        dmaengine_terminate_all(sport->dma_chan_rx);
                }
+               spin_lock_irqsave(&sport->port.lock, flags);
                imx_stop_tx(port);
                imx_stop_rx(port);
                imx_disable_dma(sport);
+               spin_unlock_irqrestore(&sport->port.lock, flags);
                imx_uart_dma_exit(sport);
        }
 
@@ -1233,11 +1318,48 @@ static void imx_shutdown(struct uart_port *port)
 static void imx_flush_buffer(struct uart_port *port)
 {
        struct imx_port *sport = (struct imx_port *)port;
+       struct scatterlist *sgl = &sport->tx_sgl[0];
+       unsigned long temp;
+       int i = 100, ubir, ubmr, ubrc, uts;
 
-       if (sport->dma_is_enabled) {
-               sport->tx_bytes = 0;
-               dmaengine_terminate_all(sport->dma_chan_tx);
+       if (!sport->dma_chan_tx)
+               return;
+
+       sport->tx_bytes = 0;
+       dmaengine_terminate_all(sport->dma_chan_tx);
+       if (sport->dma_is_txing) {
+               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);
+               sport->dma_is_txing = false;
        }
+
+       /*
+        * According to the Reference Manual description of the UART SRST bit:
+        * "Reset the transmit and receive state machines,
+        * all FIFOs and register USR1, USR2, UBIR, UBMR, UBRC, URXD, UTXD
+        * and UTS[6-3]". As we don't need to restore the old values from
+        * USR1, USR2, URXD, UTXD, only save/restore the other four registers
+        */
+       ubir = readl(sport->port.membase + UBIR);
+       ubmr = readl(sport->port.membase + UBMR);
+       ubrc = readl(sport->port.membase + UBRC);
+       uts = readl(sport->port.membase + IMX21_UTS);
+
+       temp = readl(sport->port.membase + UCR2);
+       temp &= ~UCR2_SRST;
+       writel(temp, sport->port.membase + UCR2);
+
+       while (!(readl(sport->port.membase + UCR2) & UCR2_SRST) && (--i > 0))
+               udelay(1);
+
+       /* Restore the registers */
+       writel(ubir, sport->port.membase + UBIR);
+       writel(ubmr, sport->port.membase + UBMR);
+       writel(ubrc, sport->port.membase + UBRC);
+       writel(uts, sport->port.membase + IMX21_UTS);
 }
 
 static void
@@ -1280,11 +1402,6 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
                if (sport->have_rtscts) {
                        ucr2 &= ~UCR2_IRTS;
                        ucr2 |= UCR2_CTSC;
-
-                       /* Can we enable the DMA support? */
-                       if (is_imx6q_uart(sport) && !uart_console(port)
-                               && !sport->dma_is_inited)
-                               imx_uart_dma_init(sport);
                } else {
                        termios->c_cflag &= ~CRTSCTS;
                }
@@ -1319,7 +1436,7 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
         */
        sport->port.ignore_status_mask = 0;
        if (termios->c_iflag & IGNPAR)
-               sport->port.ignore_status_mask |= URXD_PRERR;
+               sport->port.ignore_status_mask |= URXD_PRERR | URXD_FRMERR;
        if (termios->c_iflag & IGNBRK) {
                sport->port.ignore_status_mask |= URXD_BRK;
                /*
@@ -1330,6 +1447,9 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
                        sport->port.ignore_status_mask |= URXD_OVRRUN;
        }
 
+       if ((termios->c_cflag & CREAD) == 0)
+               sport->port.ignore_status_mask |= URXD_DUMMY_READ;
+
        /*
         * Update the per-port timeout.
         */
@@ -1403,8 +1523,6 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
        if (UART_ENABLE_MS(&sport->port, termios->c_cflag))
                imx_enable_ms(&sport->port);
 
-       if (sport->dma_is_inited && !sport->dma_is_enabled)
-               imx_enable_dma(sport);
        spin_unlock_irqrestore(&sport->port.lock, flags);
 }
 
index 10496672dfdbfcad71de2259420aa8b085d883aa..a9b0ab38a68c1c529629d4ec526f51257d0efcdc 100644 (file)
@@ -3,7 +3,7 @@
 /*
  *     mcf.c -- Freescale ColdFire UART driver
  *
- *     (C) Copyright 2003-2007, Greg Ungerer <gerg@snapgear.com>
+ *     (C) Copyright 2003-2007, Greg Ungerer <gerg@uclinux.org>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -198,7 +198,6 @@ static void mcf_shutdown(struct uart_port *port)
 static void mcf_set_termios(struct uart_port *port, struct ktermios *termios,
        struct ktermios *old)
 {
-       struct mcf_uart *pp = container_of(port, struct mcf_uart, port);
        unsigned long flags;
        unsigned int baud, baudclk;
 #if defined(CONFIG_M5272)
@@ -441,7 +440,6 @@ static int mcf_verify_port(struct uart_port *port, struct serial_struct *ser)
 /* Enable or disable the RS485 support */
 static int mcf_config_rs485(struct uart_port *port, struct serial_rs485 *rs485)
 {
-       struct mcf_uart *pp = container_of(port, struct mcf_uart, port);
        unsigned char mr1, mr2;
 
        /* Get mode registers */
@@ -631,6 +629,7 @@ static int mcf_probe(struct platform_device *pdev)
                port->mapbase = platp[i].mapbase;
                port->membase = (platp[i].membase) ? platp[i].membase :
                        (unsigned char __iomem *) platp[i].mapbase;
+               port->dev = &pdev->dev;
                port->iotype = SERIAL_IO_MEM;
                port->irq = platp[i].irq;
                port->uartclk = MCF_BUSCLK;
@@ -702,7 +701,7 @@ static void __exit mcf_exit(void)
 module_init(mcf_init);
 module_exit(mcf_exit);
 
-MODULE_AUTHOR("Greg Ungerer <gerg@snapgear.com>");
+MODULE_AUTHOR("Greg Ungerer <gerg@uclinux.org>");
 MODULE_DESCRIPTION("Freescale ColdFire UART driver");
 MODULE_LICENSE("GPL");
 MODULE_ALIAS("platform:mcfuart");
index 517cd073dc0856a7a5e078d1b3ecc95d698189b5..35c55505b3eb32e38536c2abbce86cfe523b6288 100644 (file)
@@ -23,7 +23,6 @@
 #define MEN_Z135_MAX_PORTS             12
 #define MEN_Z135_BASECLK               29491200
 #define MEN_Z135_FIFO_SIZE             1024
-#define MEN_Z135_NUM_MSI_VECTORS       2
 #define MEN_Z135_FIFO_WATERMARK                1020
 
 #define MEN_Z135_STAT_REG              0x0
 #define MEN_Z135_CONF_REG              0x808
 #define MEN_Z135_UART_FREQ             0x80c
 #define MEN_Z135_BAUD_REG              0x810
-#define MENZ135_TIMEOUT                        0x814
+#define MEN_Z135_TIMEOUT               0x814
 
 #define MEN_Z135_MEM_SIZE              0x818
 
-#define IS_IRQ(x) ((x) & 1)
-#define IRQ_ID(x) (((x) >> 1) & 7)
+#define IRQ_ID(x) ((x) & 0x1f)
 
 #define MEN_Z135_IER_RXCIEN BIT(0)             /* RX Space IRQ */
 #define MEN_Z135_IER_TXCIEN BIT(1)             /* TX Space IRQ */
 #define MEN_Z135_LSR_TEXP BIT(6)
 #define MEN_Z135_LSR_RXFIFOERR BIT(7)
 
-#define MEN_Z135_IRQ_ID_MST 0
-#define MEN_Z135_IRQ_ID_TSA 1
-#define MEN_Z135_IRQ_ID_RDA 2
-#define MEN_Z135_IRQ_ID_RLS 3
-#define MEN_Z135_IRQ_ID_CTI 6
+#define MEN_Z135_IRQ_ID_RLS BIT(0)
+#define MEN_Z135_IRQ_ID_RDA BIT(1)
+#define MEN_Z135_IRQ_ID_CTI BIT(2)
+#define MEN_Z135_IRQ_ID_TSA BIT(3)
+#define MEN_Z135_IRQ_ID_MST BIT(4)
 
 #define LCR(x) (((x) >> MEN_Z135_LCR_SHIFT) & 0xff)
 
@@ -118,12 +116,18 @@ static int align;
 module_param(align, int, S_IRUGO);
 MODULE_PARM_DESC(align, "Keep hardware FIFO write pointer aligned, default 0");
 
+static uint rx_timeout;
+module_param(rx_timeout, uint, S_IRUGO);
+MODULE_PARM_DESC(rx_timeout, "RX timeout. "
+               "Timeout in seconds = (timeout_reg * baud_reg * 4) / freq_reg");
+
 struct men_z135_port {
        struct uart_port port;
        struct mcb_device *mdev;
        unsigned char *rxbuf;
        u32 stat_reg;
        spinlock_t lock;
+       bool automode;
 };
 #define to_men_z135(port) container_of((port), struct men_z135_port, port)
 
@@ -180,12 +184,16 @@ static inline void men_z135_reg_clr(struct men_z135_port *uart,
  */
 static void men_z135_handle_modem_status(struct men_z135_port *uart)
 {
-       if (uart->stat_reg & MEN_Z135_MSR_DDCD)
+       u8 msr;
+
+       msr = (uart->stat_reg >> 8) & 0xff;
+
+       if (msr & MEN_Z135_MSR_DDCD)
                uart_handle_dcd_change(&uart->port,
-                               uart->stat_reg & ~MEN_Z135_MSR_DCD);
-       if (uart->stat_reg & MEN_Z135_MSR_DCTS)
+                               msr & MEN_Z135_MSR_DCD);
+       if (msr & MEN_Z135_MSR_DCTS)
                uart_handle_cts_change(&uart->port,
-                               uart->stat_reg & ~MEN_Z135_MSR_CTS);
+                               msr & MEN_Z135_MSR_CTS);
 }
 
 static void men_z135_handle_lsr(struct men_z135_port *uart)
@@ -322,7 +330,8 @@ static void men_z135_handle_tx(struct men_z135_port *uart)
 
        txfree = MEN_Z135_FIFO_WATERMARK - txc;
        if (txfree <= 0) {
-               pr_err("Not enough room in TX FIFO have %d, need %d\n",
+               dev_err(&uart->mdev->dev,
+                       "Not enough room in TX FIFO have %d, need %d\n",
                        txfree, qlen);
                goto irq_en;
        }
@@ -373,43 +382,54 @@ out:
  * @irq: The IRQ number
  * @data: Pointer to UART port
  *
- * Check IIR register to see which tasklet to start.
+ * Check IIR register to find the cause of the interrupt and handle it.
+ * It is possible that multiple interrupts reason bits are set and reading
+ * the IIR is a destructive read, so we always need to check for all possible
+ * interrupts and handle them.
  */
 static irqreturn_t men_z135_intr(int irq, void *data)
 {
        struct men_z135_port *uart = (struct men_z135_port *)data;
        struct uart_port *port = &uart->port;
+       bool handled = false;
+       unsigned long flags;
        int irq_id;
 
        uart->stat_reg = ioread32(port->membase + MEN_Z135_STAT_REG);
-       /* IRQ pending is low active */
-       if (IS_IRQ(uart->stat_reg))
-               return IRQ_NONE;
-
        irq_id = IRQ_ID(uart->stat_reg);
-       switch (irq_id) {
-       case MEN_Z135_IRQ_ID_MST:
-               men_z135_handle_modem_status(uart);
-               break;
-       case MEN_Z135_IRQ_ID_TSA:
-               men_z135_handle_tx(uart);
-               break;
-       case MEN_Z135_IRQ_ID_CTI:
-               dev_dbg(&uart->mdev->dev, "Character Timeout Indication\n");
-               /* Fallthrough */
-       case MEN_Z135_IRQ_ID_RDA:
-               /* Reading data clears RX IRQ */
-               men_z135_handle_rx(uart);
-               break;
-       case MEN_Z135_IRQ_ID_RLS:
+
+       if (!irq_id)
+               goto out;
+
+       spin_lock_irqsave(&port->lock, flags);
+       /* It's save to write to IIR[7:6] RXC[9:8] */
+       iowrite8(irq_id, port->membase + MEN_Z135_STAT_REG);
+
+       if (irq_id & MEN_Z135_IRQ_ID_RLS) {
                men_z135_handle_lsr(uart);
-               break;
-       default:
-               dev_warn(&uart->mdev->dev, "Unknown IRQ id %d\n", irq_id);
-               return IRQ_NONE;
+               handled = true;
+       }
+
+       if (irq_id & (MEN_Z135_IRQ_ID_RDA | MEN_Z135_IRQ_ID_CTI)) {
+               if (irq_id & MEN_Z135_IRQ_ID_CTI)
+                       dev_dbg(&uart->mdev->dev, "Character Timeout Indication\n");
+               men_z135_handle_rx(uart);
+               handled = true;
+       }
+
+       if (irq_id & MEN_Z135_IRQ_ID_TSA) {
+               men_z135_handle_tx(uart);
+               handled = true;
        }
 
-       return IRQ_HANDLED;
+       if (irq_id & MEN_Z135_IRQ_ID_MST) {
+               men_z135_handle_modem_status(uart);
+               handled = true;
+       }
+
+       spin_unlock_irqrestore(&port->lock, flags);
+out:
+       return IRQ_RETVAL(handled);
 }
 
 /**
@@ -464,21 +484,37 @@ static unsigned int men_z135_tx_empty(struct uart_port *port)
  */
 static void men_z135_set_mctrl(struct uart_port *port, unsigned int mctrl)
 {
-       struct men_z135_port *uart = to_men_z135(port);
-       u32 conf_reg = 0;
+       u32 old;
+       u32 conf_reg;
 
+       conf_reg = old = ioread32(port->membase + MEN_Z135_CONF_REG);
        if (mctrl & TIOCM_RTS)
                conf_reg |= MEN_Z135_MCR_RTS;
+       else
+               conf_reg &= ~MEN_Z135_MCR_RTS;
+
        if (mctrl & TIOCM_DTR)
                conf_reg |= MEN_Z135_MCR_DTR;
+       else
+               conf_reg &= ~MEN_Z135_MCR_DTR;
+
        if (mctrl & TIOCM_OUT1)
                conf_reg |= MEN_Z135_MCR_OUT1;
+       else
+               conf_reg &= ~MEN_Z135_MCR_OUT1;
+
        if (mctrl & TIOCM_OUT2)
                conf_reg |= MEN_Z135_MCR_OUT2;
+       else
+               conf_reg &= ~MEN_Z135_MCR_OUT2;
+
        if (mctrl & TIOCM_LOOP)
                conf_reg |= MEN_Z135_MCR_LOOP;
+       else
+               conf_reg &= ~MEN_Z135_MCR_LOOP;
 
-       men_z135_reg_set(uart, MEN_Z135_CONF_REG, conf_reg);
+       if (conf_reg != old)
+               iowrite32(conf_reg, port->membase + MEN_Z135_CONF_REG);
 }
 
 /**
@@ -490,12 +526,9 @@ static void men_z135_set_mctrl(struct uart_port *port, unsigned int mctrl)
 static unsigned int men_z135_get_mctrl(struct uart_port *port)
 {
        unsigned int mctrl = 0;
-       u32 stat_reg;
        u8 msr;
 
-       stat_reg = ioread32(port->membase + MEN_Z135_STAT_REG);
-
-       msr = ~((stat_reg >> 8) & 0xff);
+       msr = ioread8(port->membase + MEN_Z135_STAT_REG + 1);
 
        if (msr & MEN_Z135_MSR_CTS)
                mctrl |= TIOCM_CTS;
@@ -524,6 +557,19 @@ static void men_z135_stop_tx(struct uart_port *port)
        men_z135_reg_clr(uart, MEN_Z135_CONF_REG, MEN_Z135_IER_TXCIEN);
 }
 
+/*
+ * men_z135_disable_ms() - Disable Modem Status
+ * port: The UART port
+ *
+ * Enable Modem Status IRQ.
+ */
+static void men_z135_disable_ms(struct uart_port *port)
+{
+       struct men_z135_port *uart = to_men_z135(port);
+
+       men_z135_reg_clr(uart, MEN_Z135_CONF_REG, MEN_Z135_IER_MSIEN);
+}
+
 /**
  * men_z135_start_tx() - Start transmitting characters
  * @port: The UART port
@@ -535,6 +581,9 @@ static void men_z135_start_tx(struct uart_port *port)
 {
        struct men_z135_port *uart = to_men_z135(port);
 
+       if (uart->automode)
+               men_z135_disable_ms(port);
+
        men_z135_handle_tx(uart);
 }
 
@@ -584,6 +633,9 @@ static int men_z135_startup(struct uart_port *port)
 
        iowrite32(conf_reg, port->membase + MEN_Z135_CONF_REG);
 
+       if (rx_timeout)
+               iowrite32(rx_timeout, port->membase + MEN_Z135_TIMEOUT);
+
        return 0;
 }
 
@@ -603,6 +655,7 @@ static void men_z135_set_termios(struct uart_port *port,
                                struct ktermios *termios,
                                struct ktermios *old)
 {
+       struct men_z135_port *uart = to_men_z135(port);
        unsigned int baud;
        u32 conf_reg;
        u32 bd_reg;
@@ -643,6 +696,16 @@ static void men_z135_set_termios(struct uart_port *port,
        } else
                lcr |= MEN_Z135_PAR_DIS << MEN_Z135_PEN_SHIFT;
 
+       conf_reg |= MEN_Z135_IER_MSIEN;
+       if (termios->c_cflag & CRTSCTS) {
+               conf_reg |= MEN_Z135_MCR_RCFC;
+               uart->automode = true;
+               termios->c_cflag &= ~CLOCAL;
+       } else {
+               conf_reg &= ~MEN_Z135_MCR_RCFC;
+               uart->automode = false;
+       }
+
        termios->c_cflag &= ~CMSPAR; /* Mark/Space parity is not supported */
 
        conf_reg |= lcr << MEN_Z135_LCR_SHIFT;
diff --git a/drivers/tty/serial/mrst_max3110.c b/drivers/tty/serial/mrst_max3110.c
deleted file mode 100644 (file)
index 77239d5..0000000
+++ /dev/null
@@ -1,909 +0,0 @@
-/*
- *  mrst_max3110.c - spi uart protocol driver for Maxim 3110
- *
- * Copyright (c) 2008-2010, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-/*
- * Note:
- * 1. From Max3110 spec, the Rx FIFO has 8 words, while the Tx FIFO only has
- *    1 word. If SPI master controller doesn't support sclk frequency change,
- *    then the char need be sent out one by one with some delay
- *
- * 2. Currently only RX available interrupt is used, no need for waiting TXE
- *    interrupt for a low speed UART device
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#ifdef CONFIG_MAGIC_SYSRQ
-#define SUPPORT_SYSRQ
-#endif
-
-#include <linux/module.h>
-#include <linux/ioport.h>
-#include <linux/irq.h>
-#include <linux/init.h>
-#include <linux/console.h>
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
-#include <linux/serial_core.h>
-#include <linux/serial_reg.h>
-
-#include <linux/kthread.h>
-#include <linux/spi/spi.h>
-#include <linux/pm.h>
-
-#include "mrst_max3110.h"
-
-#define UART_TX_NEEDED 1
-#define CON_TX_NEEDED  2
-#define BIT_IRQ_PENDING    3
-
-struct uart_max3110 {
-       struct uart_port port;
-       struct spi_device *spi;
-       char name[SPI_NAME_SIZE];
-
-       wait_queue_head_t wq;
-       struct task_struct *main_thread;
-       struct task_struct *read_thread;
-       struct mutex thread_mutex;
-       struct mutex io_mutex;
-
-       u32 baud;
-       u16 cur_conf;
-       u8 clock;
-       u8 parity, word_7bits;
-       u16 irq;
-
-       unsigned long uart_flags;
-
-       /* console related */
-       struct circ_buf con_xmit;
-};
-
-/* global data structure, may need be removed */
-static struct uart_max3110 *pmax;
-
-static int receive_chars(struct uart_max3110 *max,
-                               unsigned short *str, int len);
-static int max3110_read_multi(struct uart_max3110 *max);
-static void max3110_con_receive(struct uart_max3110 *max);
-
-static int max3110_write_then_read(struct uart_max3110 *max,
-               const void *txbuf, void *rxbuf, unsigned len, int always_fast)
-{
-       struct spi_device *spi = max->spi;
-       struct spi_message      message;
-       struct spi_transfer     x;
-       int ret;
-
-       mutex_lock(&max->io_mutex);
-       spi_message_init(&message);
-       memset(&x, 0, sizeof x);
-       x.len = len;
-       x.tx_buf = txbuf;
-       x.rx_buf = rxbuf;
-       spi_message_add_tail(&x, &message);
-
-       if (always_fast)
-               x.speed_hz = spi->max_speed_hz;
-       else if (max->baud)
-               x.speed_hz = max->baud;
-
-       /* Do the i/o */
-       ret = spi_sync(spi, &message);
-       mutex_unlock(&max->io_mutex);
-       return ret;
-}
-
-/* Write a 16b word to the device */
-static int max3110_out(struct uart_max3110 *max, const u16 out)
-{
-       void *buf;
-       u16 *obuf, *ibuf;
-       int ret;
-
-       buf = kzalloc(8, GFP_KERNEL | GFP_DMA);
-       if (!buf)
-               return -ENOMEM;
-
-       obuf = buf;
-       ibuf = buf + 4;
-       *obuf = out;
-       ret = max3110_write_then_read(max, obuf, ibuf, 2, 1);
-       if (ret) {
-               pr_warn("%s: get err msg %d when sending 0x%x\n",
-                       __func__, ret, out);
-               goto exit;
-       }
-
-       receive_chars(max, ibuf, 1);
-
-exit:
-       kfree(buf);
-       return ret;
-}
-
-/*
- * This is usually used to read data from SPIC RX FIFO, which doesn't
- * need any delay like flushing character out.
- *
- * Return how many valide bytes are read back
- */
-static int max3110_read_multi(struct uart_max3110 *max)
-{
-       void *buf;
-       u16 *obuf, *ibuf;
-       int ret, blen;
-
-       blen = M3110_RX_FIFO_DEPTH * sizeof(u16);
-       buf = kzalloc(blen * 2, GFP_KERNEL | GFP_DMA);
-       if (!buf)
-               return 0;
-
-       /* tx/rx always have the same length */
-       obuf = buf;
-       ibuf = buf + blen;
-
-       if (max3110_write_then_read(max, obuf, ibuf, blen, 1)) {
-               kfree(buf);
-               return 0;
-       }
-
-       ret = receive_chars(max, ibuf, M3110_RX_FIFO_DEPTH);
-
-       kfree(buf);
-       return ret;
-}
-
-static void serial_m3110_con_putchar(struct uart_port *port, int ch)
-{
-       struct uart_max3110 *max =
-               container_of(port, struct uart_max3110, port);
-       struct circ_buf *xmit = &max->con_xmit;
-
-       if (uart_circ_chars_free(xmit)) {
-               xmit->buf[xmit->head] = (char)ch;
-               xmit->head = (xmit->head + 1) & (PAGE_SIZE - 1);
-       }
-}
-
-/*
- * Print a string to the serial port trying not to disturb
- * any possible real use of the port...
- *
- *     The console_lock must be held when we get here.
- */
-static void serial_m3110_con_write(struct console *co,
-                               const char *s, unsigned int count)
-{
-       if (!pmax)
-               return;
-
-       uart_console_write(&pmax->port, s, count, serial_m3110_con_putchar);
-
-       if (!test_and_set_bit(CON_TX_NEEDED, &pmax->uart_flags))
-               wake_up(&pmax->wq);
-}
-
-static int __init
-serial_m3110_con_setup(struct console *co, char *options)
-{
-       struct uart_max3110 *max = pmax;
-       int baud = 115200;
-       int bits = 8;
-       int parity = 'n';
-       int flow = 'n';
-
-       pr_info("setting up console\n");
-
-       if (co->index == -1)
-               co->index = 0;
-
-       if (!max) {
-               pr_err("pmax is NULL, return\n");
-               return -ENODEV;
-       }
-
-       if (options)
-               uart_parse_options(options, &baud, &parity, &bits, &flow);
-
-       return uart_set_options(&max->port, co, baud, parity, bits, flow);
-}
-
-static struct tty_driver *serial_m3110_con_device(struct console *co,
-                                                       int *index)
-{
-       struct uart_driver *p = co->data;
-       *index = co->index;
-       return p->tty_driver;
-}
-
-static struct uart_driver serial_m3110_reg;
-static struct console serial_m3110_console = {
-       .name           = "ttyS",
-       .write          = serial_m3110_con_write,
-       .device         = serial_m3110_con_device,
-       .setup          = serial_m3110_con_setup,
-       .flags          = CON_PRINTBUFFER,
-       .index          = -1,
-       .data           = &serial_m3110_reg,
-};
-
-static unsigned int serial_m3110_tx_empty(struct uart_port *port)
-{
-       return 1;
-}
-
-static void serial_m3110_stop_tx(struct uart_port *port)
-{
-       return;
-}
-
-/* stop_rx will be called in spin_lock env */
-static void serial_m3110_stop_rx(struct uart_port *port)
-{
-       return;
-}
-
-#define WORDS_PER_XFER 128
-static void send_circ_buf(struct uart_max3110 *max,
-                               struct circ_buf *xmit)
-{
-       void *buf;
-       u16 *obuf, *ibuf;
-       int i, len, blen, dma_size, left, ret = 0;
-
-
-       dma_size = WORDS_PER_XFER * sizeof(u16) * 2;
-       buf = kzalloc(dma_size, GFP_KERNEL | GFP_DMA);
-       if (!buf)
-               return;
-       obuf = buf;
-       ibuf = buf + dma_size/2;
-
-       while (!uart_circ_empty(xmit)) {
-               left = uart_circ_chars_pending(xmit);
-               while (left) {
-                       len = min(left, WORDS_PER_XFER);
-                       blen = len * sizeof(u16);
-                       memset(ibuf, 0, blen);
-
-                       for (i = 0; i < len; i++) {
-                               obuf[i] = (u8)xmit->buf[xmit->tail] | WD_TAG;
-                               xmit->tail = (xmit->tail + 1) &
-                                               (UART_XMIT_SIZE - 1);
-                       }
-
-                       /* Fail to send msg to console is not very critical */
-
-                       ret = max3110_write_then_read(max, obuf, ibuf, blen, 0);
-                       if (ret)
-                               pr_warn("%s: get err msg %d\n", __func__, ret);
-
-                       receive_chars(max, ibuf, len);
-
-                       max->port.icount.tx += len;
-                       left -= len;
-               }
-       }
-
-       kfree(buf);
-}
-
-static void transmit_char(struct uart_max3110 *max)
-{
-       struct uart_port *port = &max->port;
-       struct circ_buf *xmit = &port->state->xmit;
-
-       if (uart_circ_empty(xmit) || uart_tx_stopped(port))
-               return;
-
-       send_circ_buf(max, xmit);
-
-       if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
-               uart_write_wakeup(port);
-
-       if (uart_circ_empty(xmit))
-               serial_m3110_stop_tx(port);
-}
-
-/*
- * This will be called by uart_write() and tty_write, can't
- * go to sleep
- */
-static void serial_m3110_start_tx(struct uart_port *port)
-{
-       struct uart_max3110 *max =
-               container_of(port, struct uart_max3110, port);
-
-       if (!test_and_set_bit(UART_TX_NEEDED, &max->uart_flags))
-               wake_up(&max->wq);
-}
-
-static int
-receive_chars(struct uart_max3110 *max, unsigned short *str, int len)
-{
-       struct uart_port *port = &max->port;
-       struct tty_port *tport;
-       char buf[M3110_RX_FIFO_DEPTH];
-       int r, w, usable;
-
-       /* If uart is not opened, just return */
-       if (!port->state)
-               return 0;
-
-       tport = &port->state->port;
-
-       for (r = 0, w = 0; r < len; r++) {
-               if (str[r] & MAX3110_BREAK &&
-                   uart_handle_break(port))
-                       continue;
-
-               if (str[r] & MAX3110_READ_DATA_AVAILABLE) {
-                       if (uart_handle_sysrq_char(port, str[r] & 0xff))
-                               continue;
-
-                       buf[w++] = str[r] & 0xff;
-               }
-       }
-
-       if (!w)
-               return 0;
-
-       for (r = 0; w; r += usable, w -= usable) {
-               usable = tty_buffer_request_room(tport, w);
-               if (usable) {
-                       tty_insert_flip_string(tport, buf + r, usable);
-                       port->icount.rx += usable;
-               }
-       }
-       tty_flip_buffer_push(tport);
-
-       return r;
-}
-
-/*
- * This routine will be used in read_thread or RX IRQ handling,
- * it will first do one round buffer read(8 words), if there is some
- * valid RX data, will try to read 5 more rounds till all data
- * is read out.
- *
- * Use stack space as data buffer to save some system load, and chose
- * 504 Btyes as a threadhold to do a bulk push to upper tty layer when
- * receiving bulk data, a much bigger buffer may cause stack overflow
- */
-static void max3110_con_receive(struct uart_max3110 *max)
-{
-       int loop = 1, num;
-
-       do {
-               num = max3110_read_multi(max);
-
-               if (num) {
-                       loop = 5;
-               }
-       } while (--loop);
-}
-
-static int max3110_main_thread(void *_max)
-{
-       struct uart_max3110 *max = _max;
-       wait_queue_head_t *wq = &max->wq;
-       int ret = 0;
-       struct circ_buf *xmit = &max->con_xmit;
-
-       pr_info("start main thread\n");
-
-       do {
-               wait_event_interruptible(*wq,
-                               max->uart_flags || kthread_should_stop());
-
-               mutex_lock(&max->thread_mutex);
-
-               if (test_and_clear_bit(BIT_IRQ_PENDING, &max->uart_flags))
-                       max3110_con_receive(max);
-
-               /* first handle console output */
-               if (test_and_clear_bit(CON_TX_NEEDED, &max->uart_flags))
-                       send_circ_buf(max, xmit);
-
-               /* handle uart output */
-               if (test_and_clear_bit(UART_TX_NEEDED, &max->uart_flags))
-                       transmit_char(max);
-
-               mutex_unlock(&max->thread_mutex);
-
-       } while (!kthread_should_stop());
-
-       return ret;
-}
-
-static irqreturn_t serial_m3110_irq(int irq, void *dev_id)
-{
-       struct uart_max3110 *max = dev_id;
-
-       /* max3110's irq is a falling edge, not level triggered,
-        * so no need to disable the irq */
-
-       if (!test_and_set_bit(BIT_IRQ_PENDING, &max->uart_flags))
-               wake_up(&max->wq);
-
-       return IRQ_HANDLED;
-}
-
-/* if don't use RX IRQ, then need a thread to polling read */
-static int max3110_read_thread(void *_max)
-{
-       struct uart_max3110 *max = _max;
-
-       pr_info("start read thread\n");
-       do {
-               /*
-                * If can't acquire the mutex, it means the main thread
-                * is running which will also perform the rx job
-                */
-               if (mutex_trylock(&max->thread_mutex)) {
-                       max3110_con_receive(max);
-                       mutex_unlock(&max->thread_mutex);
-               }
-
-               set_current_state(TASK_INTERRUPTIBLE);
-               schedule_timeout(HZ / 20);
-       } while (!kthread_should_stop());
-
-       return 0;
-}
-
-static int serial_m3110_startup(struct uart_port *port)
-{
-       struct uart_max3110 *max =
-               container_of(port, struct uart_max3110, port);
-       u16 config = 0;
-       int ret = 0;
-
-       if (port->line != 0) {
-               pr_err("uart port startup failed\n");
-               return -1;
-       }
-
-       /* Disable all IRQ and config it to 115200, 8n1 */
-       config = WC_TAG | WC_FIFO_ENABLE
-                       | WC_1_STOPBITS
-                       | WC_8BIT_WORD
-                       | WC_BAUD_DR2;
-
-       /* as we use thread to handle tx/rx, need set low latency */
-       port->state->port.low_latency = 1;
-
-       if (max->irq) {
-               /* Enable RX IRQ only */
-               config |= WC_RXA_IRQ_ENABLE;
-       } else {
-               /* If IRQ is disabled, start a read thread for input data */
-               max->read_thread =
-                       kthread_run(max3110_read_thread, max, "max3110_read");
-               if (IS_ERR(max->read_thread)) {
-                       ret = PTR_ERR(max->read_thread);
-                       max->read_thread = NULL;
-                       pr_err("Can't create read thread!\n");
-                       return ret;
-               }
-       }
-
-       ret = max3110_out(max, config);
-       if (ret) {
-               if (max->read_thread)
-                       kthread_stop(max->read_thread);
-               max->read_thread = NULL;
-               return ret;
-       }
-
-       max->cur_conf = config;
-       return 0;
-}
-
-static void serial_m3110_shutdown(struct uart_port *port)
-{
-       struct uart_max3110 *max =
-               container_of(port, struct uart_max3110, port);
-       u16 config;
-
-       if (max->read_thread) {
-               kthread_stop(max->read_thread);
-               max->read_thread = NULL;
-       }
-
-       /* Disable interrupts from this port */
-       config = WC_TAG | WC_SW_SHDI;
-       max3110_out(max, config);
-}
-
-static void serial_m3110_release_port(struct uart_port *port)
-{
-}
-
-static int serial_m3110_request_port(struct uart_port *port)
-{
-       return 0;
-}
-
-static void serial_m3110_config_port(struct uart_port *port, int flags)
-{
-       port->type = PORT_MAX3100;
-}
-
-static int
-serial_m3110_verify_port(struct uart_port *port, struct serial_struct *ser)
-{
-       /* we don't want the core code to modify any port params */
-       return -EINVAL;
-}
-
-
-static const char *serial_m3110_type(struct uart_port *port)
-{
-       struct uart_max3110 *max =
-               container_of(port, struct uart_max3110, port);
-       return max->name;
-}
-
-static void
-serial_m3110_set_termios(struct uart_port *port, struct ktermios *termios,
-                      struct ktermios *old)
-{
-       struct uart_max3110 *max =
-               container_of(port, struct uart_max3110, port);
-       unsigned char cval;
-       unsigned int baud, parity = 0;
-       int clk_div = -1;
-       u16 new_conf = max->cur_conf;
-
-       switch (termios->c_cflag & CSIZE) {
-       case CS7:
-               cval = UART_LCR_WLEN7;
-               new_conf |= WC_7BIT_WORD;
-               break;
-       default:
-               /* We only support CS7 & CS8 */
-               termios->c_cflag &= ~CSIZE;
-               termios->c_cflag |= CS8;
-       case CS8:
-               cval = UART_LCR_WLEN8;
-               new_conf |= WC_8BIT_WORD;
-               break;
-       }
-
-       baud = uart_get_baud_rate(port, termios, old, 0, 230400);
-
-       /* First calc the div for 1.8MHZ clock case */
-       switch (baud) {
-       case 300:
-               clk_div = WC_BAUD_DR384;
-               break;
-       case 600:
-               clk_div = WC_BAUD_DR192;
-               break;
-       case 1200:
-               clk_div = WC_BAUD_DR96;
-               break;
-       case 2400:
-               clk_div = WC_BAUD_DR48;
-               break;
-       case 4800:
-               clk_div = WC_BAUD_DR24;
-               break;
-       case 9600:
-               clk_div = WC_BAUD_DR12;
-               break;
-       case 19200:
-               clk_div = WC_BAUD_DR6;
-               break;
-       case 38400:
-               clk_div = WC_BAUD_DR3;
-               break;
-       case 57600:
-               clk_div = WC_BAUD_DR2;
-               break;
-       case 115200:
-               clk_div = WC_BAUD_DR1;
-               break;
-       case 230400:
-               if (max->clock & MAX3110_HIGH_CLK)
-                       break;
-       default:
-               /* Pick the previous baud rate */
-               baud = max->baud;
-               clk_div = max->cur_conf & WC_BAUD_DIV_MASK;
-               tty_termios_encode_baud_rate(termios, baud, baud);
-       }
-
-       if (max->clock & MAX3110_HIGH_CLK) {
-               clk_div += 1;
-               /* High clk version max3110 doesn't support B300 */
-               if (baud == 300) {
-                       baud = 600;
-                       clk_div = WC_BAUD_DR384;
-               }
-               if (baud == 230400)
-                       clk_div = WC_BAUD_DR1;
-               tty_termios_encode_baud_rate(termios, baud, baud);
-       }
-
-       new_conf = (new_conf & ~WC_BAUD_DIV_MASK) | clk_div;
-
-       if (unlikely(termios->c_cflag & CMSPAR))
-               termios->c_cflag &= ~CMSPAR;
-
-       if (termios->c_cflag & CSTOPB)
-               new_conf |= WC_2_STOPBITS;
-       else
-               new_conf &= ~WC_2_STOPBITS;
-
-       if (termios->c_cflag & PARENB) {
-               new_conf |= WC_PARITY_ENABLE;
-               parity |= UART_LCR_PARITY;
-       } else
-               new_conf &= ~WC_PARITY_ENABLE;
-
-       if (!(termios->c_cflag & PARODD))
-               parity |= UART_LCR_EPAR;
-       max->parity = parity;
-
-       uart_update_timeout(port, termios->c_cflag, baud);
-
-       new_conf |= WC_TAG;
-       if (new_conf != max->cur_conf) {
-               if (!max3110_out(max, new_conf)) {
-                       max->cur_conf = new_conf;
-                       max->baud = baud;
-               }
-       }
-}
-
-/* Don't handle hw handshaking */
-static unsigned int serial_m3110_get_mctrl(struct uart_port *port)
-{
-       return TIOCM_DSR | TIOCM_CAR | TIOCM_DSR;
-}
-
-static void serial_m3110_set_mctrl(struct uart_port *port, unsigned int mctrl)
-{
-}
-
-static void serial_m3110_break_ctl(struct uart_port *port, int break_state)
-{
-}
-
-static void serial_m3110_pm(struct uart_port *port, unsigned int state,
-                       unsigned int oldstate)
-{
-}
-
-static struct uart_ops serial_m3110_ops = {
-       .tx_empty       = serial_m3110_tx_empty,
-       .set_mctrl      = serial_m3110_set_mctrl,
-       .get_mctrl      = serial_m3110_get_mctrl,
-       .stop_tx        = serial_m3110_stop_tx,
-       .start_tx       = serial_m3110_start_tx,
-       .stop_rx        = serial_m3110_stop_rx,
-       .break_ctl      = serial_m3110_break_ctl,
-       .startup        = serial_m3110_startup,
-       .shutdown       = serial_m3110_shutdown,
-       .set_termios    = serial_m3110_set_termios,
-       .pm             = serial_m3110_pm,
-       .type           = serial_m3110_type,
-       .release_port   = serial_m3110_release_port,
-       .request_port   = serial_m3110_request_port,
-       .config_port    = serial_m3110_config_port,
-       .verify_port    = serial_m3110_verify_port,
-};
-
-static struct uart_driver serial_m3110_reg = {
-       .owner          = THIS_MODULE,
-       .driver_name    = "MRST serial",
-       .dev_name       = "ttyS",
-       .major          = TTY_MAJOR,
-       .minor          = 64,
-       .nr             = 1,
-       .cons           = &serial_m3110_console,
-};
-
-#ifdef CONFIG_PM_SLEEP
-static int serial_m3110_suspend(struct device *dev)
-{
-       struct spi_device *spi = to_spi_device(dev);
-       struct uart_max3110 *max = spi_get_drvdata(spi);
-
-       if (max->irq > 0)
-               disable_irq(max->irq);
-       uart_suspend_port(&serial_m3110_reg, &max->port);
-       max3110_out(max, max->cur_conf | WC_SW_SHDI);
-       return 0;
-}
-
-static int serial_m3110_resume(struct device *dev)
-{
-       struct spi_device *spi = to_spi_device(dev);
-       struct uart_max3110 *max = spi_get_drvdata(spi);
-
-       max3110_out(max, max->cur_conf);
-       uart_resume_port(&serial_m3110_reg, &max->port);
-       if (max->irq > 0)
-               enable_irq(max->irq);
-       return 0;
-}
-
-static SIMPLE_DEV_PM_OPS(serial_m3110_pm_ops, serial_m3110_suspend,
-                       serial_m3110_resume);
-#define SERIAL_M3110_PM_OPS (&serial_m3110_pm_ops)
-
-#else
-#define SERIAL_M3110_PM_OPS NULL
-#endif
-
-static int serial_m3110_probe(struct spi_device *spi)
-{
-       struct uart_max3110 *max;
-       void *buffer;
-       u16 res;
-       int ret = 0;
-
-       max = kzalloc(sizeof(*max), GFP_KERNEL);
-       if (!max)
-               return -ENOMEM;
-
-       /* Set spi info */
-       spi->bits_per_word = 16;
-       max->clock = MAX3110_HIGH_CLK;
-
-       spi_setup(spi);
-
-       max->port.type = PORT_MAX3100;
-       max->port.fifosize = 2;         /* Only have 16b buffer */
-       max->port.ops = &serial_m3110_ops;
-       max->port.line = 0;
-       max->port.dev = &spi->dev;
-       max->port.uartclk = 115200;
-
-       max->spi = spi;
-       strcpy(max->name, spi->modalias);
-       max->irq = (u16)spi->irq;
-
-       mutex_init(&max->thread_mutex);
-       mutex_init(&max->io_mutex);
-
-       max->word_7bits = 0;
-       max->parity = 0;
-       max->baud = 0;
-
-       max->cur_conf = 0;
-       max->uart_flags = 0;
-
-       /* Check if reading configuration register returns something sane */
-
-       res = RC_TAG;
-       ret = max3110_write_then_read(max, (u8 *)&res, (u8 *)&res, 2, 0);
-       if (ret < 0 || res == 0 || res == 0xffff) {
-               dev_dbg(&spi->dev, "MAX3111 deemed not present (conf reg %04x)",
-                                                                       res);
-               ret = -ENODEV;
-               goto err_get_page;
-       }
-
-       buffer = (void *)__get_free_page(GFP_KERNEL);
-       if (!buffer) {
-               ret = -ENOMEM;
-               goto err_get_page;
-       }
-       max->con_xmit.buf = buffer;
-       max->con_xmit.head = 0;
-       max->con_xmit.tail = 0;
-
-       init_waitqueue_head(&max->wq);
-
-       max->main_thread = kthread_run(max3110_main_thread,
-                                       max, "max3110_main");
-       if (IS_ERR(max->main_thread)) {
-               ret = PTR_ERR(max->main_thread);
-               goto err_kthread;
-       }
-
-       if (max->irq) {
-               ret = request_irq(max->irq, serial_m3110_irq,
-                               IRQ_TYPE_EDGE_FALLING, "max3110", max);
-               if (ret) {
-                       max->irq = 0;
-                       dev_warn(&spi->dev,
-                       "unable to allocate IRQ, will use polling method\n");
-               }
-       }
-
-       spi_set_drvdata(spi, max);
-       pmax = max;
-
-       /* Give membase a psudo value to pass serial_core's check */
-       max->port.membase = (unsigned char __iomem *)0xff110000;
-       uart_add_one_port(&serial_m3110_reg, &max->port);
-
-       return 0;
-
-err_kthread:
-       free_page((unsigned long)buffer);
-err_get_page:
-       kfree(max);
-       return ret;
-}
-
-static int serial_m3110_remove(struct spi_device *dev)
-{
-       struct uart_max3110 *max = spi_get_drvdata(dev);
-
-       if (!max)
-               return 0;
-
-       uart_remove_one_port(&serial_m3110_reg, &max->port);
-
-       free_page((unsigned long)max->con_xmit.buf);
-
-       if (max->irq)
-               free_irq(max->irq, max);
-
-       if (max->main_thread)
-               kthread_stop(max->main_thread);
-
-       kfree(max);
-       return 0;
-}
-
-static struct spi_driver uart_max3110_driver = {
-       .driver = {
-                       .name   = "spi_max3111",
-                       .owner  = THIS_MODULE,
-                       .pm     = SERIAL_M3110_PM_OPS,
-       },
-       .probe          = serial_m3110_probe,
-       .remove         = serial_m3110_remove,
-};
-
-static int __init serial_m3110_init(void)
-{
-       int ret = 0;
-
-       ret = uart_register_driver(&serial_m3110_reg);
-       if (ret)
-               return ret;
-
-       ret = spi_register_driver(&uart_max3110_driver);
-       if (ret)
-               uart_unregister_driver(&serial_m3110_reg);
-
-       return ret;
-}
-
-static void __exit serial_m3110_exit(void)
-{
-       spi_unregister_driver(&uart_max3110_driver);
-       uart_unregister_driver(&serial_m3110_reg);
-}
-
-module_init(serial_m3110_init);
-module_exit(serial_m3110_exit);
-
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("spi:max3110-uart");
diff --git a/drivers/tty/serial/mrst_max3110.h b/drivers/tty/serial/mrst_max3110.h
deleted file mode 100644 (file)
index 35af073..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-#ifndef _MRST_MAX3110_H
-#define _MRST_MAX3110_H
-
-#define MAX3110_HIGH_CLK       0x1     /* 3.6864 MHZ */
-#define MAX3110_LOW_CLK                0x0     /* 1.8432 MHZ */
-
-/* status bits for all 4 MAX3110 operate modes */
-#define MAX3110_READ_DATA_AVAILABLE    (1 << 15)
-#define MAX3110_WRITE_BUF_EMPTY                (1 << 14)
-#define MAX3110_BREAK                  (1 << 10)
-
-#define WC_TAG                 (3 << 14)
-#define RC_TAG                 (1 << 14)
-#define WD_TAG                 (2 << 14)
-#define RD_TAG                 (0 << 14)
-
-/* bits def for write configuration */
-#define WC_FIFO_ENABLE_MASK    (1 << 13)
-#define WC_FIFO_ENABLE         (0 << 13)
-
-#define WC_SW_SHDI             (1 << 12)
-
-#define WC_IRQ_MASK            (0xF << 8)
-#define WC_TXE_IRQ_ENABLE      (1 << 11)       /* TX empty irq */
-#define WC_RXA_IRQ_ENABLE      (1 << 10)       /* RX available irq */
-#define WC_PAR_HIGH_IRQ_ENABLE (1 << 9)
-#define WC_REC_ACT_IRQ_ENABLE  (1 << 8)
-
-#define WC_IRDA_ENABLE         (1 << 7)
-
-#define WC_STOPBITS_MASK       (1 << 6)
-#define WC_2_STOPBITS          (1 << 6)
-#define WC_1_STOPBITS          (0 << 6)
-
-#define WC_PARITY_ENABLE_MASK  (1 << 5)
-#define WC_PARITY_ENABLE       (1 << 5)
-
-#define WC_WORDLEN_MASK                (1 << 4)
-#define WC_7BIT_WORD           (1 << 4)
-#define WC_8BIT_WORD           (0 << 4)
-
-#define WC_BAUD_DIV_MASK       (0xF)
-#define WC_BAUD_DR1            (0x0)
-#define WC_BAUD_DR2            (0x1)
-#define WC_BAUD_DR4            (0x2)
-#define WC_BAUD_DR8            (0x3)
-#define WC_BAUD_DR16           (0x4)
-#define WC_BAUD_DR32           (0x5)
-#define WC_BAUD_DR64           (0x6)
-#define WC_BAUD_DR128          (0x7)
-#define WC_BAUD_DR3            (0x8)
-#define WC_BAUD_DR6            (0x9)
-#define WC_BAUD_DR12           (0xA)
-#define WC_BAUD_DR24           (0xB)
-#define WC_BAUD_DR48           (0xC)
-#define WC_BAUD_DR96           (0xD)
-#define WC_BAUD_DR192          (0xE)
-#define WC_BAUD_DR384          (0xF)
-
-#define M3110_RX_FIFO_DEPTH    8
-#endif
index c88b522ccd73470967a5fbd03480ce7b4e3b6d62..b73889c8ed4b91242be90295d225aaa93b0adaa2 100644 (file)
@@ -920,14 +920,15 @@ static void msm_console_write(struct console *co, const char *s,
 static int __init msm_console_setup(struct console *co, char *options)
 {
        struct uart_port *port;
-       struct msm_port *msm_port;
-       int baud = 0, flow, bits, parity;
+       int baud = 115200;
+       int bits = 8;
+       int parity = 'n';
+       int flow = 'n';
 
        if (unlikely(co->index >= UART_NR || co->index < 0))
                return -ENXIO;
 
        port = get_port_from_line(co->index);
-       msm_port = UART_TO_MSM(port);
 
        if (unlikely(!port->membase))
                return -ENXIO;
@@ -937,23 +938,6 @@ static int __init msm_console_setup(struct console *co, char *options)
        if (options)
                uart_parse_options(options, &baud, &parity, &bits, &flow);
 
-       bits = 8;
-       parity = 'n';
-       flow = 'n';
-       msm_write(port, UART_MR2_BITS_PER_CHAR_8 | UART_MR2_STOP_BIT_LEN_ONE,
-                 UART_MR2);    /* 8N1 */
-
-       if (baud < 300 || baud > 115200)
-               baud = 115200;
-       msm_set_baud_rate(port, baud);
-
-       msm_reset(port);
-
-       if (msm_port->is_uartdm) {
-               msm_write(port, UART_CR_CMD_PROTECTION_EN, UART_CR);
-               msm_write(port, UART_CR_TX_ENABLE, UART_CR);
-       }
-
        pr_info("msm_serial: console setup on port #%d\n", port->line);
 
        return uart_set_options(port, co, baud, parity, bits, flow);
@@ -1142,9 +1126,6 @@ static int __init msm_serial_init(void)
 
 static void __exit msm_serial_exit(void)
 {
-#ifdef CONFIG_SERIAL_MSM_CONSOLE
-       unregister_console(&msm_console);
-#endif
        platform_driver_unregister(&msm_platform_driver);
        uart_unregister_driver(&msm_uart_driver);
 }
index ec553f8eb2185be6be4ce7fc445ece43ccc386ce..d1298b6cc68e4e6f1f610959f57f94cf350650fd 100644 (file)
@@ -152,8 +152,6 @@ struct mxs_auart_port {
        unsigned int mctrl_prev;
        enum mxs_auart_type devtype;
 
-       unsigned int irq;
-
        struct clk *clk;
        struct device *dev;
 
@@ -1228,37 +1226,32 @@ static int mxs_auart_probe(struct platform_device *pdev)
                        of_match_device(mxs_auart_dt_ids, &pdev->dev);
        struct mxs_auart_port *s;
        u32 version;
-       int ret = 0;
+       int ret, irq;
        struct resource *r;
 
-       s = kzalloc(sizeof(struct mxs_auart_port), GFP_KERNEL);
-       if (!s) {
-               ret = -ENOMEM;
-               goto out;
-       }
+       s = devm_kzalloc(&pdev->dev, sizeof(*s), GFP_KERNEL);
+       if (!s)
+               return -ENOMEM;
 
        ret = serial_mxs_probe_dt(s, pdev);
        if (ret > 0)
                s->port.line = pdev->id < 0 ? 0 : pdev->id;
        else if (ret < 0)
-               goto out_free;
+               return ret;
 
        if (of_id) {
                pdev->id_entry = of_id->data;
                s->devtype = pdev->id_entry->driver_data;
        }
 
-       s->clk = clk_get(&pdev->dev, NULL);
-       if (IS_ERR(s->clk)) {
-               ret = PTR_ERR(s->clk);
-               goto out_free;
-       }
+       s->clk = devm_clk_get(&pdev->dev, NULL);
+       if (IS_ERR(s->clk))
+               return PTR_ERR(s->clk);
 
        r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!r) {
-               ret = -ENXIO;
-               goto out_free_clk;
-       }
+       if (!r)
+               return -ENXIO;
+
 
        s->port.mapbase = r->start;
        s->port.membase = ioremap(r->start, resource_size(r));
@@ -1271,11 +1264,15 @@ static int mxs_auart_probe(struct platform_device *pdev)
 
        s->mctrl_prev = 0;
 
-       s->irq = platform_get_irq(pdev, 0);
-       s->port.irq = s->irq;
-       ret = request_irq(s->irq, mxs_auart_irq_handle, 0, dev_name(&pdev->dev), s);
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0)
+               return irq;
+
+       s->port.irq = irq;
+       ret = devm_request_irq(&pdev->dev, irq, mxs_auart_irq_handle, 0,
+                              dev_name(&pdev->dev), s);
        if (ret)
-               goto out_free_clk;
+               return ret;
 
        platform_set_drvdata(pdev, s);
 
@@ -1288,7 +1285,7 @@ static int mxs_auart_probe(struct platform_device *pdev)
         */
        ret = mxs_auart_request_gpio_irq(s);
        if (ret)
-               goto out_free_irq;
+               return ret;
 
        auart_port[s->port.line] = s;
 
@@ -1307,14 +1304,7 @@ static int mxs_auart_probe(struct platform_device *pdev)
 
 out_free_gpio_irq:
        mxs_auart_free_gpio_irq(s);
-out_free_irq:
        auart_port[pdev->id] = NULL;
-       free_irq(s->irq, s);
-out_free_clk:
-       clk_put(s->clk);
-out_free:
-       kfree(s);
-out:
        return ret;
 }
 
@@ -1323,13 +1313,8 @@ static int mxs_auart_remove(struct platform_device *pdev)
        struct mxs_auart_port *s = platform_get_drvdata(pdev);
 
        uart_remove_one_port(&auart_driver, &s->port);
-
        auart_port[pdev->id] = NULL;
-
        mxs_auart_free_gpio_irq(s);
-       clk_put(s->clk);
-       free_irq(s->irq, s);
-       kfree(s);
 
        return 0;
 }
index 64f1bab7e9d75e7bcaeda1ff40bea99d7a928d4e..7ff61e24a195cbca54d60aaefd65bbdf3d0af187 100644 (file)
@@ -102,6 +102,11 @@ static int of_platform_serial_setup(struct platform_device *ofdev,
        if (of_property_read_u32(np, "fifo-size", &prop) == 0)
                port->fifosize = prop;
 
+       /* Check for a fixed line number */
+       ret = of_alias_get_id(np, "serial");
+       if (ret >= 0)
+               port->line = ret;
+
        port->irq = irq_of_parse_and_map(np, 0);
        port->iotype = UPIO_MEM;
        if (of_property_read_u32(np, "reg-io-width", &prop) == 0) {
@@ -128,6 +133,10 @@ static int of_platform_serial_setup(struct platform_device *ofdev,
        if (of_find_property(np, "no-loopback-test", NULL))
                port->flags |= UPF_SKIP_TEST;
 
+       ret = of_alias_get_id(np, "serial");
+       if (ret >= 0)
+               port->line = ret;
+
        port->dev = &ofdev->dev;
 
        switch (type) {
@@ -331,6 +340,10 @@ static struct of_device_id of_platform_serial_table[] = {
                .data = (void *)PORT_ALTR_16550_F64, },
        { .compatible = "altr,16550-FIFO128",
                .data = (void *)PORT_ALTR_16550_F128, },
+       { .compatible = "mrvl,mmp-uart",
+               .data = (void *)PORT_XSCALE, },
+       { .compatible = "mrvl,pxa-uart",
+               .data = (void *)PORT_XSCALE, },
 #ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL
        { .compatible = "ibm,qpace-nwp-serial",
                .data = (void *)PORT_NWPSERIAL, },
index 2e1073da6719b8877a1d07e488ffc933200797d5..10256fa04b40abf0bd07f6ed5b8d5d721df1eb3f 100644 (file)
@@ -63,7 +63,7 @@
 #define UART_ERRATA_i202_MDR1_ACCESS   BIT(0)
 #define UART_ERRATA_i291_DMA_FORCEIDLE BIT(1)
 
-#define DEFAULT_CLK_SPEED 48000000 /* 48Mhz*/
+#define DEFAULT_CLK_SPEED 48000000 /* 48Mhz */
 
 /* SCR register bitmasks */
 #define OMAP_UART_SCR_RX_TRIG_GRANU1_MASK              (1 << 7)
@@ -93,7 +93,7 @@
 /* WER = 0x7F
  * Enable module level wakeup in WER reg
  */
-#define OMAP_UART_WER_MOD_WKUP 0X7F
+#define OMAP_UART_WER_MOD_WKUP 0x7F
 
 /* Enable XON/XOFF flow control on output */
 #define OMAP_UART_SW_TX                0x08
@@ -114,7 +114,7 @@ struct uart_omap_dma {
        dma_addr_t              tx_buf_dma_phys;
        unsigned int            uart_base;
        /*
-        * Buffer for rx dma.It is not required for tx because the buffer
+        * Buffer for rx dma. It is not required for tx because the buffer
         * comes from port structure.
         */
        unsigned char           *rx_buf;
@@ -151,7 +151,7 @@ struct uart_omap_port {
        int                     use_dma;
        /*
         * Some bits in registers are cleared on a read, so they must
-        * be saved whenever the register is read but the bits will not
+        * be saved whenever the register is read, but the bits will not
         * be immediately processed.
         */
        unsigned int            lsr_break_flag;
@@ -681,7 +681,7 @@ static unsigned int serial_omap_get_mctrl(struct uart_port *port)
 static void serial_omap_set_mctrl(struct uart_port *port, unsigned int mctrl)
 {
        struct uart_omap_port *up = to_uart_omap_port(port);
-       unsigned char mcr = 0, old_mcr;
+       unsigned char mcr = 0, old_mcr, lcr;
 
        dev_dbg(up->port.dev, "serial_omap_set_mctrl+%d\n", up->port.line);
        if (mctrl & TIOCM_RTS)
@@ -701,6 +701,17 @@ static void serial_omap_set_mctrl(struct uart_port *port, unsigned int mctrl)
                     UART_MCR_DTR | UART_MCR_RTS);
        up->mcr = old_mcr | mcr;
        serial_out(up, UART_MCR, up->mcr);
+
+       /* Turn off autoRTS if RTS is lowered; restore autoRTS if RTS raised */
+       lcr = serial_in(up, UART_LCR);
+       serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
+       if ((mctrl & TIOCM_RTS) && (port->status & UPSTAT_AUTORTS))
+               up->efr |= UART_EFR_RTS;
+       else
+               up->efr &= UART_EFR_RTS;
+       serial_out(up, UART_EFR, up->efr);
+       serial_out(up, UART_LCR, lcr);
+
        pm_runtime_mark_last_busy(up->dev);
        pm_runtime_put_autosuspend(up->dev);
 }
@@ -756,8 +767,6 @@ static int serial_omap_startup(struct uart_port *port)
         * (they will be reenabled in set_termios())
         */
        serial_omap_clear_fifos(up);
-       /* For Hardware flow control */
-       serial_out(up, UART_MCR, UART_MCR_RTS);
 
        /*
         * Clear the interrupt registers.
@@ -1053,12 +1062,12 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios,
 
        serial_out(up, UART_TI752_TCR, OMAP_UART_TCR_TRIG);
 
-       if (termios->c_cflag & CRTSCTS && up->port.flags & UPF_HARD_FLOW) {
-               /* Enable AUTORTS and AUTOCTS */
-               up->efr |= UART_EFR_CTS | UART_EFR_RTS;
+       up->port.status &= ~(UPSTAT_AUTOCTS | UPSTAT_AUTORTS | UPSTAT_AUTOXOFF);
 
-               /* Ensure MCR RTS is asserted */
-               up->mcr |= UART_MCR_RTS;
+       if (termios->c_cflag & CRTSCTS && up->port.flags & UPF_HARD_FLOW) {
+               /* Enable AUTOCTS (autoRTS is enabled when RTS is raised) */
+               up->port.status |= UPSTAT_AUTOCTS | UPSTAT_AUTORTS;
+               up->efr |= UART_EFR_CTS;
        } else {
                /* Disable AUTORTS and AUTOCTS */
                up->efr &= ~(UART_EFR_CTS | UART_EFR_RTS);
@@ -1081,8 +1090,10 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios,
                 * Enable XON/XOFF flow control on output.
                 * Transmit XON1, XOFF1
                 */
-               if (termios->c_iflag & IXOFF)
+               if (termios->c_iflag & IXOFF) {
+                       up->port.status |= UPSTAT_AUTOXOFF;
                        up->efr |= OMAP_UART_SW_TX;
+               }
 
                /*
                 * IXANY Flag:
index 107e807225752623c7f8cae56b17f00d4d003d37..af821a9087204ec654f637a617513ba06d68a0ef 100644 (file)
@@ -28,6 +28,9 @@
 #define SUPPORT_SYSRQ
 #endif
 
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
 #include <linux/module.h>
 #include <linux/ioport.h>
 #include <linux/io.h>
@@ -78,6 +81,10 @@ static void dbg(const char *fmt, ...)
 #define S3C24XX_SERIAL_MAJOR   204
 #define S3C24XX_SERIAL_MINOR   64
 
+#define S3C24XX_TX_PIO                 1
+#define S3C24XX_TX_DMA                 2
+#define S3C24XX_RX_PIO                 1
+#define S3C24XX_RX_DMA                 2
 /* macros to change one thing to another */
 
 #define tx_enabled(port) ((port)->unused[0])
@@ -154,39 +161,272 @@ static void s3c24xx_serial_rx_disable(struct uart_port *port)
 static void s3c24xx_serial_stop_tx(struct uart_port *port)
 {
        struct s3c24xx_uart_port *ourport = to_ourport(port);
+       struct s3c24xx_uart_dma *dma = ourport->dma;
+       struct circ_buf *xmit = &port->state->xmit;
+       struct dma_tx_state state;
+       int count;
 
-       if (tx_enabled(port)) {
-               if (s3c24xx_serial_has_interrupt_mask(port))
-                       __set_bit(S3C64XX_UINTM_TXD,
-                               portaddrl(port, S3C64XX_UINTM));
-               else
-                       disable_irq_nosync(ourport->tx_irq);
-               tx_enabled(port) = 0;
-               if (port->flags & UPF_CONS_FLOW)
-                       s3c24xx_serial_rx_enable(port);
+       if (!tx_enabled(port))
+               return;
+
+       if (s3c24xx_serial_has_interrupt_mask(port))
+               __set_bit(S3C64XX_UINTM_TXD,
+                       portaddrl(port, S3C64XX_UINTM));
+       else
+               disable_irq_nosync(ourport->tx_irq);
+
+       if (dma && dma->tx_chan && ourport->tx_in_progress == S3C24XX_TX_DMA) {
+               dmaengine_pause(dma->tx_chan);
+               dmaengine_tx_status(dma->tx_chan, dma->tx_cookie, &state);
+               dmaengine_terminate_all(dma->tx_chan);
+               dma_sync_single_for_cpu(ourport->port.dev,
+                       dma->tx_transfer_addr, dma->tx_size, DMA_TO_DEVICE);
+               async_tx_ack(dma->tx_desc);
+               count = dma->tx_bytes_requested - state.residue;
+               xmit->tail = (xmit->tail + count) & (UART_XMIT_SIZE - 1);
+               port->icount.tx += count;
        }
+
+       tx_enabled(port) = 0;
+       ourport->tx_in_progress = 0;
+
+       if (port->flags & UPF_CONS_FLOW)
+               s3c24xx_serial_rx_enable(port);
+
+       ourport->tx_mode = 0;
 }
 
-static void s3c24xx_serial_start_tx(struct uart_port *port)
+static void s3c24xx_serial_start_next_tx(struct s3c24xx_uart_port *ourport);
+
+static void s3c24xx_serial_tx_dma_complete(void *args)
+{
+       struct s3c24xx_uart_port *ourport = args;
+       struct uart_port *port = &ourport->port;
+       struct circ_buf *xmit = &port->state->xmit;
+       struct s3c24xx_uart_dma *dma = ourport->dma;
+       struct dma_tx_state state;
+       unsigned long flags;
+       int count;
+
+
+       dmaengine_tx_status(dma->tx_chan, dma->tx_cookie, &state);
+       count = dma->tx_bytes_requested - state.residue;
+       async_tx_ack(dma->tx_desc);
+
+       dma_sync_single_for_cpu(ourport->port.dev, dma->tx_transfer_addr,
+                               dma->tx_size, DMA_TO_DEVICE);
+
+       spin_lock_irqsave(&port->lock, flags);
+
+       xmit->tail = (xmit->tail + count) & (UART_XMIT_SIZE - 1);
+       port->icount.tx += count;
+       ourport->tx_in_progress = 0;
+
+       if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+               uart_write_wakeup(port);
+
+       s3c24xx_serial_start_next_tx(ourport);
+       spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void enable_tx_dma(struct s3c24xx_uart_port *ourport)
+{
+       struct uart_port *port = &ourport->port;
+       u32 ucon;
+
+       /* Mask Tx interrupt */
+       if (s3c24xx_serial_has_interrupt_mask(port))
+               __set_bit(S3C64XX_UINTM_TXD,
+                         portaddrl(port, S3C64XX_UINTM));
+       else
+               disable_irq_nosync(ourport->tx_irq);
+
+       /* Enable tx dma mode */
+       ucon = rd_regl(port, S3C2410_UCON);
+       ucon &= ~(S3C64XX_UCON_TXBURST_MASK | S3C64XX_UCON_TXMODE_MASK);
+       ucon |= (dma_get_cache_alignment() >= 16) ?
+               S3C64XX_UCON_TXBURST_16 : S3C64XX_UCON_TXBURST_1;
+       ucon |= S3C64XX_UCON_TXMODE_DMA;
+       wr_regl(port,  S3C2410_UCON, ucon);
+
+       ourport->tx_mode = S3C24XX_TX_DMA;
+}
+
+static void enable_tx_pio(struct s3c24xx_uart_port *ourport)
+{
+       struct uart_port *port = &ourport->port;
+       u32 ucon, ufcon;
+
+       /* Set ufcon txtrig */
+       ourport->tx_in_progress = S3C24XX_TX_PIO;
+       ufcon = rd_regl(port, S3C2410_UFCON);
+       wr_regl(port,  S3C2410_UFCON, ufcon);
+
+       /* Enable tx pio mode */
+       ucon = rd_regl(port, S3C2410_UCON);
+       ucon &= ~(S3C64XX_UCON_TXMODE_MASK);
+       ucon |= S3C64XX_UCON_TXMODE_CPU;
+       wr_regl(port,  S3C2410_UCON, ucon);
+
+       /* Unmask Tx interrupt */
+       if (s3c24xx_serial_has_interrupt_mask(port))
+               __clear_bit(S3C64XX_UINTM_TXD,
+                           portaddrl(port, S3C64XX_UINTM));
+       else
+               enable_irq(ourport->tx_irq);
+
+       ourport->tx_mode = S3C24XX_TX_PIO;
+}
+
+static void s3c24xx_serial_start_tx_pio(struct s3c24xx_uart_port *ourport)
+{
+       if (ourport->tx_mode != S3C24XX_TX_PIO)
+               enable_tx_pio(ourport);
+}
+
+static int s3c24xx_serial_start_tx_dma(struct s3c24xx_uart_port *ourport,
+                                     unsigned int count)
+{
+       struct uart_port *port = &ourport->port;
+       struct circ_buf *xmit = &port->state->xmit;
+       struct s3c24xx_uart_dma *dma = ourport->dma;
+
+
+       if (ourport->tx_mode != S3C24XX_TX_DMA)
+               enable_tx_dma(ourport);
+
+       while (xmit->tail & (dma_get_cache_alignment() - 1)) {
+               if (rd_regl(port, S3C2410_UFSTAT) & ourport->info->tx_fifofull)
+                       return 0;
+               wr_regb(port, S3C2410_UTXH, xmit->buf[xmit->tail]);
+               xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+               port->icount.tx++;
+               count--;
+       }
+
+       dma->tx_size = count & ~(dma_get_cache_alignment() - 1);
+       dma->tx_transfer_addr = dma->tx_addr + xmit->tail;
+
+       dma_sync_single_for_device(ourport->port.dev, dma->tx_transfer_addr,
+                               dma->tx_size, DMA_TO_DEVICE);
+
+       dma->tx_desc = dmaengine_prep_slave_single(dma->tx_chan,
+                               dma->tx_transfer_addr, dma->tx_size,
+                               DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT);
+       if (!dma->tx_desc) {
+               dev_err(ourport->port.dev, "Unable to get desc for Tx\n");
+               return -EIO;
+       }
+
+       dma->tx_desc->callback = s3c24xx_serial_tx_dma_complete;
+       dma->tx_desc->callback_param = ourport;
+       dma->tx_bytes_requested = dma->tx_size;
+
+       ourport->tx_in_progress = S3C24XX_TX_DMA;
+       dma->tx_cookie = dmaengine_submit(dma->tx_desc);
+       dma_async_issue_pending(dma->tx_chan);
+       return 0;
+}
+
+static void s3c24xx_serial_start_next_tx(struct s3c24xx_uart_port *ourport)
+{
+       struct uart_port *port = &ourport->port;
+       struct circ_buf *xmit = &port->state->xmit;
+       unsigned long count;
+
+       /* Get data size up to the end of buffer */
+       count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
+
+       if (!count) {
+               s3c24xx_serial_stop_tx(port);
+               return;
+       }
+
+       if (!ourport->dma || !ourport->dma->tx_chan || count < port->fifosize)
+               s3c24xx_serial_start_tx_pio(ourport);
+       else
+               s3c24xx_serial_start_tx_dma(ourport, count);
+}
+
+void s3c24xx_serial_start_tx(struct uart_port *port)
 {
        struct s3c24xx_uart_port *ourport = to_ourport(port);
+       struct circ_buf *xmit = &port->state->xmit;
 
        if (!tx_enabled(port)) {
                if (port->flags & UPF_CONS_FLOW)
                        s3c24xx_serial_rx_disable(port);
 
-               if (s3c24xx_serial_has_interrupt_mask(port))
-                       __clear_bit(S3C64XX_UINTM_TXD,
-                               portaddrl(port, S3C64XX_UINTM));
-               else
-                       enable_irq(ourport->tx_irq);
                tx_enabled(port) = 1;
+               if (!ourport->dma || !ourport->dma->tx_chan)
+                       s3c24xx_serial_start_tx_pio(ourport);
+       }
+
+       if (ourport->dma && ourport->dma->tx_chan) {
+               if (!uart_circ_empty(xmit) && !ourport->tx_in_progress)
+                       s3c24xx_serial_start_next_tx(ourport);
+       }
+}
+
+static void s3c24xx_uart_copy_rx_to_tty(struct s3c24xx_uart_port *ourport,
+               struct tty_port *tty, int count)
+{
+       struct s3c24xx_uart_dma *dma = ourport->dma;
+       int copied;
+
+       if (!count)
+               return;
+
+       dma_sync_single_for_cpu(ourport->port.dev, dma->rx_addr,
+                               dma->rx_size, DMA_FROM_DEVICE);
+
+       ourport->port.icount.rx += count;
+       if (!tty) {
+               dev_err(ourport->port.dev, "No tty port\n");
+               return;
+       }
+       copied = tty_insert_flip_string(tty,
+                       ((unsigned char *)(ourport->dma->rx_buf)), count);
+       if (copied != count) {
+               WARN_ON(1);
+               dev_err(ourport->port.dev, "RxData copy to tty layer failed\n");
+       }
+}
+
+static int s3c24xx_serial_rx_fifocnt(struct s3c24xx_uart_port *ourport,
+                                    unsigned long ufstat);
+
+static void uart_rx_drain_fifo(struct s3c24xx_uart_port *ourport)
+{
+       struct uart_port *port = &ourport->port;
+       struct tty_port *tty = &port->state->port;
+       unsigned int ch, ufstat;
+       unsigned int count;
+
+       ufstat = rd_regl(port, S3C2410_UFSTAT);
+       count = s3c24xx_serial_rx_fifocnt(ourport, ufstat);
+
+       if (!count)
+               return;
+
+       while (count-- > 0) {
+               ch = rd_regb(port, S3C2410_URXH);
+
+               ourport->port.icount.rx++;
+               tty_insert_flip_char(tty, ch, TTY_NORMAL);
        }
+
+       tty_flip_buffer_push(tty);
 }
 
 static void s3c24xx_serial_stop_rx(struct uart_port *port)
 {
        struct s3c24xx_uart_port *ourport = to_ourport(port);
+       struct s3c24xx_uart_dma *dma = ourport->dma;
+       struct tty_port *t = &port->state->port;
+       struct dma_tx_state state;
+       enum dma_status dma_status;
+       unsigned int received;
 
        if (rx_enabled(port)) {
                dbg("s3c24xx_serial_stop_rx: port=%p\n", port);
@@ -197,6 +437,17 @@ static void s3c24xx_serial_stop_rx(struct uart_port *port)
                        disable_irq_nosync(ourport->rx_irq);
                rx_enabled(port) = 0;
        }
+       if (dma && dma->rx_chan) {
+               dmaengine_pause(dma->tx_chan);
+               dma_status = dmaengine_tx_status(dma->rx_chan,
+                               dma->rx_cookie, &state);
+               if (dma_status == DMA_IN_PROGRESS ||
+                       dma_status == DMA_PAUSED) {
+                       received = dma->rx_bytes_requested - state.residue;
+                       dmaengine_terminate_all(dma->rx_chan);
+                       s3c24xx_uart_copy_rx_to_tty(ourport, t, received);
+               }
+       }
 }
 
 static inline struct s3c24xx_uart_info
@@ -228,12 +479,157 @@ static int s3c24xx_serial_rx_fifocnt(struct s3c24xx_uart_port *ourport,
        return (ufstat & info->rx_fifomask) >> info->rx_fifoshift;
 }
 
+static void s3c64xx_start_rx_dma(struct s3c24xx_uart_port *ourport);
+static void s3c24xx_serial_rx_dma_complete(void *args)
+{
+       struct s3c24xx_uart_port *ourport = args;
+       struct uart_port *port = &ourport->port;
+
+       struct s3c24xx_uart_dma *dma = ourport->dma;
+       struct tty_port *t = &port->state->port;
+       struct tty_struct *tty = tty_port_tty_get(&ourport->port.state->port);
+
+       struct dma_tx_state state;
+       unsigned long flags;
+       int received;
+
+       dmaengine_tx_status(dma->rx_chan,  dma->rx_cookie, &state);
+       received  = dma->rx_bytes_requested - state.residue;
+       async_tx_ack(dma->rx_desc);
+
+       spin_lock_irqsave(&port->lock, flags);
+
+       if (received)
+               s3c24xx_uart_copy_rx_to_tty(ourport, t, received);
+
+       if (tty) {
+               tty_flip_buffer_push(t);
+               tty_kref_put(tty);
+       }
+
+       s3c64xx_start_rx_dma(ourport);
+
+       spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void s3c64xx_start_rx_dma(struct s3c24xx_uart_port *ourport)
+{
+       struct s3c24xx_uart_dma *dma = ourport->dma;
+
+       dma_sync_single_for_device(ourport->port.dev, dma->rx_addr,
+                               dma->rx_size, DMA_FROM_DEVICE);
+
+       dma->rx_desc = dmaengine_prep_slave_single(dma->rx_chan,
+                               dma->rx_addr, dma->rx_size, DMA_DEV_TO_MEM,
+                               DMA_PREP_INTERRUPT);
+       if (!dma->rx_desc) {
+               dev_err(ourport->port.dev, "Unable to get desc for Rx\n");
+               return;
+       }
+
+       dma->rx_desc->callback = s3c24xx_serial_rx_dma_complete;
+       dma->rx_desc->callback_param = ourport;
+       dma->rx_bytes_requested = dma->rx_size;
+
+       dma->rx_cookie = dmaengine_submit(dma->rx_desc);
+       dma_async_issue_pending(dma->rx_chan);
+}
 
 /* ? - where has parity gone?? */
 #define S3C2410_UERSTAT_PARITY (0x1000)
 
-static irqreturn_t
-s3c24xx_serial_rx_chars(int irq, void *dev_id)
+static void enable_rx_dma(struct s3c24xx_uart_port *ourport)
+{
+       struct uart_port *port = &ourport->port;
+       unsigned int ucon;
+
+       /* set Rx mode to DMA mode */
+       ucon = rd_regl(port, S3C2410_UCON);
+       ucon &= ~(S3C64XX_UCON_RXBURST_MASK |
+                       S3C64XX_UCON_TIMEOUT_MASK |
+                       S3C64XX_UCON_EMPTYINT_EN |
+                       S3C64XX_UCON_DMASUS_EN |
+                       S3C64XX_UCON_TIMEOUT_EN |
+                       S3C64XX_UCON_RXMODE_MASK);
+       ucon |= S3C64XX_UCON_RXBURST_16 |
+                       0xf << S3C64XX_UCON_TIMEOUT_SHIFT |
+                       S3C64XX_UCON_EMPTYINT_EN |
+                       S3C64XX_UCON_TIMEOUT_EN |
+                       S3C64XX_UCON_RXMODE_DMA;
+       wr_regl(port, S3C2410_UCON, ucon);
+
+       ourport->rx_mode = S3C24XX_RX_DMA;
+}
+
+static void enable_rx_pio(struct s3c24xx_uart_port *ourport)
+{
+       struct uart_port *port = &ourport->port;
+       unsigned int ucon;
+
+       /* set Rx mode to DMA mode */
+       ucon = rd_regl(port, S3C2410_UCON);
+       ucon &= ~(S3C64XX_UCON_TIMEOUT_MASK |
+                       S3C64XX_UCON_EMPTYINT_EN |
+                       S3C64XX_UCON_DMASUS_EN |
+                       S3C64XX_UCON_TIMEOUT_EN |
+                       S3C64XX_UCON_RXMODE_MASK);
+       ucon |= 0xf << S3C64XX_UCON_TIMEOUT_SHIFT |
+                       S3C64XX_UCON_TIMEOUT_EN |
+                       S3C64XX_UCON_RXMODE_CPU;
+       wr_regl(port, S3C2410_UCON, ucon);
+
+       ourport->rx_mode = S3C24XX_RX_PIO;
+}
+
+static irqreturn_t s3c24xx_serial_rx_chars_dma(int irq, void *dev_id)
+{
+       unsigned int utrstat, ufstat, received;
+       struct s3c24xx_uart_port *ourport = dev_id;
+       struct uart_port *port = &ourport->port;
+       struct s3c24xx_uart_dma *dma = ourport->dma;
+       struct tty_struct *tty = tty_port_tty_get(&ourport->port.state->port);
+       struct tty_port *t = &port->state->port;
+       unsigned long flags;
+       struct dma_tx_state state;
+
+       utrstat = rd_regl(port, S3C2410_UTRSTAT);
+       ufstat = rd_regl(port, S3C2410_UFSTAT);
+
+       spin_lock_irqsave(&port->lock, flags);
+
+       if (!(utrstat & S3C2410_UTRSTAT_TIMEOUT)) {
+               s3c64xx_start_rx_dma(ourport);
+               if (ourport->rx_mode == S3C24XX_RX_PIO)
+                       enable_rx_dma(ourport);
+               goto finish;
+       }
+
+       if (ourport->rx_mode == S3C24XX_RX_DMA) {
+               dmaengine_pause(dma->rx_chan);
+               dmaengine_tx_status(dma->rx_chan, dma->rx_cookie, &state);
+               dmaengine_terminate_all(dma->rx_chan);
+               received = dma->rx_bytes_requested - state.residue;
+               s3c24xx_uart_copy_rx_to_tty(ourport, t, received);
+
+               enable_rx_pio(ourport);
+       }
+
+       uart_rx_drain_fifo(ourport);
+
+       if (tty) {
+               tty_flip_buffer_push(t);
+               tty_kref_put(tty);
+       }
+
+       wr_regl(port, S3C2410_UTRSTAT, S3C2410_UTRSTAT_TIMEOUT);
+
+finish:
+       spin_unlock_irqrestore(&port->lock, flags);
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t s3c24xx_serial_rx_chars_pio(int irq, void *dev_id)
 {
        struct s3c24xx_uart_port *ourport = dev_id;
        struct uart_port *port = &ourport->port;
@@ -324,16 +720,33 @@ out:
        return IRQ_HANDLED;
 }
 
+
+static irqreturn_t s3c24xx_serial_rx_chars(int irq, void *dev_id)
+{
+       struct s3c24xx_uart_port *ourport = dev_id;
+
+       if (ourport->dma && ourport->dma->rx_chan)
+               return s3c24xx_serial_rx_chars_dma(irq, dev_id);
+       return s3c24xx_serial_rx_chars_pio(irq, dev_id);
+}
+
 static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id)
 {
        struct s3c24xx_uart_port *ourport = id;
        struct uart_port *port = &ourport->port;
        struct circ_buf *xmit = &port->state->xmit;
        unsigned long flags;
-       int count = port->fifosize;
+       int count;
 
        spin_lock_irqsave(&port->lock, flags);
 
+       count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
+
+       if (ourport->dma && ourport->dma->tx_chan && count >= port->fifosize) {
+               s3c24xx_serial_start_tx_dma(ourport, count);
+               goto out;
+       }
+
        if (port->x_char) {
                wr_regb(port, S3C2410_UTXH, port->x_char);
                port->icount.tx++;
@@ -352,6 +765,7 @@ static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id)
 
        /* try and drain the buffer... */
 
+       count = port->fifosize;
        while (!uart_circ_empty(xmit) && count-- > 0) {
                if (rd_regl(port, S3C2410_UFSTAT) & ourport->info->tx_fifofull)
                        break;
@@ -453,6 +867,93 @@ static void s3c24xx_serial_break_ctl(struct uart_port *port, int break_state)
        spin_unlock_irqrestore(&port->lock, flags);
 }
 
+static int s3c24xx_serial_request_dma(struct s3c24xx_uart_port *p)
+{
+       struct s3c24xx_uart_dma *dma = p->dma;
+       dma_cap_mask_t mask;
+       unsigned long flags;
+
+       /* Default slave configuration parameters */
+       dma->rx_conf.direction          = DMA_DEV_TO_MEM;
+       dma->rx_conf.src_addr_width     = DMA_SLAVE_BUSWIDTH_1_BYTE;
+       dma->rx_conf.src_addr           = p->port.mapbase + S3C2410_URXH;
+       dma->rx_conf.src_maxburst       = 16;
+
+       dma->tx_conf.direction          = DMA_MEM_TO_DEV;
+       dma->tx_conf.dst_addr_width     = DMA_SLAVE_BUSWIDTH_1_BYTE;
+       dma->tx_conf.dst_addr           = p->port.mapbase + S3C2410_UTXH;
+       if (dma_get_cache_alignment() >= 16)
+               dma->tx_conf.dst_maxburst = 16;
+       else
+               dma->tx_conf.dst_maxburst = 1;
+
+       dma_cap_zero(mask);
+       dma_cap_set(DMA_SLAVE, mask);
+
+       dma->rx_chan = dma_request_slave_channel_compat(mask, dma->fn,
+                                       dma->rx_param, p->port.dev, "rx");
+       if (!dma->rx_chan)
+               return -ENODEV;
+
+       dmaengine_slave_config(dma->rx_chan, &dma->rx_conf);
+
+       dma->tx_chan = dma_request_slave_channel_compat(mask, dma->fn,
+                                       dma->tx_param, p->port.dev, "tx");
+       if (!dma->tx_chan) {
+               dma_release_channel(dma->rx_chan);
+               return -ENODEV;
+       }
+
+       dmaengine_slave_config(dma->tx_chan, &dma->tx_conf);
+
+       /* RX buffer */
+       dma->rx_size = PAGE_SIZE;
+
+       dma->rx_buf = kmalloc(dma->rx_size, GFP_KERNEL);
+
+       if (!dma->rx_buf) {
+               dma_release_channel(dma->rx_chan);
+               dma_release_channel(dma->tx_chan);
+               return -ENOMEM;
+       }
+
+       dma->rx_addr = dma_map_single(dma->rx_chan->device->dev, dma->rx_buf,
+                               dma->rx_size, DMA_FROM_DEVICE);
+
+       spin_lock_irqsave(&p->port.lock, flags);
+
+       /* TX buffer */
+       dma->tx_addr = dma_map_single(dma->tx_chan->device->dev,
+                               p->port.state->xmit.buf,
+                               UART_XMIT_SIZE, DMA_TO_DEVICE);
+
+       spin_unlock_irqrestore(&p->port.lock, flags);
+
+       return 0;
+}
+
+static void s3c24xx_serial_release_dma(struct s3c24xx_uart_port *p)
+{
+       struct s3c24xx_uart_dma *dma = p->dma;
+
+       if (dma->rx_chan) {
+               dmaengine_terminate_all(dma->rx_chan);
+               dma_unmap_single(dma->rx_chan->device->dev, dma->rx_addr,
+                               dma->rx_size, DMA_FROM_DEVICE);
+               kfree(dma->rx_buf);
+               dma_release_channel(dma->rx_chan);
+               dma->rx_chan = NULL;
+       }
+
+       if (dma->tx_chan) {
+               dmaengine_terminate_all(dma->tx_chan);
+               dma_unmap_single(dma->tx_chan->device->dev, dma->tx_addr,
+                               UART_XMIT_SIZE, DMA_TO_DEVICE);
+               dma_release_channel(dma->tx_chan);
+               dma->tx_chan = NULL;
+       }
+}
+
 static void s3c24xx_serial_shutdown(struct uart_port *port)
 {
        struct s3c24xx_uart_port *ourport = to_ourport(port);
@@ -478,6 +979,11 @@ static void s3c24xx_serial_shutdown(struct uart_port *port)
                wr_regl(port, S3C64XX_UINTP, 0xf);
                wr_regl(port, S3C64XX_UINTM, 0xf);
        }
+
+       if (ourport->dma)
+               s3c24xx_serial_release_dma(ourport);
+
+       ourport->tx_in_progress = 0;
 }
 
 static int s3c24xx_serial_startup(struct uart_port *port)
@@ -529,12 +1035,21 @@ err:
 static int s3c64xx_serial_startup(struct uart_port *port)
 {
        struct s3c24xx_uart_port *ourport = to_ourport(port);
+       unsigned long flags;
+       unsigned int ufcon;
        int ret;
 
        dbg("s3c64xx_serial_startup: port=%p (%08llx,%p)\n",
            port, (unsigned long long)port->mapbase, port->membase);
 
        wr_regl(port, S3C64XX_UINTM, 0xf);
+       if (ourport->dma) {
+               ret = s3c24xx_serial_request_dma(ourport);
+               if (ret < 0) {
+                       dev_warn(port->dev, "DMA request failed\n");
+                       return ret;
+               }
+       }
 
        ret = request_irq(port->irq, s3c64xx_serial_handle_irq, IRQF_SHARED,
                          s3c24xx_serial_portname(port), ourport);
@@ -549,8 +1064,20 @@ static int s3c64xx_serial_startup(struct uart_port *port)
        tx_enabled(port) = 0;
        ourport->tx_claimed = 1;
 
+       spin_lock_irqsave(&port->lock, flags);
+
+       ufcon = rd_regl(port, S3C2410_UFCON);
+       ufcon |= S3C2410_UFCON_RESETRX | S3C2410_UFCON_RESETTX |
+                       S5PV210_UFCON_RXTRIG8;
+       wr_regl(port, S3C2410_UFCON, ufcon);
+
+       enable_rx_pio(ourport);
+
+       spin_unlock_irqrestore(&port->lock, flags);
+
        /* Enable Rx Interrupt */
        __clear_bit(S3C64XX_UINTM_RXD, portaddrl(port, S3C64XX_UINTM));
+
        dbg("s3c64xx_serial_startup ok\n");
        return ret;
 }
@@ -1209,6 +1736,18 @@ static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport,
        ret = platform_get_irq(platdev, 1);
        if (ret > 0)
                ourport->tx_irq = ret;
+       /*
+        * DMA is currently supported only on DT platforms, if DMA properties
+        * are specified.
+        */
+       if (platdev->dev.of_node && of_find_property(platdev->dev.of_node,
+                                                    "dmas", NULL)) {
+               ourport->dma = devm_kzalloc(port->dev,
+                                           sizeof(*ourport->dma),
+                                           GFP_KERNEL);
+               if (!ourport->dma)
+                       return -ENOMEM;
+       }
 
        ourport->clk    = clk_get(&platdev->dev, "uart");
        if (IS_ERR(ourport->clk)) {
@@ -1857,6 +2396,111 @@ static struct platform_driver samsung_serial_driver = {
 
 module_platform_driver(samsung_serial_driver);
 
+#ifdef CONFIG_SERIAL_SAMSUNG_CONSOLE
+/*
+ * Early console.
+ */
+
+struct samsung_early_console_data {
+       u32 txfull_mask;
+};
+
+static void samsung_early_busyuart(struct uart_port *port)
+{
+       while (!(readl(port->membase + S3C2410_UTRSTAT) & S3C2410_UTRSTAT_TXFE))
+               ;
+}
+
+static void samsung_early_busyuart_fifo(struct uart_port *port)
+{
+       struct samsung_early_console_data *data = port->private_data;
+
+       while (readl(port->membase + S3C2410_UFSTAT) & data->txfull_mask)
+               ;
+}
+
+static void samsung_early_putc(struct uart_port *port, int c)
+{
+       if (readl(port->membase + S3C2410_UFCON) & S3C2410_UFCON_FIFOMODE)
+               samsung_early_busyuart_fifo(port);
+       else
+               samsung_early_busyuart(port);
+
+       writeb(c, port->membase + S3C2410_UTXH);
+}
+
+static void samsung_early_write(struct console *con, const char *s, unsigned n)
+{
+       struct earlycon_device *dev = con->data;
+
+       uart_console_write(&dev->port, s, n, samsung_early_putc);
+}
+
+static int __init samsung_early_console_setup(struct earlycon_device *device,
+                                             const char *opt)
+{
+       if (!device->port.membase)
+               return -ENODEV;
+
+       device->con->write = samsung_early_write;
+       return 0;
+}
+
+/* S3C2410 */
+static struct samsung_early_console_data s3c2410_early_console_data = {
+       .txfull_mask = S3C2410_UFSTAT_TXFULL,
+};
+
+static int __init s3c2410_early_console_setup(struct earlycon_device *device,
+                                             const char *opt)
+{
+       device->port.private_data = &s3c2410_early_console_data;
+       return samsung_early_console_setup(device, opt);
+}
+OF_EARLYCON_DECLARE(s3c2410, "samsung,s3c2410-uart",
+                       s3c2410_early_console_setup);
+EARLYCON_DECLARE(s3c2410, s3c2410_early_console_setup);
+
+/* S3C2412, S3C2440, S3C64xx */
+static struct samsung_early_console_data s3c2440_early_console_data = {
+       .txfull_mask = S3C2440_UFSTAT_TXFULL,
+};
+
+static int __init s3c2440_early_console_setup(struct earlycon_device *device,
+                                             const char *opt)
+{
+       device->port.private_data = &s3c2440_early_console_data;
+       return samsung_early_console_setup(device, opt);
+}
+OF_EARLYCON_DECLARE(s3c2412, "samsung,s3c2412-uart",
+                       s3c2440_early_console_setup);
+OF_EARLYCON_DECLARE(s3c2440, "samsung,s3c2440-uart",
+                       s3c2440_early_console_setup);
+OF_EARLYCON_DECLARE(s3c6400, "samsung,s3c6400-uart",
+                       s3c2440_early_console_setup);
+EARLYCON_DECLARE(s3c2412, s3c2440_early_console_setup);
+EARLYCON_DECLARE(s3c2440, s3c2440_early_console_setup);
+EARLYCON_DECLARE(s3c6400, s3c2440_early_console_setup);
+
+/* S5PV210, EXYNOS */
+static struct samsung_early_console_data s5pv210_early_console_data = {
+       .txfull_mask = S5PV210_UFSTAT_TXFULL,
+};
+
+static int __init s5pv210_early_console_setup(struct earlycon_device *device,
+                                             const char *opt)
+{
+       device->port.private_data = &s5pv210_early_console_data;
+       return samsung_early_console_setup(device, opt);
+}
+OF_EARLYCON_DECLARE(s5pv210, "samsung,s5pv210-uart",
+                       s5pv210_early_console_setup);
+OF_EARLYCON_DECLARE(exynos4210, "samsung,exynos4210-uart",
+                       s5pv210_early_console_setup);
+EARLYCON_DECLARE(s5pv210, s5pv210_early_console_setup);
+EARLYCON_DECLARE(exynos4210, s5pv210_early_console_setup);
+#endif
+
 MODULE_ALIAS("platform:samsung-uart");
 MODULE_DESCRIPTION("Samsung SoC Serial port driver");
 MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
index eb071dd19b2d13deb189afba8a9bf6364d29e9ed..d275032aa68d47ee68c42a0370ff7eecc42fb192 100644 (file)
@@ -12,6 +12,8 @@
  * published by the Free Software Foundation.
 */
 
+#include <linux/dmaengine.h>
+
 struct s3c24xx_uart_info {
        char                    *name;
        unsigned int            type;
@@ -41,6 +43,40 @@ struct s3c24xx_serial_drv_data {
        unsigned int                    fifosize[CONFIG_SERIAL_SAMSUNG_UARTS];
 };
 
+struct s3c24xx_uart_dma {
+       dma_filter_fn                   fn;
+       void                            *rx_param;
+       void                            *tx_param;
+
+       unsigned int                    rx_chan_id;
+       unsigned int                    tx_chan_id;
+
+       struct dma_slave_config         rx_conf;
+       struct dma_slave_config         tx_conf;
+
+       struct dma_chan                 *rx_chan;
+       struct dma_chan                 *tx_chan;
+
+       dma_addr_t                      rx_addr;
+       dma_addr_t                      tx_addr;
+
+       dma_cookie_t                    rx_cookie;
+       dma_cookie_t                    tx_cookie;
+
+       char                            *rx_buf;
+
+       dma_addr_t                      tx_transfer_addr;
+
+       size_t                          rx_size;
+       size_t                          tx_size;
+
+       struct dma_async_tx_descriptor  *tx_desc;
+       struct dma_async_tx_descriptor  *rx_desc;
+
+       int                             tx_bytes_requested;
+       int                             rx_bytes_requested;
+};
+
 struct s3c24xx_uart_port {
        unsigned char                   rx_claimed;
        unsigned char                   tx_claimed;
@@ -50,6 +86,10 @@ struct s3c24xx_uart_port {
        unsigned int                    rx_irq;
        unsigned int                    tx_irq;
 
+       unsigned int                    tx_in_progress;
+       unsigned int                    tx_mode;
+       unsigned int                    rx_mode;
+
        struct s3c24xx_uart_info        *info;
        struct clk                      *clk;
        struct clk                      *baudclk;
@@ -59,6 +99,8 @@ struct s3c24xx_uart_port {
        /* reference to platform data */
        struct s3c2410_uartcfg          *cfg;
 
+       struct s3c24xx_uart_dma         *dma;
+
 #ifdef CONFIG_CPU_FREQ
        struct notifier_block           freq_transition;
 #endif
index 984605bb5bf1d593087bfffe485323538144b2c5..6a1055ae343727e964cbb6af44909b42cd526afb 100644 (file)
@@ -179,14 +179,6 @@ static int uart_port_startup(struct tty_struct *tty, struct uart_state *state,
                        if (tty->termios.c_cflag & CBAUD)
                                uart_set_mctrl(uport, TIOCM_RTS | TIOCM_DTR);
                }
-
-               spin_lock_irq(&uport->lock);
-               if (uart_cts_enabled(uport) &&
-                   !(uport->ops->get_mctrl(uport) & TIOCM_CTS))
-                       uport->hw_stopped = 1;
-               else
-                       uport->hw_stopped = 0;
-               spin_unlock_irq(&uport->lock);
        }
 
        /*
@@ -442,6 +434,7 @@ static void uart_change_speed(struct tty_struct *tty, struct uart_state *state,
 {
        struct uart_port *uport = state->uart_port;
        struct ktermios *termios;
+       int hw_stopped;
 
        /*
         * If we have no tty, termios, or the port does not exist,
@@ -466,6 +459,18 @@ static void uart_change_speed(struct tty_struct *tty, struct uart_state *state,
                uport->status &= ~UPSTAT_DCD_ENABLE;
        else
                uport->status |= UPSTAT_DCD_ENABLE;
+
+       /* reset sw-assisted CTS flow control based on (possibly) new mode */
+       hw_stopped = uport->hw_stopped;
+       uport->hw_stopped = uart_softcts_mode(uport) &&
+                               !(uport->ops->get_mctrl(uport) & TIOCM_CTS);
+       if (uport->hw_stopped) {
+               if (!hw_stopped)
+                       uport->ops->stop_tx(uport);
+       } else {
+               if (hw_stopped)
+                       __uart_start(tty);
+       }
        spin_unlock_irq(&uport->lock);
 }
 
@@ -619,22 +624,22 @@ static void uart_throttle(struct tty_struct *tty)
 {
        struct uart_state *state = tty->driver_data;
        struct uart_port *port = state->uart_port;
-       upf_t mask = 0;
+       upstat_t mask = 0;
 
        if (I_IXOFF(tty))
-               mask |= UPF_SOFT_FLOW;
+               mask |= UPSTAT_AUTOXOFF;
        if (tty->termios.c_cflag & CRTSCTS)
-               mask |= UPF_HARD_FLOW;
+               mask |= UPSTAT_AUTORTS;
 
-       if (port->flags & mask) {
+       if (port->status & mask) {
                port->ops->throttle(port);
-               mask &= ~port->flags;
+               mask &= ~port->status;
        }
 
-       if (mask & UPF_SOFT_FLOW)
+       if (mask & UPSTAT_AUTOXOFF)
                uart_send_xchar(tty, STOP_CHAR(tty));
 
-       if (mask & UPF_HARD_FLOW)
+       if (mask & UPSTAT_AUTORTS)
                uart_clear_mctrl(port, TIOCM_RTS);
 }
 
@@ -642,22 +647,22 @@ static void uart_unthrottle(struct tty_struct *tty)
 {
        struct uart_state *state = tty->driver_data;
        struct uart_port *port = state->uart_port;
-       upf_t mask = 0;
+       upstat_t mask = 0;
 
        if (I_IXOFF(tty))
-               mask |= UPF_SOFT_FLOW;
+               mask |= UPSTAT_AUTOXOFF;
        if (tty->termios.c_cflag & CRTSCTS)
-               mask |= UPF_HARD_FLOW;
+               mask |= UPSTAT_AUTORTS;
 
-       if (port->flags & mask) {
+       if (port->status & mask) {
                port->ops->unthrottle(port);
-               mask &= ~port->flags;
+               mask &= ~port->status;
        }
 
-       if (mask & UPF_SOFT_FLOW)
+       if (mask & UPSTAT_AUTOXOFF)
                uart_send_xchar(tty, START_CHAR(tty));
 
-       if (mask & UPF_HARD_FLOW)
+       if (mask & UPSTAT_AUTORTS)
                uart_set_mctrl(port, TIOCM_RTS);
 }
 
@@ -1351,30 +1356,6 @@ static void uart_set_termios(struct tty_struct *tty,
                        mask |= TIOCM_RTS;
                uart_set_mctrl(uport, mask);
        }
-
-       /*
-        * If the port is doing h/w assisted flow control, do nothing.
-        * We assume that port->hw_stopped has never been set.
-        */
-       if (uport->flags & UPF_HARD_FLOW)
-               return;
-
-       /* Handle turning off CRTSCTS */
-       if ((old_termios->c_cflag & CRTSCTS) && !(cflag & CRTSCTS)) {
-               spin_lock_irq(&uport->lock);
-               uport->hw_stopped = 0;
-               __uart_start(tty);
-               spin_unlock_irq(&uport->lock);
-       }
-       /* Handle turning on CRTSCTS */
-       else if (!(old_termios->c_cflag & CRTSCTS) && (cflag & CRTSCTS)) {
-               spin_lock_irq(&uport->lock);
-               if (!(uport->ops->get_mctrl(uport) & TIOCM_CTS)) {
-                       uport->hw_stopped = 1;
-                       uport->ops->stop_tx(uport);
-               }
-               spin_unlock_irq(&uport->lock);
-       }
 }
 
 /*
@@ -2008,23 +1989,24 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *uport)
        }
        put_device(tty_dev);
 
-       if (console_suspend_enabled || !uart_console(uport))
-               uport->suspended = 1;
+       /* Nothing to do if the console is not suspending */
+       if (!console_suspend_enabled && uart_console(uport))
+               goto unlock;
+
+       uport->suspended = 1;
 
        if (port->flags & ASYNC_INITIALIZED) {
                const struct uart_ops *ops = uport->ops;
                int tries;
 
-               if (console_suspend_enabled || !uart_console(uport)) {
-                       set_bit(ASYNCB_SUSPENDED, &port->flags);
-                       clear_bit(ASYNCB_INITIALIZED, &port->flags);
-
-                       spin_lock_irq(&uport->lock);
-                       ops->stop_tx(uport);
-                       ops->set_mctrl(uport, 0);
-                       ops->stop_rx(uport);
-                       spin_unlock_irq(&uport->lock);
-               }
+               set_bit(ASYNCB_SUSPENDED, &port->flags);
+               clear_bit(ASYNCB_INITIALIZED, &port->flags);
+
+               spin_lock_irq(&uport->lock);
+               ops->stop_tx(uport);
+               ops->set_mctrl(uport, 0);
+               ops->stop_rx(uport);
+               spin_unlock_irq(&uport->lock);
 
                /*
                 * Wait for the transmitter to empty.
@@ -2036,19 +2018,17 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *uport)
                                drv->dev_name,
                                drv->tty_driver->name_base + uport->line);
 
-               if (console_suspend_enabled || !uart_console(uport))
-                       ops->shutdown(uport);
+               ops->shutdown(uport);
        }
 
        /*
         * Disable the console device before suspending.
         */
-       if (console_suspend_enabled && uart_console(uport))
+       if (uart_console(uport))
                console_stop(uport->cons);
 
-       if (console_suspend_enabled || !uart_console(uport))
-               uart_change_pm(state, UART_PM_STATE_OFF);
-
+       uart_change_pm(state, UART_PM_STATE_OFF);
+unlock:
        mutex_unlock(&port->mutex);
 
        return 0;
@@ -2856,7 +2836,7 @@ void uart_handle_cts_change(struct uart_port *uport, unsigned int status)
 
        uport->icount.cts++;
 
-       if (uart_cts_enabled(uport)) {
+       if (uart_softcts_mode(uport)) {
                if (uport->hw_stopped) {
                        if (status) {
                                uport->hw_stopped = 0;
@@ -2869,6 +2849,7 @@ void uart_handle_cts_change(struct uart_port *uport, unsigned int status)
                                uport->ops->stop_tx(uport);
                        }
                }
+
        }
 }
 EXPORT_SYMBOL_GPL(uart_handle_cts_change);
index e032963989fc00199f8aca45082f5047d3f61214..5b50c792ad5fd00e72ef86ed95a196d20e9ae06c 100644 (file)
@@ -858,7 +858,7 @@ static int sci_handle_fifo_overrun(struct uart_port *port)
                tty_insert_flip_char(tport, 0, TTY_OVERRUN);
                tty_flip_buffer_push(tport);
 
-               dev_notice(port->dev, "overrun error\n");
+               dev_dbg(port->dev, "overrun error\n");
                copied++;
        }
 
@@ -997,12 +997,15 @@ static inline unsigned long port_rx_irq_mask(struct uart_port *port)
 static irqreturn_t sci_mpxed_interrupt(int irq, void *ptr)
 {
        unsigned short ssr_status, scr_status, err_enabled;
+       unsigned short slr_status = 0;
        struct uart_port *port = ptr;
        struct sci_port *s = to_sci_port(port);
        irqreturn_t ret = IRQ_NONE;
 
        ssr_status = serial_port_in(port, SCxSR);
        scr_status = serial_port_in(port, SCSCR);
+       if (port->type == PORT_SCIF || port->type == PORT_HSCIF)
+               slr_status = serial_port_in(port, SCLSR);
        err_enabled = scr_status & port_rx_irq_mask(port);
 
        /* Tx Interrupt */
@@ -1015,8 +1018,11 @@ static irqreturn_t sci_mpxed_interrupt(int irq, void *ptr)
         * DR flags
         */
        if (((ssr_status & SCxSR_RDxF(port)) || s->chan_rx) &&
-           (scr_status & SCSCR_RIE))
+           (scr_status & SCSCR_RIE)) {
+               if (port->type == PORT_SCIF || port->type == PORT_HSCIF)
+                       sci_handle_fifo_overrun(port);
                ret = sci_rx_interrupt(irq, ptr);
+       }
 
        /* Error Interrupt */
        if ((ssr_status & SCxSR_ERRORS(port)) && err_enabled)
@@ -1026,6 +1032,12 @@ static irqreturn_t sci_mpxed_interrupt(int irq, void *ptr)
        if ((ssr_status & SCxSR_BRK(port)) && err_enabled)
                ret = sci_br_interrupt(irq, ptr);
 
+       /* Overrun Interrupt */
+       if (port->type == PORT_SCIF || port->type == PORT_HSCIF) {
+               if (slr_status & 0x01)
+                       sci_handle_fifo_overrun(port);
+       }
+
        return ret;
 }
 
@@ -2605,7 +2617,7 @@ static int sci_probe(struct platform_device *dev)
        return 0;
 }
 
-static int sci_suspend(struct device *dev)
+static __maybe_unused int sci_suspend(struct device *dev)
 {
        struct sci_port *sport = dev_get_drvdata(dev);
 
@@ -2615,7 +2627,7 @@ static int sci_suspend(struct device *dev)
        return 0;
 }
 
-static int sci_resume(struct device *dev)
+static __maybe_unused int sci_resume(struct device *dev)
 {
        struct sci_port *sport = dev_get_drvdata(dev);
 
@@ -2625,10 +2637,7 @@ static int sci_resume(struct device *dev)
        return 0;
 }
 
-static const struct dev_pm_ops sci_dev_pm_ops = {
-       .suspend        = sci_suspend,
-       .resume         = sci_resume,
-};
+static SIMPLE_DEV_PM_OPS(sci_dev_pm_ops, sci_suspend, sci_resume);
 
 static struct platform_driver sci_driver = {
        .probe          = sci_probe,
index b269f6bd16d69f67126d444e79ef154dabbe573e..27ed0e960880990c810fe4190e28f03dd455db4f 100644 (file)
@@ -177,7 +177,7 @@ static void sirfsoc_uart_stop_tx(struct uart_port *port)
                        dmaengine_pause(sirfport->tx_dma_chan);
                        sirfport->tx_dma_state = TX_DMA_PAUSE;
                } else {
-                       if (!sirfport->is_marco)
+                       if (!sirfport->is_atlas7)
                                wr_regl(port, ureg->sirfsoc_int_en_reg,
                                rd_regl(port, ureg->sirfsoc_int_en_reg) &
                                ~uint_en->sirfsoc_txfifo_empty_en);
@@ -186,7 +186,7 @@ static void sirfsoc_uart_stop_tx(struct uart_port *port)
                                uint_en->sirfsoc_txfifo_empty_en);
                }
        } else {
-               if (!sirfport->is_marco)
+               if (!sirfport->is_atlas7)
                        wr_regl(port, ureg->sirfsoc_int_en_reg,
                                rd_regl(port, ureg->sirfsoc_int_en_reg) &
                                ~uint_en->sirfsoc_txfifo_empty_en);
@@ -217,7 +217,7 @@ static void sirfsoc_uart_tx_with_dma(struct sirfsoc_uart_port *sirfport)
        }
        if (sirfport->tx_dma_state == TX_DMA_RUNNING)
                return;
-       if (!sirfport->is_marco)
+       if (!sirfport->is_atlas7)
                wr_regl(port, ureg->sirfsoc_int_en_reg,
                                rd_regl(port, ureg->sirfsoc_int_en_reg)&
                                ~(uint_en->sirfsoc_txfifo_empty_en));
@@ -244,7 +244,7 @@ static void sirfsoc_uart_tx_with_dma(struct sirfsoc_uart_port *sirfport)
                }
                if (tran_size < 4)
                        sirfsoc_uart_pio_tx_chars(sirfport, tran_size);
-               if (!sirfport->is_marco)
+               if (!sirfport->is_atlas7)
                        wr_regl(port, ureg->sirfsoc_int_en_reg,
                                rd_regl(port, ureg->sirfsoc_int_en_reg)|
                                uint_en->sirfsoc_txfifo_empty_en);
@@ -293,7 +293,7 @@ static void sirfsoc_uart_start_tx(struct uart_port *port)
                sirfsoc_uart_pio_tx_chars(sirfport,
                        SIRFSOC_UART_IO_TX_REASONABLE_CNT);
                wr_regl(port, ureg->sirfsoc_tx_fifo_op, SIRFUART_FIFO_START);
-               if (!sirfport->is_marco)
+               if (!sirfport->is_atlas7)
                        wr_regl(port, ureg->sirfsoc_int_en_reg,
                                        rd_regl(port, ureg->sirfsoc_int_en_reg)|
                                        uint_en->sirfsoc_txfifo_empty_en);
@@ -311,7 +311,7 @@ static void sirfsoc_uart_stop_rx(struct uart_port *port)
 
        wr_regl(port, ureg->sirfsoc_rx_fifo_op, 0);
        if (sirfport->rx_dma_chan) {
-               if (!sirfport->is_marco)
+               if (!sirfport->is_atlas7)
                        wr_regl(port, ureg->sirfsoc_int_en_reg,
                                rd_regl(port, ureg->sirfsoc_int_en_reg) &
                                ~(SIRFUART_RX_DMA_INT_EN(port, uint_en) |
@@ -322,7 +322,7 @@ static void sirfsoc_uart_stop_rx(struct uart_port *port)
                                        uint_en->sirfsoc_rx_done_en);
                dmaengine_terminate_all(sirfport->rx_dma_chan);
        } else {
-               if (!sirfport->is_marco)
+               if (!sirfport->is_atlas7)
                        wr_regl(port, ureg->sirfsoc_int_en_reg,
                                rd_regl(port, ureg->sirfsoc_int_en_reg)&
                                ~(SIRFUART_RX_IO_INT_EN(port, uint_en)));
@@ -344,7 +344,7 @@ static void sirfsoc_uart_disable_ms(struct uart_port *port)
        if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) {
                wr_regl(port, ureg->sirfsoc_afc_ctrl,
                                rd_regl(port, ureg->sirfsoc_afc_ctrl) & ~0x3FF);
-               if (!sirfport->is_marco)
+               if (!sirfport->is_atlas7)
                        wr_regl(port, ureg->sirfsoc_int_en_reg,
                                        rd_regl(port, ureg->sirfsoc_int_en_reg)&
                                        ~uint_en->sirfsoc_cts_en);
@@ -380,7 +380,7 @@ static void sirfsoc_uart_enable_ms(struct uart_port *port)
                wr_regl(port, ureg->sirfsoc_afc_ctrl,
                                rd_regl(port, ureg->sirfsoc_afc_ctrl) |
                                SIRFUART_AFC_TX_EN | SIRFUART_AFC_RX_EN);
-               if (!sirfport->is_marco)
+               if (!sirfport->is_atlas7)
                        wr_regl(port, ureg->sirfsoc_int_en_reg,
                                        rd_regl(port, ureg->sirfsoc_int_en_reg)
                                        | uint_en->sirfsoc_cts_en);
@@ -544,7 +544,7 @@ static void sirfsoc_rx_tmo_process_tl(unsigned long param)
                sirfport->rx_io_count = 0;
                wr_regl(port, ureg->sirfsoc_int_st_reg,
                                uint_st->sirfsoc_rx_done);
-               if (!sirfport->is_marco)
+               if (!sirfport->is_atlas7)
                        wr_regl(port, ureg->sirfsoc_int_en_reg,
                                rd_regl(port, ureg->sirfsoc_int_en_reg) &
                                ~(uint_en->sirfsoc_rx_done_en));
@@ -555,7 +555,7 @@ static void sirfsoc_rx_tmo_process_tl(unsigned long param)
        } else {
                wr_regl(port, ureg->sirfsoc_int_st_reg,
                                uint_st->sirfsoc_rx_done);
-               if (!sirfport->is_marco)
+               if (!sirfport->is_atlas7)
                        wr_regl(port, ureg->sirfsoc_int_en_reg,
                                rd_regl(port, ureg->sirfsoc_int_en_reg) |
                                (uint_en->sirfsoc_rx_done_en));
@@ -578,7 +578,7 @@ static void sirfsoc_uart_handle_rx_tmo(struct sirfsoc_uart_port *sirfport)
        dmaengine_terminate_all(sirfport->rx_dma_chan);
        sirfport->rx_dma_items[sirfport->rx_issued].xmit.head =
                SIRFSOC_RX_DMA_BUF_SIZE - tx_state.residue;
-       if (!sirfport->is_marco)
+       if (!sirfport->is_atlas7)
                wr_regl(port, ureg->sirfsoc_int_en_reg,
                        rd_regl(port, ureg->sirfsoc_int_en_reg) &
                        ~(uint_en->sirfsoc_rx_timeout_en));
@@ -598,7 +598,7 @@ static void sirfsoc_uart_handle_rx_done(struct sirfsoc_uart_port *sirfport)
        sirfsoc_uart_pio_rx_chars(port, 4 - sirfport->rx_io_count);
        if (sirfport->rx_io_count == 4) {
                sirfport->rx_io_count = 0;
-               if (!sirfport->is_marco)
+               if (!sirfport->is_atlas7)
                        wr_regl(port, ureg->sirfsoc_int_en_reg,
                                rd_regl(port, ureg->sirfsoc_int_en_reg) &
                                ~(uint_en->sirfsoc_rx_done_en));
@@ -748,7 +748,7 @@ static void sirfsoc_uart_start_next_rx_dma(struct uart_port *port)
        for (i = 0; i < SIRFSOC_RX_LOOP_BUF_CNT; i++)
                sirfsoc_rx_submit_one_dma_desc(port, i);
        sirfport->rx_completed = sirfport->rx_issued = 0;
-       if (!sirfport->is_marco)
+       if (!sirfport->is_atlas7)
                wr_regl(port, ureg->sirfsoc_int_en_reg,
                                rd_regl(port, ureg->sirfsoc_int_en_reg) |
                                SIRFUART_RX_DMA_INT_EN(port, uint_en));
@@ -770,7 +770,7 @@ static void sirfsoc_uart_start_rx(struct uart_port *port)
        if (sirfport->rx_dma_chan)
                sirfsoc_uart_start_next_rx_dma(port);
        else {
-               if (!sirfport->is_marco)
+               if (!sirfport->is_atlas7)
                        wr_regl(port, ureg->sirfsoc_int_en_reg,
                                rd_regl(port, ureg->sirfsoc_int_en_reg) |
                                SIRFUART_RX_IO_INT_EN(port, uint_en));
@@ -1124,7 +1124,7 @@ static void sirfsoc_uart_shutdown(struct uart_port *port)
 {
        struct sirfsoc_uart_port *sirfport = to_sirfport(port);
        struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg;
-       if (!sirfport->is_marco)
+       if (!sirfport->is_atlas7)
                wr_regl(port, ureg->sirfsoc_int_en_reg, 0);
        else
                wr_regl(port, SIRFUART_INT_EN_CLR, ~0UL);
@@ -1271,7 +1271,7 @@ static struct uart_driver sirfsoc_uart_drv = {
 
 static struct of_device_id sirfsoc_uart_ids[] = {
        { .compatible = "sirf,prima2-uart", .data = &sirfsoc_uart,},
-       { .compatible = "sirf,marco-uart", .data = &sirfsoc_uart},
+       { .compatible = "sirf,atlas7-uart", .data = &sirfsoc_uart},
        { .compatible = "sirf,prima2-usp-uart", .data = &sirfsoc_usp},
        {}
 };
@@ -1350,8 +1350,8 @@ static int sirfsoc_uart_probe(struct platform_device *pdev)
                gpio_direction_output(sirfport->rts_gpio, 1);
        }
 usp_no_flow_control:
-       if (of_device_is_compatible(pdev->dev.of_node, "sirf,marco-uart"))
-               sirfport->is_marco = true;
+       if (of_device_is_compatible(pdev->dev.of_node, "sirf,atlas7-uart"))
+               sirfport->is_atlas7 = true;
 
        if (of_property_read_u32(pdev->dev.of_node,
                        "fifosize",
@@ -1393,7 +1393,7 @@ usp_no_flow_control:
                goto err;
        }
        port->uartclk = clk_get_rate(sirfport->clk);
-       if (of_device_is_compatible(pdev->dev.of_node, "sirf,marco-bt-uart")) {
+       if (of_device_is_compatible(pdev->dev.of_node, "sirf,atlas7-bt-uart")) {
                sirfport->clk_general = devm_clk_get(&pdev->dev, "general");
                if (IS_ERR(sirfport->clk_general)) {
                        ret = PTR_ERR(sirfport->clk_general);
index 275d038939901d7d95fe84da938f1c4a1b896b54..727eb6b88fff0f28e57d44dc789223f2855231b3 100644 (file)
@@ -421,8 +421,8 @@ struct sirfsoc_uart_port {
        bool                            is_bt_uart;
        struct clk                      *clk_general;
        struct clk                      *clk_noc;
-       /* for SiRFmarco, there are SET/CLR for UART_INT_EN */
-       bool                            is_marco;
+       /* for SiRFatlas7, there are SET/CLR for UART_INT_EN */
+       bool                            is_atlas7;
        struct sirfsoc_uart_register    *uart_reg;
        struct dma_chan                 *rx_dma_chan;
        struct dma_chan                 *tx_dma_chan;
diff --git a/drivers/tty/serial/sprd_serial.c b/drivers/tty/serial/sprd_serial.c
new file mode 100644 (file)
index 0000000..594b633
--- /dev/null
@@ -0,0 +1,793 @@
+/*
+ * Copyright (C) 2012-2015 Spreadtrum Communications Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#if defined(CONFIG_SERIAL_SPRD_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/clk.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/serial_core.h>
+#include <linux/serial.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+
+/* device name */
+#define UART_NR_MAX            8
+#define SPRD_TTY_NAME          "ttyS"
+#define SPRD_FIFO_SIZE         128
+#define SPRD_DEF_RATE          26000000
+#define SPRD_BAUD_IO_LIMIT     3000000
+#define SPRD_TIMEOUT           256
+
+/* the offset of serial registers and BITs for them */
+/* data registers */
+#define SPRD_TXD               0x0000
+#define SPRD_RXD               0x0004
+
+/* line status register and its BITs  */
+#define SPRD_LSR               0x0008
+#define SPRD_LSR_OE            BIT(4)
+#define SPRD_LSR_FE            BIT(3)
+#define SPRD_LSR_PE            BIT(2)
+#define SPRD_LSR_BI            BIT(7)
+#define SPRD_LSR_TX_OVER       BIT(15)
+
+/* data number in TX and RX fifo */
+#define SPRD_STS1              0x000C
+
+/* interrupt enable register and its BITs */
+#define SPRD_IEN               0x0010
+#define SPRD_IEN_RX_FULL       BIT(0)
+#define SPRD_IEN_TX_EMPTY      BIT(1)
+#define SPRD_IEN_BREAK_DETECT  BIT(7)
+#define SPRD_IEN_TIMEOUT       BIT(13)
+
+/* interrupt clear register */
+#define SPRD_ICLR              0x0014
+
+/* line control register */
+#define SPRD_LCR               0x0018
+#define SPRD_LCR_STOP_1BIT     0x10
+#define SPRD_LCR_STOP_2BIT     0x30
+#define SPRD_LCR_DATA_LEN      (BIT(2) | BIT(3))
+#define SPRD_LCR_DATA_LEN5     0x0
+#define SPRD_LCR_DATA_LEN6     0x4
+#define SPRD_LCR_DATA_LEN7     0x8
+#define SPRD_LCR_DATA_LEN8     0xc
+#define SPRD_LCR_PARITY        (BIT(0) | BIT(1))
+#define SPRD_LCR_PARITY_EN     0x2
+#define SPRD_LCR_EVEN_PAR      0x0
+#define SPRD_LCR_ODD_PAR       0x1
+
+/* control register 1 */
+#define SPRD_CTL1                      0x001C
+#define RX_HW_FLOW_CTL_THLD    BIT(6)
+#define RX_HW_FLOW_CTL_EN      BIT(7)
+#define TX_HW_FLOW_CTL_EN      BIT(8)
+#define RX_TOUT_THLD_DEF       0x3E00
+#define RX_HFC_THLD_DEF        0x40
+
+/* fifo threshold register */
+#define SPRD_CTL2              0x0020
+#define THLD_TX_EMPTY  0x40
+#define THLD_RX_FULL   0x40
+
+/* config baud rate register */
+#define SPRD_CLKD0             0x0024
+#define SPRD_CLKD1             0x0028
+
+/* interrupt mask status register */
+#define SPRD_IMSR                      0x002C
+#define SPRD_IMSR_RX_FIFO_FULL         BIT(0)
+#define SPRD_IMSR_TX_FIFO_EMPTY        BIT(1)
+#define SPRD_IMSR_BREAK_DETECT         BIT(7)
+#define SPRD_IMSR_TIMEOUT              BIT(13)
+
+struct reg_backup {
+       u32 ien;
+       u32 ctrl0;
+       u32 ctrl1;
+       u32 ctrl2;
+       u32 clkd0;
+       u32 clkd1;
+       u32 dspwait;
+};
+
+struct sprd_uart_port {
+       struct uart_port port;
+       struct reg_backup reg_bak;
+       char name[16];
+};
+
+static struct sprd_uart_port *sprd_port[UART_NR_MAX];
+static int sprd_ports_num;
+
+static inline unsigned int serial_in(struct uart_port *port, int offset)
+{
+       return readl_relaxed(port->membase + offset);
+}
+
+static inline void serial_out(struct uart_port *port, int offset, int value)
+{
+       writel_relaxed(value, port->membase + offset);
+}
+
+static unsigned int sprd_tx_empty(struct uart_port *port)
+{
+       if (serial_in(port, SPRD_STS1) & 0xff00)
+               return 0;
+       else
+               return TIOCSER_TEMT;
+}
+
+static unsigned int sprd_get_mctrl(struct uart_port *port)
+{
+       return TIOCM_DSR | TIOCM_CTS;
+}
+
+static void sprd_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+       /* nothing to do */
+}
+
+static void sprd_stop_tx(struct uart_port *port)
+{
+       unsigned int ien, iclr;
+
+       iclr = serial_in(port, SPRD_ICLR);
+       ien = serial_in(port, SPRD_IEN);
+
+       iclr |= SPRD_IEN_TX_EMPTY;
+       ien &= ~SPRD_IEN_TX_EMPTY;
+
+       serial_out(port, SPRD_ICLR, iclr);
+       serial_out(port, SPRD_IEN, ien);
+}
+
+static void sprd_start_tx(struct uart_port *port)
+{
+       unsigned int ien;
+
+       ien = serial_in(port, SPRD_IEN);
+       if (!(ien & SPRD_IEN_TX_EMPTY)) {
+               ien |= SPRD_IEN_TX_EMPTY;
+               serial_out(port, SPRD_IEN, ien);
+       }
+}
+
+static void sprd_stop_rx(struct uart_port *port)
+{
+       unsigned int ien, iclr;
+
+       iclr = serial_in(port, SPRD_ICLR);
+       ien = serial_in(port, SPRD_IEN);
+
+       ien &= ~(SPRD_IEN_RX_FULL | SPRD_IEN_BREAK_DETECT);
+       iclr |= SPRD_IEN_RX_FULL | SPRD_IEN_BREAK_DETECT;
+
+       serial_out(port, SPRD_IEN, ien);
+       serial_out(port, SPRD_ICLR, iclr);
+}
+
+/* The Sprd serial does not support this function. */
+static void sprd_break_ctl(struct uart_port *port, int break_state)
+{
+       /* nothing to do */
+}
+
+static int handle_lsr_errors(struct uart_port *port,
+                            unsigned int *flag,
+                            unsigned int *lsr)
+{
+       int ret = 0;
+
+       /* statistics */
+       if (*lsr & SPRD_LSR_BI) {
+               *lsr &= ~(SPRD_LSR_FE | SPRD_LSR_PE);
+               port->icount.brk++;
+               ret = uart_handle_break(port);
+               if (ret)
+                       return ret;
+       } else if (*lsr & SPRD_LSR_PE)
+               port->icount.parity++;
+       else if (*lsr & SPRD_LSR_FE)
+               port->icount.frame++;
+       if (*lsr & SPRD_LSR_OE)
+               port->icount.overrun++;
+
+       /* mask off conditions which should be ignored */
+       *lsr &= port->read_status_mask;
+       if (*lsr & SPRD_LSR_BI)
+               *flag = TTY_BREAK;
+       else if (*lsr & SPRD_LSR_PE)
+               *flag = TTY_PARITY;
+       else if (*lsr & SPRD_LSR_FE)
+               *flag = TTY_FRAME;
+
+       return ret;
+}
+
+static inline void sprd_rx(struct uart_port *port)
+{
+       struct tty_port *tty = &port->state->port;
+       unsigned int ch, flag, lsr, max_count = SPRD_TIMEOUT;
+
+       while ((serial_in(port, SPRD_STS1) & 0x00ff) && max_count--) {
+               lsr = serial_in(port, SPRD_LSR);
+               ch = serial_in(port, SPRD_RXD);
+               flag = TTY_NORMAL;
+               port->icount.rx++;
+
+               if (lsr & (SPRD_LSR_BI | SPRD_LSR_PE |
+                       SPRD_LSR_FE | SPRD_LSR_OE))
+                       if (handle_lsr_errors(port, &lsr, &flag))
+                               continue;
+               if (uart_handle_sysrq_char(port, ch))
+                       continue;
+
+               uart_insert_char(port, lsr, SPRD_LSR_OE, ch, flag);
+       }
+
+       tty_flip_buffer_push(tty);
+}
+
+static inline void sprd_tx(struct uart_port *port)
+{
+       struct circ_buf *xmit = &port->state->xmit;
+       int count;
+
+       if (port->x_char) {
+               serial_out(port, SPRD_TXD, port->x_char);
+               port->icount.tx++;
+               port->x_char = 0;
+               return;
+       }
+
+       if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
+               sprd_stop_tx(port);
+               return;
+       }
+
+       count = THLD_TX_EMPTY;
+       do {
+               serial_out(port, SPRD_TXD, xmit->buf[xmit->tail]);
+               xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+               port->icount.tx++;
+               if (uart_circ_empty(xmit))
+                       break;
+       } while (--count > 0);
+
+       if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+               uart_write_wakeup(port);
+
+       if (uart_circ_empty(xmit))
+               sprd_stop_tx(port);
+}
+
+/* this handles the interrupt from one port */
+static irqreturn_t sprd_handle_irq(int irq, void *dev_id)
+{
+       struct uart_port *port = dev_id;
+       unsigned int ims;
+
+       spin_lock(&port->lock);
+
+       ims = serial_in(port, SPRD_IMSR);
+
+       if (!ims)
+               return IRQ_NONE;
+
+       serial_out(port, SPRD_ICLR, ~0);
+
+       if (ims & (SPRD_IMSR_RX_FIFO_FULL |
+               SPRD_IMSR_BREAK_DETECT | SPRD_IMSR_TIMEOUT))
+               sprd_rx(port);
+
+       if (ims & SPRD_IMSR_TX_FIFO_EMPTY)
+               sprd_tx(port);
+
+       spin_unlock(&port->lock);
+
+       return IRQ_HANDLED;
+}
+
+static int sprd_startup(struct uart_port *port)
+{
+       int ret = 0;
+       unsigned int ien, fc;
+       unsigned int timeout;
+       struct sprd_uart_port *sp;
+       unsigned long flags;
+
+       serial_out(port, SPRD_CTL2, ((THLD_TX_EMPTY << 8) | THLD_RX_FULL));
+
+       /* clear rx fifo */
+       timeout = SPRD_TIMEOUT;
+       while (timeout-- && serial_in(port, SPRD_STS1) & 0x00ff)
+               serial_in(port, SPRD_RXD);
+
+       /* clear tx fifo */
+       timeout = SPRD_TIMEOUT;
+       while (timeout-- && serial_in(port, SPRD_STS1) & 0xff00)
+               cpu_relax();
+
+       /* clear interrupt */
+       serial_out(port, SPRD_IEN, 0);
+       serial_out(port, SPRD_ICLR, ~0);
+
+       /* allocate irq */
+       sp = container_of(port, struct sprd_uart_port, port);
+       snprintf(sp->name, sizeof(sp->name), "sprd_serial%d", port->line);
+       ret = devm_request_irq(port->dev, port->irq, sprd_handle_irq,
+                               IRQF_SHARED, sp->name, port);
+       if (ret) {
+               dev_err(port->dev, "fail to request serial irq %d, ret=%d\n",
+                       port->irq, ret);
+               return ret;
+       }
+       fc = serial_in(port, SPRD_CTL1);
+       fc |= RX_TOUT_THLD_DEF | RX_HFC_THLD_DEF;
+       serial_out(port, SPRD_CTL1, fc);
+
+       /* enable interrupt */
+       spin_lock_irqsave(&port->lock, flags);
+       ien = serial_in(port, SPRD_IEN);
+       ien |= SPRD_IEN_RX_FULL | SPRD_IEN_BREAK_DETECT | SPRD_IEN_TIMEOUT;
+       serial_out(port, SPRD_IEN, ien);
+       spin_unlock_irqrestore(&port->lock, flags);
+
+       return 0;
+}
+
+static void sprd_shutdown(struct uart_port *port)
+{
+       serial_out(port, SPRD_IEN, 0);
+       serial_out(port, SPRD_ICLR, ~0);
+       devm_free_irq(port->dev, port->irq, port);
+}
+
+static void sprd_set_termios(struct uart_port *port,
+                                   struct ktermios *termios,
+                                   struct ktermios *old)
+{
+       unsigned int baud, quot;
+       unsigned int lcr = 0, fc;
+       unsigned long flags;
+
+       /* ask the core to calculate the divisor for us */
+       baud = uart_get_baud_rate(port, termios, old, 0, SPRD_BAUD_IO_LIMIT);
+
+       quot = (unsigned int)((port->uartclk + baud / 2) / baud);
+
+       /* set data length */
+       switch (termios->c_cflag & CSIZE) {
+       case CS5:
+               lcr |= SPRD_LCR_DATA_LEN5;
+               break;
+       case CS6:
+               lcr |= SPRD_LCR_DATA_LEN6;
+               break;
+       case CS7:
+               lcr |= SPRD_LCR_DATA_LEN7;
+               break;
+       case CS8:
+       default:
+               lcr |= SPRD_LCR_DATA_LEN8;
+               break;
+       }
+
+       /* calculate stop bits */
+       lcr &= ~(SPRD_LCR_STOP_1BIT | SPRD_LCR_STOP_2BIT);
+       if (termios->c_cflag & CSTOPB)
+               lcr |= SPRD_LCR_STOP_2BIT;
+       else
+               lcr |= SPRD_LCR_STOP_1BIT;
+
+       /* calculate parity */
+       lcr &= ~SPRD_LCR_PARITY;
+       termios->c_cflag &= ~CMSPAR;    /* no support mark/space */
+       if (termios->c_cflag & PARENB) {
+               lcr |= SPRD_LCR_PARITY_EN;
+               if (termios->c_cflag & PARODD)
+                       lcr |= SPRD_LCR_ODD_PAR;
+               else
+                       lcr |= SPRD_LCR_EVEN_PAR;
+       }
+
+       spin_lock_irqsave(&port->lock, flags);
+
+       /* update the per-port timeout */
+       uart_update_timeout(port, termios->c_cflag, baud);
+
+       port->read_status_mask = SPRD_LSR_OE;
+       if (termios->c_iflag & INPCK)
+               port->read_status_mask |= SPRD_LSR_FE | SPRD_LSR_PE;
+       if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK))
+               port->read_status_mask |= SPRD_LSR_BI;
+
+       /* characters to ignore */
+       port->ignore_status_mask = 0;
+       if (termios->c_iflag & IGNPAR)
+               port->ignore_status_mask |= SPRD_LSR_PE | SPRD_LSR_FE;
+       if (termios->c_iflag & IGNBRK) {
+               port->ignore_status_mask |= SPRD_LSR_BI;
+               /*
+                * If we're ignoring parity and break indicators,
+                * ignore overruns too (for real raw support).
+                */
+               if (termios->c_iflag & IGNPAR)
+                       port->ignore_status_mask |= SPRD_LSR_OE;
+       }
+
+       /* flow control */
+       fc = serial_in(port, SPRD_CTL1);
+       fc &= ~(RX_HW_FLOW_CTL_THLD | RX_HW_FLOW_CTL_EN | TX_HW_FLOW_CTL_EN);
+       if (termios->c_cflag & CRTSCTS) {
+               fc |= RX_HW_FLOW_CTL_THLD;
+               fc |= RX_HW_FLOW_CTL_EN;
+               fc |= TX_HW_FLOW_CTL_EN;
+       }
+
+       /* clock divider bit0~bit15 */
+       serial_out(port, SPRD_CLKD0, quot & 0xffff);
+
+       /* clock divider bit16~bit20 */
+       serial_out(port, SPRD_CLKD1, (quot & 0x1f0000) >> 16);
+       serial_out(port, SPRD_LCR, lcr);
+       fc |= RX_TOUT_THLD_DEF | RX_HFC_THLD_DEF;
+       serial_out(port, SPRD_CTL1, fc);
+
+       spin_unlock_irqrestore(&port->lock, flags);
+
+       /* Don't rewrite B0 */
+       if (tty_termios_baud_rate(termios))
+               tty_termios_encode_baud_rate(termios, baud, baud);
+}
+
+static const char *sprd_type(struct uart_port *port)
+{
+       return "SPX";
+}
+
+static void sprd_release_port(struct uart_port *port)
+{
+       /* nothing to do */
+}
+
+static int sprd_request_port(struct uart_port *port)
+{
+       return 0;
+}
+
+static void sprd_config_port(struct uart_port *port, int flags)
+{
+       if (flags & UART_CONFIG_TYPE)
+               port->type = PORT_SPRD;
+}
+
+static int sprd_verify_port(struct uart_port *port,
+                                  struct serial_struct *ser)
+{
+       if (ser->type != PORT_SPRD)
+               return -EINVAL;
+       if (port->irq != ser->irq)
+               return -EINVAL;
+       return 0;
+}
+
+static struct uart_ops serial_sprd_ops = {
+       .tx_empty = sprd_tx_empty,
+       .get_mctrl = sprd_get_mctrl,
+       .set_mctrl = sprd_set_mctrl,
+       .stop_tx = sprd_stop_tx,
+       .start_tx = sprd_start_tx,
+       .stop_rx = sprd_stop_rx,
+       .break_ctl = sprd_break_ctl,
+       .startup = sprd_startup,
+       .shutdown = sprd_shutdown,
+       .set_termios = sprd_set_termios,
+       .type = sprd_type,
+       .release_port = sprd_release_port,
+       .request_port = sprd_request_port,
+       .config_port = sprd_config_port,
+       .verify_port = sprd_verify_port,
+};
+
+#ifdef CONFIG_SERIAL_SPRD_CONSOLE
+static inline void wait_for_xmitr(struct uart_port *port)
+{
+       unsigned int status, tmout = 10000;
+
+       /* wait up to 10ms for the character(s) to be sent */
+       do {
+               status = serial_in(port, SPRD_STS1);
+               if (--tmout == 0)
+                       break;
+               udelay(1);
+       } while (status & 0xff00);
+}
+
+static void sprd_console_putchar(struct uart_port *port, int ch)
+{
+       wait_for_xmitr(port);
+       serial_out(port, SPRD_TXD, ch);
+}
+
+static void sprd_console_write(struct console *co, const char *s,
+                                     unsigned int count)
+{
+       struct uart_port *port = &sprd_port[co->index]->port;
+       int locked = 1;
+       unsigned long flags;
+
+       if (port->sysrq)
+               locked = 0;
+       else if (oops_in_progress)
+               locked = spin_trylock_irqsave(&port->lock, flags);
+       else
+               spin_lock_irqsave(&port->lock, flags);
+
+       uart_console_write(port, s, count, sprd_console_putchar);
+
+       /* wait for transmitter to become empty */
+       wait_for_xmitr(port);
+
+       if (locked)
+               spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static int __init sprd_console_setup(struct console *co, char *options)
+{
+       struct uart_port *port;
+       int baud = 115200;
+       int bits = 8;
+       int parity = 'n';
+       int flow = 'n';
+
+       if (co->index >= UART_NR_MAX || co->index < 0)
+               co->index = 0;
+
+       port = &sprd_port[co->index]->port;
+       if (port == NULL) {
+               pr_info("serial port %d not yet initialized\n", co->index);
+               return -ENODEV;
+       }
+       if (options)
+               uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+       return uart_set_options(port, co, baud, parity, bits, flow);
+}
+
+static struct uart_driver sprd_uart_driver;
+static struct console sprd_console = {
+       .name = SPRD_TTY_NAME,
+       .write = sprd_console_write,
+       .device = uart_console_device,
+       .setup = sprd_console_setup,
+       .flags = CON_PRINTBUFFER,
+       .index = -1,
+       .data = &sprd_uart_driver,
+};
+
+#define SPRD_CONSOLE   (&sprd_console)
+
+/* Support for earlycon */
+static void sprd_putc(struct uart_port *port, int c)
+{
+       unsigned int timeout = SPRD_TIMEOUT;
+
+       while (timeout-- &&
+                  !(readl(port->membase + SPRD_LSR) & SPRD_LSR_TX_OVER))
+               cpu_relax();
+
+       writeb(c, port->membase + SPRD_TXD);
+}
+
+static void sprd_early_write(struct console *con, const char *s,
+                                   unsigned n)
+{
+       struct earlycon_device *dev = con->data;
+
+       uart_console_write(&dev->port, s, n, sprd_putc);
+}
+
+static int __init sprd_early_console_setup(
+                               struct earlycon_device *device,
+                               const char *opt)
+{
+       if (!device->port.membase)
+               return -ENODEV;
+
+       device->con->write = sprd_early_write;
+       return 0;
+}
+
+EARLYCON_DECLARE(sprd_serial, sprd_early_console_setup);
+OF_EARLYCON_DECLARE(sprd_serial, "sprd,sc9836-uart",
+                   sprd_early_console_setup);
+
+#else /* !CONFIG_SERIAL_SPRD_CONSOLE */
+#define SPRD_CONSOLE           NULL
+#endif
+
+static struct uart_driver sprd_uart_driver = {
+       .owner = THIS_MODULE,
+       .driver_name = "sprd_serial",
+       .dev_name = SPRD_TTY_NAME,
+       .major = 0,
+       .minor = 0,
+       .nr = UART_NR_MAX,
+       .cons = SPRD_CONSOLE,
+};
+
+static int sprd_probe_dt_alias(int index, struct device *dev)
+{
+       struct device_node *np;
+       int ret = index;
+
+       if (!IS_ENABLED(CONFIG_OF))
+               return ret;
+
+       np = dev->of_node;
+       if (!np)
+               return ret;
+
+       ret = of_alias_get_id(np, "serial");
+       if (IS_ERR_VALUE(ret))
+               ret = index;
+       else if (ret >= ARRAY_SIZE(sprd_port) || sprd_port[ret] != NULL) {
+               dev_warn(dev, "requested serial port %d not available.\n", ret);
+               ret = index;
+       }
+
+       return ret;
+}
+
+static int sprd_remove(struct platform_device *dev)
+{
+       struct sprd_uart_port *sup = platform_get_drvdata(dev);
+
+       if (sup) {
+               uart_remove_one_port(&sprd_uart_driver, &sup->port);
+               sprd_port[sup->port.line] = NULL;
+               sprd_ports_num--;
+       }
+
+       if (!sprd_ports_num)
+               uart_unregister_driver(&sprd_uart_driver);
+
+       return 0;
+}
+
+static int sprd_probe(struct platform_device *pdev)
+{
+       struct resource *res;
+       struct uart_port *up;
+       struct clk *clk;
+       int irq;
+       int index;
+       int ret;
+
+       for (index = 0; index < ARRAY_SIZE(sprd_port); index++)
+               if (sprd_port[index] == NULL)
+                       break;
+
+       if (index == ARRAY_SIZE(sprd_port))
+               return -EBUSY;
+
+       index = sprd_probe_dt_alias(index, &pdev->dev);
+
+       sprd_port[index] = devm_kzalloc(&pdev->dev,
+               sizeof(*sprd_port[index]), GFP_KERNEL);
+       if (!sprd_port[index])
+               return -ENOMEM;
+
+       up = &sprd_port[index]->port;
+       up->dev = &pdev->dev;
+       up->line = index;
+       up->type = PORT_SPRD;
+       up->iotype = SERIAL_IO_PORT;
+       up->uartclk = SPRD_DEF_RATE;
+       up->fifosize = SPRD_FIFO_SIZE;
+       up->ops = &serial_sprd_ops;
+       up->flags = UPF_BOOT_AUTOCONF;
+
+       clk = devm_clk_get(&pdev->dev, NULL);
+       if (!IS_ERR(clk))
+               up->uartclk = clk_get_rate(clk);
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res) {
+               dev_err(&pdev->dev, "not provide mem resource\n");
+               return -ENODEV;
+       }
+       up->mapbase = res->start;
+       up->membase = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(up->membase))
+               return PTR_ERR(up->membase);
+
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0) {
+               dev_err(&pdev->dev, "not provide irq resource\n");
+               return -ENODEV;
+       }
+       up->irq = irq;
+
+       if (!sprd_ports_num) {
+               ret = uart_register_driver(&sprd_uart_driver);
+               if (ret < 0) {
+                       pr_err("Failed to register SPRD-UART driver\n");
+                       return ret;
+               }
+       }
+       sprd_ports_num++;
+
+       ret = uart_add_one_port(&sprd_uart_driver, up);
+       if (ret) {
+               sprd_port[index] = NULL;
+               sprd_remove(pdev);
+       }
+
+       platform_set_drvdata(pdev, up);
+
+       return ret;
+}
+
+static int sprd_suspend(struct device *dev)
+{
+       struct sprd_uart_port *sup = dev_get_drvdata(dev);
+
+       uart_suspend_port(&sprd_uart_driver, &sup->port);
+
+       return 0;
+}
+
+static int sprd_resume(struct device *dev)
+{
+       struct sprd_uart_port *sup = dev_get_drvdata(dev);
+
+       uart_resume_port(&sprd_uart_driver, &sup->port);
+
+       return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(sprd_pm_ops, sprd_suspend, sprd_resume);
+
+static const struct of_device_id serial_ids[] = {
+       {.compatible = "sprd,sc9836-uart",},
+       {}
+};
+
+static struct platform_driver sprd_platform_driver = {
+       .probe          = sprd_probe,
+       .remove         = sprd_remove,
+       .driver         = {
+               .name   = "sprd_serial",
+               .of_match_table = of_match_ptr(serial_ids),
+               .pm     = &sprd_pm_ops,
+       },
+};
+
+module_platform_driver(sprd_platform_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Spreadtrum SoC serial driver series");
index 542bab37e50265fe0dfc959bd861231a75511afd..cff531a51a783f8501bae180fd5788ce45a00c34 100644 (file)
@@ -637,10 +637,12 @@ static void cdns_uart_set_termios(struct uart_port *port,
 
        spin_lock_irqsave(&port->lock, flags);
 
-       /* Empty the receive FIFO 1st before making changes */
-       while ((cdns_uart_readl(CDNS_UART_SR_OFFSET) &
-                CDNS_UART_SR_RXEMPTY) != CDNS_UART_SR_RXEMPTY) {
-               cdns_uart_readl(CDNS_UART_FIFO_OFFSET);
+       /* Wait for the transmit FIFO to empty before making changes */
+       if (!(cdns_uart_readl(CDNS_UART_CR_OFFSET) & CDNS_UART_CR_TX_DIS)) {
+               while (!(cdns_uart_readl(CDNS_UART_SR_OFFSET) &
+                               CDNS_UART_SR_TXEMPTY)) {
+                       cpu_relax();
+               }
        }
 
        /* Disable the TX and RX to set baud rate */
index 3605103fc1ac6d891f255fea127f6fbd8b6ad96b..75661641f5fe068237e7830acd9472383964da3a 100644 (file)
@@ -557,3 +557,9 @@ int tty_buffer_set_limit(struct tty_port *port, int limit)
        return 0;
 }
 EXPORT_SYMBOL_GPL(tty_buffer_set_limit);
+
+/* slave ptys can claim nested buffer lock when handling BRK and INTR */
+void tty_buffer_set_lock_subclass(struct tty_port *port)
+{
+       lockdep_set_subclass(&port->buf.lock, TTY_LOCK_SLAVE);
+}
index 1787fa4d9448bfd2428be22cbc9be0aac1f24b1d..a5cf253b2544f001a775f93a7905e63475f7eb7e 100644 (file)
@@ -530,7 +530,7 @@ EXPORT_SYMBOL(tty_termios_hw_change);
  *     Locking: termios_rwsem
  */
 
-int tty_set_termios(struct tty_struct *tty, struct ktermios *new_termios)
+static int tty_set_termios(struct tty_struct *tty, struct ktermios *new_termios)
 {
        struct ktermios old_termios;
        struct tty_ldisc *ld;
@@ -563,7 +563,6 @@ int tty_set_termios(struct tty_struct *tty, struct ktermios *new_termios)
        up_write(&tty->termios_rwsem);
        return 0;
 }
-EXPORT_SYMBOL_GPL(tty_set_termios);
 
 /**
  *     set_termios             -       set termios values for a tty
index 4486741190c47d52d8079f360081d99085ce82c4..0efcf713b756e5e7eaf95bf7b5f2a744fc7f6dc0 100644 (file)
@@ -4,18 +4,8 @@
 #include <linux/semaphore.h>
 #include <linux/sched.h>
 
-/*
- * Nested tty locks are necessary for releasing pty pairs.
- * The stable lock order is master pty first, then slave pty.
- */
-
 /* Legacy tty mutex glue */
 
-enum {
-       TTY_MUTEX_NORMAL,
-       TTY_MUTEX_SLAVE,
-};
-
 /*
  * Getting the big tty mutex.
  */
@@ -46,12 +36,8 @@ EXPORT_SYMBOL(tty_unlock);
 
 void __lockfunc tty_lock_slave(struct tty_struct *tty)
 {
-       if (tty && tty != tty->link) {
-               WARN_ON(!mutex_is_locked(&tty->link->legacy_mutex) ||
-                       !tty->driver->type == TTY_DRIVER_TYPE_PTY ||
-                       !tty->driver->type == PTY_TYPE_SLAVE);
+       if (tty && tty != tty->link)
                tty_lock(tty);
-       }
 }
 
 void __lockfunc tty_unlock_slave(struct tty_struct *tty)
@@ -62,5 +48,5 @@ void __lockfunc tty_unlock_slave(struct tty_struct *tty)
 
 void tty_set_lock_subclass(struct tty_struct *tty)
 {
-       lockdep_set_subclass(&tty->legacy_mutex, TTY_MUTEX_SLAVE);
+       lockdep_set_subclass(&tty->legacy_mutex, TTY_LOCK_SLAVE);
 }
index f3fbbbca9bde1ba4d9a9a5ccda71f96b2d464eac..6e00572cbeb9386bd357a5ec466e5c2eb259fc4c 100644 (file)
@@ -500,6 +500,7 @@ void invert_screen(struct vc_data *vc, int offset, int count, int viewed)
 #endif
        if (DO_UPDATE(vc))
                do_update_region(vc, (unsigned long) p, count);
+       notify_update(vc);
 }
 
 /* used by selection: complement pointer position */
@@ -516,6 +517,7 @@ void complement_pos(struct vc_data *vc, int offset)
                scr_writew(old, screenpos(vc, old_offset, 1));
                if (DO_UPDATE(vc))
                        vc->vc_sw->con_putc(vc, old, oldy, oldx);
+               notify_update(vc);
        }
 
        old_offset = offset;
@@ -533,8 +535,8 @@ void complement_pos(struct vc_data *vc, int offset)
                        oldy = (offset >> 1) / vc->vc_cols;
                        vc->vc_sw->con_putc(vc, new, oldy, oldx);
                }
+               notify_update(vc);
        }
-
 }
 
 static void insert_char(struct vc_data *vc, unsigned int nr)
@@ -3318,11 +3320,8 @@ static int vt_bind(struct con_driver *con)
                if (first == 0 && last == MAX_NR_CONSOLES -1)
                        deflt = 1;
 
-               if (first != -1) {
-                       console_lock();
+               if (first != -1)
                        do_bind_con_driver(csw, first, last, deflt);
-                       console_unlock();
-               }
 
                first = -1;
                last = -1;
@@ -3362,9 +3361,7 @@ static int vt_unbind(struct con_driver *con)
                        deflt = 1;
 
                if (first != -1) {
-                       console_lock();
                        ret = do_unbind_con_driver(csw, first, last, deflt);
-                       console_unlock();
                        if (ret != 0)
                                return ret;
                }
@@ -3394,11 +3391,15 @@ static ssize_t store_bind(struct device *dev, struct device_attribute *attr,
        struct con_driver *con = dev_get_drvdata(dev);
        int bind = simple_strtoul(buf, NULL, 0);
 
+       console_lock();
+
        if (bind)
                vt_bind(con);
        else
                vt_unbind(con);
 
+       console_unlock();
+
        return count;
 }
 
@@ -3665,8 +3666,7 @@ int do_unregister_con_driver(const struct consw *csw)
        for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
                struct con_driver *con_driver = &registered_con_driver[i];
 
-               if (con_driver->con == csw &&
-                   con_driver->flag & CON_DRIVER_FLAG_INIT) {
+               if (con_driver->con == csw) {
                        vtconsole_deinit_device(con_driver);
                        device_destroy(vtconsole_class,
                                       MKDEV(0, con_driver->node));
index 195aafc6cd07c691e37e7d0247538dbd63ff5360..6512e9cbc6d57ca6abe80cc61a15db606c240cb7 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/list.h>
 #include <linux/errno.h>
 #include <linux/mod_devicetable.h>
+#include <linux/console.h>
 
 #define PNP_NAME_LEN           50
 
@@ -309,15 +310,22 @@ struct pnp_fixup {
 #define PNP_DISABLE            0x0004
 #define PNP_CONFIGURABLE       0x0008
 #define PNP_REMOVABLE          0x0010
+#define PNP_CONSOLE            0x0020
 
 #define pnp_can_read(dev)      (((dev)->protocol->get) && \
                                 ((dev)->capabilities & PNP_READ))
 #define pnp_can_write(dev)     (((dev)->protocol->set) && \
                                 ((dev)->capabilities & PNP_WRITE))
-#define pnp_can_disable(dev)   (((dev)->protocol->disable) && \
-                                ((dev)->capabilities & PNP_DISABLE))
+#define pnp_can_disable(dev)   (((dev)->protocol->disable) &&            \
+                                ((dev)->capabilities & PNP_DISABLE) &&   \
+                                (!((dev)->capabilities & PNP_CONSOLE) || \
+                                 console_suspend_enabled))
 #define pnp_can_configure(dev) ((!(dev)->active) && \
                                 ((dev)->capabilities & PNP_CONFIGURABLE))
+#define pnp_can_suspend(dev)   (((dev)->protocol->suspend) &&            \
+                                (!((dev)->capabilities & PNP_CONSOLE) || \
+                                 console_suspend_enabled))
+
 
 #ifdef CONFIG_ISAPNP
 extern struct pnp_protocol isapnp_protocol;
index e02acf0a0ec995f6b2751f40b1e6c82c5f776bd0..a8efa235b7c127c57a51b832bab4f335bdd571e2 100644 (file)
@@ -85,6 +85,9 @@ struct uart_8250_port {
        unsigned char           mcr_force;      /* mask of forced bits */
        unsigned char           cur_iotype;     /* Running I/O type */
        unsigned int            rpm_tx_active;
+       unsigned char           canary;         /* non-zero during system sleep
+                                                *   if no_console_suspend
+                                                */
 
        /*
         * Some bits in registers are cleared on a read, so they must
@@ -126,6 +129,7 @@ extern int serial8250_do_startup(struct uart_port *port);
 extern void serial8250_do_shutdown(struct uart_port *port);
 extern void serial8250_do_pm(struct uart_port *port, unsigned int state,
                             unsigned int oldstate);
+extern void serial8250_do_set_mctrl(struct uart_port *port, unsigned int mctrl);
 extern int fsl8250_handle_irq(struct uart_port *port);
 int serial8250_handle_irq(struct uart_port *port, unsigned int iir);
 unsigned char serial8250_rx_chars(struct uart_8250_port *up, unsigned char lsr);
index 057038cf27880d34a17e428fb51baccf563692c2..baf3e1d08416faaf34edd51bca72c2801e7e7e06 100644 (file)
@@ -123,6 +123,7 @@ struct uart_port {
        void                    (*set_termios)(struct uart_port *,
                                               struct ktermios *new,
                                               struct ktermios *old);
+       void                    (*set_mctrl)(struct uart_port *, unsigned int);
        int                     (*startup)(struct uart_port *port);
        void                    (*shutdown)(struct uart_port *port);
        void                    (*throttle)(struct uart_port *port);
@@ -190,8 +191,10 @@ struct uart_port {
 #define UPF_NO_TXEN_TEST       ((__force upf_t) (1 << 15))
 #define UPF_MAGIC_MULTIPLIER   ((__force upf_t) ASYNC_MAGIC_MULTIPLIER /* 16 */ )
 
-/* Port has hardware-assisted h/w flow control (iow, auto-RTS *not* auto-CTS) */
-#define UPF_HARD_FLOW          ((__force upf_t) (1 << 21))
+/* Port has hardware-assisted h/w flow control */
+#define UPF_AUTO_CTS           ((__force upf_t) (1 << 20))
+#define UPF_AUTO_RTS           ((__force upf_t) (1 << 21))
+#define UPF_HARD_FLOW          ((__force upf_t) (UPF_AUTO_CTS | UPF_AUTO_RTS))
 /* Port has hardware-assisted s/w flow control */
 #define UPF_SOFT_FLOW          ((__force upf_t) (1 << 22))
 #define UPF_CONS_FLOW          ((__force upf_t) (1 << 23))
@@ -213,11 +216,17 @@ struct uart_port {
 #error Change mask not equivalent to userspace-visible bit defines
 #endif
 
-       /* status must be updated while holding port lock */
+       /*
+        * Must hold termios_rwsem, port mutex and port lock to change;
+        * can hold any one lock to read.
+        */
        upstat_t                status;
 
 #define UPSTAT_CTS_ENABLE      ((__force upstat_t) (1 << 0))
 #define UPSTAT_DCD_ENABLE      ((__force upstat_t) (1 << 1))
+#define UPSTAT_AUTORTS         ((__force upstat_t) (1 << 2))
+#define UPSTAT_AUTOCTS         ((__force upstat_t) (1 << 3))
+#define UPSTAT_AUTOXOFF                ((__force upstat_t) (1 << 4))
 
        int                     hw_stopped;             /* sw-assisted CTS flow state */
        unsigned int            mctrl;                  /* current modem ctrl settings */
@@ -391,6 +400,13 @@ static inline bool uart_cts_enabled(struct uart_port *uport)
        return !!(uport->status & UPSTAT_CTS_ENABLE);
 }
 
+static inline bool uart_softcts_mode(struct uart_port *uport)
+{
+       upstat_t mask = UPSTAT_CTS_ENABLE | UPSTAT_AUTOCTS;
+
+       return ((uport->status & mask) == UPSTAT_CTS_ENABLE);
+}
+
 /*
  * The following are helper functions for the low level drivers.
  */
index e6fc9567690bc6d5fe59ac0dfcc88cbeaac01fc8..a7f004a3c177b143a18eb17d2fdbd41c17aa0268 100644 (file)
                                   S3C2410_UCON_RXIRQMODE | \
                                   S3C2410_UCON_RXFIFO_TOI)
 
+#define S3C64XX_UCON_TXBURST_1          (0<<20)
+#define S3C64XX_UCON_TXBURST_4          (1<<20)
+#define S3C64XX_UCON_TXBURST_8          (2<<20)
+#define S3C64XX_UCON_TXBURST_16         (3<<20)
+#define S3C64XX_UCON_TXBURST_MASK       (0xf<<20)
+#define S3C64XX_UCON_RXBURST_1          (0<<16)
+#define S3C64XX_UCON_RXBURST_4          (1<<16)
+#define S3C64XX_UCON_RXBURST_8          (2<<16)
+#define S3C64XX_UCON_RXBURST_16         (3<<16)
+#define S3C64XX_UCON_RXBURST_MASK       (0xf<<16)
+#define S3C64XX_UCON_TIMEOUT_SHIFT      (12)
+#define S3C64XX_UCON_TIMEOUT_MASK       (0xf<<12)
+#define S3C64XX_UCON_EMPTYINT_EN        (1<<11)
+#define S3C64XX_UCON_DMASUS_EN          (1<<10)
+#define S3C64XX_UCON_TXINT_LEVEL        (1<<9)
+#define S3C64XX_UCON_RXINT_LEVEL        (1<<8)
+#define S3C64XX_UCON_TIMEOUT_EN         (1<<7)
+#define S3C64XX_UCON_ERRINT_EN          (1<<6)
+#define S3C64XX_UCON_TXMODE_DMA         (2<<2)
+#define S3C64XX_UCON_TXMODE_CPU         (1<<2)
+#define S3C64XX_UCON_TXMODE_MASK        (3<<2)
+#define S3C64XX_UCON_RXMODE_DMA         (2<<0)
+#define S3C64XX_UCON_RXMODE_CPU         (1<<0)
+#define S3C64XX_UCON_RXMODE_MASK        (3<<0)
+
 #define S3C2410_UFCON_FIFOMODE   (1<<0)
 #define S3C2410_UFCON_TXTRIG0    (0<<6)
 #define S3C2410_UFCON_RXTRIG8    (1<<4)
 #define S3C2440_UFSTAT_TXMASK    (63<<8)
 #define S3C2440_UFSTAT_RXMASK    (63)
 
+#define S3C2410_UTRSTAT_TIMEOUT   (1<<3)
 #define S3C2410_UTRSTAT_TXE      (1<<2)
 #define S3C2410_UTRSTAT_TXFE     (1<<1)
 #define S3C2410_UTRSTAT_RXDR     (1<<0)
 #define S3C64XX_UINTM          0x38
 
 #define S3C64XX_UINTM_RXD      (0)
+#define S3C64XX_UINTM_ERROR     (1)
 #define S3C64XX_UINTM_TXD      (2)
 #define S3C64XX_UINTM_RXD_MSK  (1 << S3C64XX_UINTM_RXD)
+#define S3C64XX_UINTM_ERR_MSK   (1 << S3C64XX_UINTM_ERROR)
 #define S3C64XX_UINTM_TXD_MSK  (1 << S3C64XX_UINTM_TXD)
 
 /* Following are specific to S5PV210 */
index 7d66ae508e5cc08c0ba8f91b09e55224a565f04d..358a337af598564f16b03bfa68a696ff85d4513b 100644 (file)
 #include <linux/llist.h>
 
 
+/*
+ * Lock subclasses for tty locks
+ *
+ * TTY_LOCK_NORMAL is for normal ttys and master ptys.
+ * TTY_LOCK_SLAVE is for slave ptys only.
+ *
+ * Lock subclasses are necessary for handling nested locking with pty pairs.
+ * tty locks which use nested locking:
+ *
+ * legacy_mutex - Nested tty locks are necessary for releasing pty pairs.
+ *               The stable lock order is master pty first, then slave pty.
+ * termios_rwsem - The stable lock order is tty_buffer lock->termios_rwsem.
+ *                Subclassing this lock enables the slave pty to hold its
+ *                termios_rwsem when claiming the master tty_buffer lock.
+ * tty_buffer lock - slave ptys can claim nested buffer lock when handling
+ *                  signal chars. The stable lock order is slave pty, then
+ *                  master.
+ */
+
+enum {
+       TTY_LOCK_NORMAL = 0,
+       TTY_LOCK_SLAVE,
+};
 
 /*
  * (Note: the *_driver.minor_start values 1, 64, 128, 192 are
@@ -443,6 +466,7 @@ extern void tty_flush_to_ldisc(struct tty_struct *tty);
 extern void tty_buffer_free_all(struct tty_port *port);
 extern void tty_buffer_flush(struct tty_struct *tty, struct tty_ldisc *ld);
 extern void tty_buffer_init(struct tty_port *port);
+extern void tty_buffer_set_lock_subclass(struct tty_port *port);
 extern speed_t tty_termios_baud_rate(struct ktermios *termios);
 extern speed_t tty_termios_input_baud_rate(struct ktermios *termios);
 extern void tty_termios_encode_baud_rate(struct ktermios *termios,
@@ -467,7 +491,6 @@ static inline speed_t tty_get_baud_rate(struct tty_struct *tty)
 
 extern void tty_termios_copy_hw(struct ktermios *new, struct ktermios *old);
 extern int tty_termios_hw_change(struct ktermios *a, struct ktermios *b);
-extern int tty_set_termios(struct tty_struct *tty, struct ktermios *kt);
 
 extern struct tty_ldisc *tty_ldisc_ref(struct tty_struct *);
 extern void tty_ldisc_deref(struct tty_ldisc *);
index 057db7d2f4484b45a8cd4be42a5c10ec5509372e..f38c10ba3ff5ea01821ed375d25be2a403365e94 100644 (file)
 #ifndef VT_BUF_HAVE_RW
 #define scr_writew(val, addr) (*(addr) = (val))
 #define scr_readw(addr) (*(addr))
-#define scr_memcpyw(d, s, c) memcpy(d, s, c)
-#define scr_memmovew(d, s, c) memmove(d, s, c)
-#define VT_BUF_HAVE_MEMCPYW
-#define VT_BUF_HAVE_MEMMOVEW
 #endif
 
 #ifndef VT_BUF_HAVE_MEMSETW
index c17218094f187511710d4f25f341216c531ca111..b2122813f18a2a4c5b55b6950f6bcea696d347f4 100644 (file)
@@ -55,7 +55,8 @@
 #define PORT_ALTR_16550_F64 27 /* Altera 16550 UART with 64 FIFOs */
 #define PORT_ALTR_16550_F128 28 /* Altera 16550 UART with 128 FIFOs */
 #define PORT_RT2880    29      /* Ralink RT2880 internal UART */
-#define PORT_MAX_8250  29      /* max port ID */
+#define PORT_16550A_FSL64 30   /* Freescale 16550 UART with 64 FIFOs */
+#define PORT_MAX_8250  30      /* max port ID */
 
 /*
  * ARM specific type numbers.  These are not currently guaranteed
 /* MESON */
 #define PORT_MESON     109
 
+/* Conexant Digicolor */
+#define PORT_DIGICOLOR 110
+
+/* SPRD SERIAL  */
+#define PORT_SPRD      111
+
+/* Cris v10 / v32 SoC */
+#define PORT_CRIS      112
+
 #endif /* _UAPILINUX_SERIAL_CORE_H */
index 53af3b790129ee61d195288be4d278f67ca738e7..00adb01fa5f38f53858ea2bb265f2b040948f4cf 100644 (file)
@@ -86,7 +86,8 @@
 #define UART_FCR6_T_TRIGGER_8  0x10 /* Mask for transmit trigger set at 8 */
 #define UART_FCR6_T_TRIGGER_24  0x20 /* Mask for transmit trigger set at 24 */
 #define UART_FCR6_T_TRIGGER_30 0x30 /* Mask for transmit trigger set at 30 */
-#define UART_FCR7_64BYTE       0x20 /* Go into 64 byte mode (TI16C750) */
+#define UART_FCR7_64BYTE       0x20 /* Go into 64 byte mode (TI16C750 and
+                                       some Freescale UARTs) */
 
 #define UART_FCR_R_TRIG_SHIFT          6
 #define UART_FCR_R_TRIG_BITS(x)                \