浏览代码

Merge tag 'tty-3.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty

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
  ...
Linus Torvalds 10 年之前
父节点
当前提交
a9724125ad
共有 73 个文件被更改,包括 4523 次插入1910 次删除
  1. 11 0
      Documentation/devicetree/bindings/arm/sprd.txt
  2. 27 0
      Documentation/devicetree/bindings/serial/digicolor-usart.txt
  3. 2 2
      Documentation/devicetree/bindings/serial/sirf-uart.txt
  4. 7 0
      Documentation/devicetree/bindings/serial/sprd-uart.txt
  5. 2 0
      Documentation/devicetree/bindings/vendor-prefixes.txt
  6. 12 0
      Documentation/kernel-parameters.txt
  7. 1 0
      arch/arm/boot/dts/exynos4210-origen.dts
  8. 1 0
      arch/arm/boot/dts/exynos4210-smdkv310.dts
  9. 1 0
      arch/arm/boot/dts/exynos4210-trats.dts
  10. 1 0
      arch/arm/boot/dts/exynos4210-universal_c210.dts
  11. 4 0
      arch/arm/boot/dts/exynos4412-odroid-common.dtsi
  12. 1 0
      arch/arm/boot/dts/exynos4412-origen.dts
  13. 1 0
      arch/arm/boot/dts/exynos4412-smdk4412.dts
  14. 4 0
      arch/arm/boot/dts/exynos4412-tiny4412.dts
  15. 1 0
      arch/arm/boot/dts/exynos4412-trats2.dts
  16. 0 3
      arch/x86/include/asm/intel-mid.h
  17. 166 21
      arch/x86/kernel/early_printk.c
  18. 0 2
      arch/x86/platform/intel-mid/device_libs/Makefile
  19. 0 35
      arch/x86/platform/intel-mid/device_libs/platform_max3111.c
  20. 4 216
      arch/x86/platform/intel-mid/early_printk_intel_mid.c
  21. 2 13
      drivers/bluetooth/hci_ath.c
  22. 1 1
      drivers/pnp/driver.c
  23. 1 1
      drivers/tty/amiserial.c
  24. 0 1
      drivers/tty/ehv_bytechan.c
  25. 1 1
      drivers/tty/isicom.c
  26. 149 105
      drivers/tty/n_tty.c
  27. 16 15
      drivers/tty/pty.c
  28. 2 2
      drivers/tty/rocket.c
  29. 162 68
      drivers/tty/serial/8250/8250_core.c
  30. 5 1
      drivers/tty/serial/8250/8250_dma.c
  31. 11 0
      drivers/tty/serial/8250/8250_dw.c
  32. 9 4
      drivers/tty/serial/8250/8250_early.c
  33. 76 12
      drivers/tty/serial/8250/8250_omap.c
  34. 2 2
      drivers/tty/serial/8250/8250_pci.c
  35. 7 1
      drivers/tty/serial/8250/8250_pnp.c
  36. 19 0
      drivers/tty/serial/8250/Kconfig
  37. 44 10
      drivers/tty/serial/Kconfig
  38. 3 1
      drivers/tty/serial/Makefile
  39. 1 0
      drivers/tty/serial/altera_jtaguart.c
  40. 1 0
      drivers/tty/serial/altera_uart.c
  41. 101 52
      drivers/tty/serial/atmel_serial.c
  42. 560 0
      drivers/tty/serial/digicolor-usart.c
  43. 996 0
      drivers/tty/serial/etraxfs-uart.c
  44. 77 58
      drivers/tty/serial/fsl_lpuart.c
  45. 154 36
      drivers/tty/serial/imx.c
  46. 3 4
      drivers/tty/serial/mcf.c
  47. 109 46
      drivers/tty/serial/men_z135_uart.c
  48. 0 909
      drivers/tty/serial/mrst_max3110.c
  49. 0 61
      drivers/tty/serial/mrst_max3110.h
  50. 4 23
      drivers/tty/serial/msm_serial.c
  51. 20 35
      drivers/tty/serial/mxs-auart.c
  52. 13 0
      drivers/tty/serial/of_serial.c
  53. 24 13
      drivers/tty/serial/omap-serial.c
  54. 662 18
      drivers/tty/serial/samsung.c
  55. 42 0
      drivers/tty/serial/samsung.h
  56. 46 65
      drivers/tty/serial/serial_core.c
  57. 17 8
      drivers/tty/serial/sh-sci.c
  58. 20 20
      drivers/tty/serial/sirfsoc_uart.c
  59. 2 2
      drivers/tty/serial/sirfsoc_uart.h
  60. 793 0
      drivers/tty/serial/sprd_serial.c
  61. 6 4
      drivers/tty/serial/xilinx_uartps.c
  62. 6 0
      drivers/tty/tty_buffer.c
  63. 1 2
      drivers/tty/tty_ioctl.c
  64. 2 16
      drivers/tty/tty_mutex.c
  65. 9 9
      drivers/tty/vt/vt.c
  66. 10 2
      include/linux/pnp.h
  67. 4 0
      include/linux/serial_8250.h
  68. 19 3
      include/linux/serial_core.h
  69. 28 0
      include/linux/serial_s3c.h
  70. 24 1
      include/linux/tty.h
  71. 0 4
      include/linux/vt_buffer.h
  72. 11 1
      include/uapi/linux/serial_core.h
  73. 2 1
      include/uapi/linux/serial_reg.h

+ 11 - 0
Documentation/devicetree/bindings/arm/sprd.txt

@@ -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";

+ 27 - 0
Documentation/devicetree/bindings/serial/digicolor-usart.txt

@@ -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>;
+	};

+ 2 - 2
Documentation/devicetree/bindings/serial/sirf-uart.txt

@@ -2,7 +2,7 @@
 
 
 Required properties:
 Required properties:
 - compatible : Should be "sirf,prima2-uart", "sirf, prima2-usp-uart",
 - 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.
 		uart located in BT module and used for BT.
 - reg : Offset and length of the register set for the device
 - reg : Offset and length of the register set for the device
 - interrupts : Should contain uart interrupt
 - interrupts : Should contain uart interrupt
@@ -37,7 +37,7 @@ usp@b0090000 {
 for uart use in BT module,
 for uart use in BT module,
 uart6: uart@11000000 {
 uart6: uart@11000000 {
 	cell-index = <6>;
 	cell-index = <6>;
-	compatible = "sirf,marco-bt-uart", "sirf,marco-uart";
+	compatible = "sirf,atlas7-bt-uart", "sirf,atlas7-uart";
 	reg = <0x11000000 0x1000>;
 	reg = <0x11000000 0x1000>;
 	interrupts = <0 100 0>;
 	interrupts = <0 100 0>;
 	clocks = <&clks 138>, <&clks 140>, <&clks 141>;
 	clocks = <&clks 138>, <&clks 140>, <&clks 141>;

+ 7 - 0
Documentation/devicetree/bindings/serial/sprd-uart.txt

@@ -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.

+ 2 - 0
Documentation/devicetree/bindings/vendor-prefixes.txt

@@ -37,6 +37,7 @@ chrp	Common Hardware Reference Platform
 chunghwa	Chunghwa Picture Tubes Ltd.
 chunghwa	Chunghwa Picture Tubes Ltd.
 cirrus	Cirrus Logic, Inc.
 cirrus	Cirrus Logic, Inc.
 cnm	Chips&Media, Inc.
 cnm	Chips&Media, Inc.
+cnxt	Conexant Systems, Inc.
 cortina	Cortina Systems, Inc.
 cortina	Cortina Systems, Inc.
 cosmic	Cosmic Circuits
 cosmic	Cosmic Circuits
 crystalfontz	Crystalfontz America, Inc.
 crystalfontz	Crystalfontz America, Inc.
@@ -162,6 +163,7 @@ snps	Synopsys, Inc.
 solidrun	SolidRun
 solidrun	SolidRun
 sony	Sony Corporation
 sony	Sony Corporation
 spansion	Spansion Inc.
 spansion	Spansion Inc.
+sprd	Spreadtrum Communications Inc.
 st	STMicroelectronics
 st	STMicroelectronics
 ste	ST-Ericsson
 ste	ST-Ericsson
 stericsson	ST-Ericsson
 stericsson	ST-Ericsson

+ 12 - 0
Documentation/kernel-parameters.txt

@@ -970,6 +970,18 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
 
 
 		smh	Use ARM semihosting calls for early console.
 		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=	[X86,SH,BLACKFIN,ARM,M68k]
 			earlyprintk=vga
 			earlyprintk=vga
 			earlyprintk=efi
 			earlyprintk=efi

+ 1 - 0
arch/arm/boot/dts/exynos4210-origen.dts

@@ -31,6 +31,7 @@
 
 
 	chosen {
 	chosen {
 		bootargs ="root=/dev/ram0 rw ramdisk=8192 initrd=0x41000000,8M console=ttySAC2,115200 init=/linuxrc";
 		bootargs ="root=/dev/ram0 rw ramdisk=8192 initrd=0x41000000,8M console=ttySAC2,115200 init=/linuxrc";
+		stdout-path = &serial_2;
 	};
 	};
 
 
 	regulators {
 	regulators {

+ 1 - 0
arch/arm/boot/dts/exynos4210-smdkv310.dts

@@ -27,6 +27,7 @@
 
 
 	chosen {
 	chosen {
 		bootargs = "root=/dev/ram0 rw ramdisk=8192 initrd=0x41000000,8M console=ttySAC1,115200 init=/linuxrc";
 		bootargs = "root=/dev/ram0 rw ramdisk=8192 initrd=0x41000000,8M console=ttySAC1,115200 init=/linuxrc";
+		stdout-path = &serial_1;
 	};
 	};
 
 
 	sdhci@12530000 {
 	sdhci@12530000 {

+ 1 - 0
arch/arm/boot/dts/exynos4210-trats.dts

@@ -28,6 +28,7 @@
 
 
 	chosen {
 	chosen {
 		bootargs = "console=ttySAC2,115200N8 root=/dev/mmcblk0p5 rootwait earlyprintk panic=5";
 		bootargs = "console=ttySAC2,115200N8 root=/dev/mmcblk0p5 rootwait earlyprintk panic=5";
+		stdout-path = &serial_2;
 	};
 	};
 
 
 	regulators {
 	regulators {

+ 1 - 0
arch/arm/boot/dts/exynos4210-universal_c210.dts

@@ -26,6 +26,7 @@
 
 
 	chosen {
 	chosen {
 		bootargs = "console=ttySAC2,115200N8 root=/dev/mmcblk0p5 rw rootwait earlyprintk panic=5 maxcpus=1";
 		bootargs = "console=ttySAC2,115200N8 root=/dev/mmcblk0p5 rw rootwait earlyprintk panic=5 maxcpus=1";
+		stdout-path = &serial_2;
 	};
 	};
 
 
 	sysram@02020000 {
 	sysram@02020000 {

+ 4 - 0
arch/arm/boot/dts/exynos4412-odroid-common.dtsi

@@ -12,6 +12,10 @@
 #include "exynos4412.dtsi"
 #include "exynos4412.dtsi"
 
 
 / {
 / {
+	chosen {
+		stdout-path = &serial_1;
+	};
+
 	firmware@0204F000 {
 	firmware@0204F000 {
 		compatible = "samsung,secure-firmware";
 		compatible = "samsung,secure-firmware";
 		reg = <0x0204F000 0x1000>;
 		reg = <0x0204F000 0x1000>;

+ 1 - 0
arch/arm/boot/dts/exynos4412-origen.dts

@@ -26,6 +26,7 @@
 
 
 	chosen {
 	chosen {
 		bootargs ="console=ttySAC2,115200";
 		bootargs ="console=ttySAC2,115200";
+		stdout-path = &serial_2;
 	};
 	};
 
 
 	firmware@0203F000 {
 	firmware@0203F000 {

+ 1 - 0
arch/arm/boot/dts/exynos4412-smdk4412.dts

@@ -25,6 +25,7 @@
 
 
 	chosen {
 	chosen {
 		bootargs ="root=/dev/ram0 rw ramdisk=8192 initrd=0x41000000,8M console=ttySAC1,115200 init=/linuxrc";
 		bootargs ="root=/dev/ram0 rw ramdisk=8192 initrd=0x41000000,8M console=ttySAC1,115200 init=/linuxrc";
+		stdout-path = &serial_1;
 	};
 	};
 
 
 	g2d@10800000 {
 	g2d@10800000 {

+ 4 - 0
arch/arm/boot/dts/exynos4412-tiny4412.dts

@@ -18,6 +18,10 @@
 	model = "FriendlyARM TINY4412 board based on Exynos4412";
 	model = "FriendlyARM TINY4412 board based on Exynos4412";
 	compatible = "friendlyarm,tiny4412", "samsung,exynos4412", "samsung,exynos4";
 	compatible = "friendlyarm,tiny4412", "samsung,exynos4412", "samsung,exynos4";
 
 
+	chosen {
+		stdout-path = &serial_0;
+	};
+
 	memory {
 	memory {
 		reg = <0x40000000 0x40000000>;
 		reg = <0x40000000 0x40000000>;
 	};
 	};

+ 1 - 0
arch/arm/boot/dts/exynos4412-trats2.dts

@@ -32,6 +32,7 @@
 
 
 	chosen {
 	chosen {
 		bootargs = "console=ttySAC2,115200N8 root=/dev/mmcblk0p5 rootwait earlyprintk panic=5";
 		bootargs = "console=ttySAC2,115200N8 root=/dev/mmcblk0p5 rootwait earlyprintk panic=5";
+		stdout-path = &serial_2;
 	};
 	};
 
 
 	firmware@0204F000 {
 	firmware@0204F000 {

+ 0 - 3
arch/x86/include/asm/intel-mid.h

@@ -136,9 +136,6 @@ extern enum intel_mid_timer_options intel_mid_timer_options;
 #define SFI_MTMR_MAX_NUM 8
 #define SFI_MTMR_MAX_NUM 8
 #define SFI_MRTC_MAX	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 struct console early_hsu_console;
 extern void hsu_early_console_init(const char *);
 extern void hsu_early_console_init(const char *);
 
 

+ 166 - 21
arch/x86/kernel/early_printk.c

@@ -19,6 +19,7 @@
 #include <linux/usb/ehci_def.h>
 #include <linux/usb/ehci_def.h>
 #include <linux/efi.h>
 #include <linux/efi.h>
 #include <asm/efi.h>
 #include <asm/efi.h>
+#include <asm/pci_x86.h>
 
 
 /* Simple VGA output */
 /* Simple VGA output */
 #define VGABASE		(__ISA_IO_base + 0xb8000)
 #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 */
 /* 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
 #define XMTRDY          0x20
 
 
@@ -94,13 +95,40 @@ static int early_serial_base = 0x3f8;  /* ttyS0 */
 #define DLL             0       /*  Divisor Latch Low         */
 #define DLL             0       /*  Divisor Latch Low         */
 #define DLH             1       /*  Divisor latch High        */
 #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)
 static int early_serial_putc(unsigned char ch)
 {
 {
 	unsigned timeout = 0xffff;
 	unsigned timeout = 0xffff;
 
 
-	while ((inb(early_serial_base + LSR) & XMTRDY) == 0 && --timeout)
+	while ((serial_in(early_serial_base, LSR) & XMTRDY) == 0 && --timeout)
 		cpu_relax();
 		cpu_relax();
-	outb(ch, early_serial_base + TXR);
+	serial_out(early_serial_base, TXR, ch);
 	return timeout ? 0 : -1;
 	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
 #define DEFAULT_BAUD 9600
 
 
 static __init void early_serial_init(char *s)
 static __init void early_serial_init(char *s)
 {
 {
-	unsigned char c;
 	unsigned divisor;
 	unsigned divisor;
-	unsigned baud = DEFAULT_BAUD;
+	unsigned long baud = DEFAULT_BAUD;
 	char *e;
 	char *e;
 
 
 	if (*s == ',')
 	if (*s == ',')
@@ -145,24 +188,124 @@ static __init void early_serial_init(char *s)
 			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) {
 	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;
 			baud = DEFAULT_BAUD;
 	}
 	}
 
 
+	/* Convert from baud to divisor value */
 	divisor = 115200 / baud;
 	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 = {
 static struct console early_serial_console = {
 	.name =		"earlyser",
 	.name =		"earlyser",
@@ -210,6 +353,13 @@ static int __init setup_early_printk(char *buf)
 			early_serial_init(buf + 4);
 			early_serial_init(buf + 4);
 			early_console_register(&early_serial_console, keep);
 			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) &&
 		if (!strncmp(buf, "vga", 3) &&
 		    boot_params.screen_info.orig_video_isVGA == 1) {
 		    boot_params.screen_info.orig_video_isVGA == 1) {
 			max_xpos = boot_params.screen_info.orig_video_cols;
 			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);
 			early_console_register(&xenboot_console, keep);
 #endif
 #endif
 #ifdef CONFIG_EARLY_PRINTK_INTEL_MID
 #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)) {
 		if (!strncmp(buf, "hsu", 3)) {
 			hsu_early_console_init(buf + 3);
 			hsu_early_console_init(buf + 3);
 			early_console_register(&early_hsu_console, keep);
 			early_console_register(&early_hsu_console, keep);

+ 0 - 2
arch/x86/platform/intel-mid/device_libs/Makefile

@@ -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_INPUT_BMA150)) += platform_bma023.o
 obj-$(subst m,y,$(CONFIG_GPIO_PCA953X)) += platform_tca6416.o
 obj-$(subst m,y,$(CONFIG_GPIO_PCA953X)) += platform_tca6416.o
 obj-$(subst m,y,$(CONFIG_DRM_MEDFIELD)) += platform_tc35876x.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
 # MISC Devices
 obj-$(subst m,y,$(CONFIG_KEYBOARD_GPIO)) += platform_gpio_keys.o
 obj-$(subst m,y,$(CONFIG_KEYBOARD_GPIO)) += platform_gpio_keys.o
 obj-$(subst m,y,$(CONFIG_INTEL_MID_WATCHDOG)) += platform_wdt.o
 obj-$(subst m,y,$(CONFIG_INTEL_MID_WATCHDOG)) += platform_wdt.o

+ 0 - 35
arch/x86/platform/intel-mid/device_libs/platform_max3111.c

@@ -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);

+ 4 - 216
arch/x86/platform/intel-mid/early_printk_intel_mid.c

@@ -10,15 +10,13 @@
  */
  */
 
 
 /*
 /*
- * 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_reg.h>
 #include <linux/serial_mfd.h>
 #include <linux/serial_mfd.h>
-#include <linux/kmsg_dump.h>
 #include <linux/console.h>
 #include <linux/console.h>
 #include <linux/kernel.h>
 #include <linux/kernel.h>
 #include <linux/delay.h>
 #include <linux/delay.h>
@@ -28,216 +26,6 @@
 #include <asm/pgtable.h>
 #include <asm/pgtable.h>
 #include <asm/intel-mid.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
  * Following is the early console based on Medfield HSU (High
  * Speed UART) device.
  * Speed UART) device.
@@ -259,7 +47,7 @@ void hsu_early_console_init(const char *s)
 		port = clamp_val(port, 0, 2);
 		port = clamp_val(port, 0, 2);
 
 
 	paddr = HSU_PORT_BASE + port * 0x80;
 	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 */
 	/* Disable FIFO */
 	writeb(0x0, phsu + UART_FCR);
 	writeb(0x0, phsu + UART_FCR);

+ 2 - 13
drivers/bluetooth/hci_ath.c

@@ -51,33 +51,22 @@ struct ath_struct {
 
 
 static int ath_wakeup_ar3k(struct tty_struct *tty)
 static int ath_wakeup_ar3k(struct tty_struct *tty)
 {
 {
-	struct ktermios ktermios;
 	int status = tty->driver->ops->tiocmget(tty);
 	int status = tty->driver->ops->tiocmget(tty);
 
 
 	if (status & TIOCM_CTS)
 	if (status & TIOCM_CTS)
 		return status;
 		return status;
 
 
-	/* Disable Automatic RTSCTS */
-	ktermios = tty->termios;
-	ktermios.c_cflag &= ~CRTSCTS;
-	tty_set_termios(tty, &ktermios);
-
 	/* Clear RTS first */
 	/* Clear RTS first */
-	status = tty->driver->ops->tiocmget(tty);
+	tty->driver->ops->tiocmget(tty);
 	tty->driver->ops->tiocmset(tty, 0x00, TIOCM_RTS);
 	tty->driver->ops->tiocmset(tty, 0x00, TIOCM_RTS);
 	mdelay(20);
 	mdelay(20);
 
 
 	/* Set RTS, wake up board */
 	/* Set RTS, wake up board */
-	status = tty->driver->ops->tiocmget(tty);
+	tty->driver->ops->tiocmget(tty);
 	tty->driver->ops->tiocmset(tty, TIOCM_RTS, 0x00);
 	tty->driver->ops->tiocmset(tty, TIOCM_RTS, 0x00);
 	mdelay(20);
 	mdelay(20);
 
 
 	status = tty->driver->ops->tiocmget(tty);
 	status = tty->driver->ops->tiocmget(tty);
-
-	/* Enable Automatic RTSCTS */
-	ktermios.c_cflag |= CRTSCTS;
-	status = tty_set_termios(tty, &ktermios);
-
 	return status;
 	return status;
 }
 }
 
 

+ 1 - 1
drivers/pnp/driver.c

@@ -182,7 +182,7 @@ static int __pnp_bus_suspend(struct device *dev, pm_message_t state)
 			return error;
 			return error;
 	}
 	}
 
 
-	if (pnp_dev->protocol->suspend)
+	if (pnp_can_suspend(pnp_dev))
 		pnp_dev->protocol->suspend(pnp_dev, state);
 		pnp_dev->protocol->suspend(pnp_dev, state);
 	return 0;
 	return 0;
 }
 }

+ 1 - 1
drivers/tty/amiserial.c

@@ -931,7 +931,7 @@ static void rs_send_xchar(struct tty_struct *tty, char ch)
 	struct serial_state *info = tty->driver_data;
 	struct serial_state *info = tty->driver_data;
         unsigned long flags;
         unsigned long flags;
 
 
-	if (serial_paranoia_check(info, tty->name, "rs_send_char"))
+	if (serial_paranoia_check(info, tty->name, "rs_send_xchar"))
 		return;
 		return;
 
 
 	info->x_char = ch;
 	info->x_char = ch;

+ 0 - 1
drivers/tty/ehv_bytechan.c

@@ -112,7 +112,6 @@ static void disable_tx_interrupt(struct ehv_bc_data *bc)
 static int find_console_handle(void)
 static int find_console_handle(void)
 {
 {
 	struct device_node *np = of_stdout;
 	struct device_node *np = of_stdout;
-	const char *sprop = NULL;
 	const uint32_t *iprop;
 	const uint32_t *iprop;
 
 
 	/* We don't care what the aliased node is actually called.  We only
 	/* We don't care what the aliased node is actually called.  We only

+ 1 - 1
drivers/tty/isicom.c

@@ -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(0x8000 | ((port->channel) << (card->shift_count)) | 0x3, base);
 	outw((length & 0xff) << 8 | 0x00, base);
 	outw((length & 0xff) << 8 | 0x00, base);
-	outw((length & 0xff00), base);
+	outw((length & 0xff00u), base);
 	InterruptTheCard(base);
 	InterruptTheCard(base);
 
 
 	unlock_card(card);
 	unlock_card(card);

+ 149 - 105
drivers/tty/n_tty.c

@@ -90,6 +90,7 @@
 struct n_tty_data {
 struct n_tty_data {
 	/* producer-published */
 	/* producer-published */
 	size_t read_head;
 	size_t read_head;
+	size_t commit_head;
 	size_t canon_head;
 	size_t canon_head;
 	size_t echo_head;
 	size_t echo_head;
 	size_t echo_commit;
 	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);
 	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
  *	@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
  *	Caller holds exclusive termios_rwsem
  *	   or
  *	   or
@@ -198,12 +174,12 @@ static int receive_room(struct tty_struct *tty)
  *		holds non-exclusive termios_rwsem
  *		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;
 	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;
 		ldata->no_room = 0;
 
 
 		WARN_RATELIMIT(tty->port->itty == NULL,
 		WARN_RATELIMIT(tty->port->itty == NULL,
@@ -224,7 +200,7 @@ static ssize_t chars_in_buffer(struct tty_struct *tty)
 	ssize_t n = 0;
 	ssize_t n = 0;
 
 
 	if (!ldata->icanon)
 	if (!ldata->icanon)
-		n = read_cnt(ldata);
+		n = ldata->commit_head - ldata->read_tail;
 	else
 	else
 		n = ldata->canon_head - ldata->read_tail;
 		n = ldata->canon_head - ldata->read_tail;
 	return n;
 	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)
 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
 	 * Check the remaining room for the input canonicalization
 	 * mode.  We don't want to throttle the driver if we're in
 	 * mode.  We don't want to throttle the driver if we're in
 	 * canonical mode and don't have a newline yet!
 	 * canonical mode and don't have a newline yet!
 	 */
 	 */
+	if (ldata->icanon && ldata->canon_head == ldata->read_tail)
+		return;
+
 	while (1) {
 	while (1) {
 		int throttled;
 		int throttled;
 		tty_set_flow_change(tty, TTY_THROTTLE_SAFE);
 		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;
 			break;
 		throttled = tty_throttle_safe(tty);
 		throttled = tty_throttle_safe(tty);
 		if (!throttled)
 		if (!throttled)
@@ -274,7 +253,7 @@ static void n_tty_check_unthrottle(struct tty_struct *tty)
 			return;
 			return;
 		if (!tty->count)
 		if (!tty->count)
 			return;
 			return;
-		n_tty_set_room(tty);
+		n_tty_kick_worker(tty);
 		n_tty_write_wakeup(tty->link);
 		n_tty_write_wakeup(tty->link);
 		if (waitqueue_active(&tty->link->write_wait))
 		if (waitqueue_active(&tty->link->write_wait))
 			wake_up_interruptible_poll(&tty->link->write_wait, POLLOUT);
 			wake_up_interruptible_poll(&tty->link->write_wait, POLLOUT);
@@ -296,7 +275,7 @@ static void n_tty_check_unthrottle(struct tty_struct *tty)
 			break;
 			break;
 		if (!tty->count)
 		if (!tty->count)
 			break;
 			break;
-		n_tty_set_room(tty);
+		n_tty_kick_worker(tty);
 		unthrottled = tty_unthrottle_safe(tty);
 		unthrottled = tty_unthrottle_safe(tty);
 		if (!unthrottled)
 		if (!unthrottled)
 			break;
 			break;
@@ -313,10 +292,6 @@ static void n_tty_check_unthrottle(struct tty_struct *tty)
  *
  *
  *	n_tty_receive_buf()/producer path:
  *	n_tty_receive_buf()/producer path:
  *		caller holds non-exclusive termios_rwsem
  *		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)
 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->read_head = ldata->canon_head = ldata->read_tail = 0;
 	ldata->echo_head = ldata->echo_tail = ldata->echo_commit = 0;
 	ldata->echo_head = ldata->echo_tail = ldata->echo_commit = 0;
+	ldata->commit_head = 0;
 	ldata->echo_mark = 0;
 	ldata->echo_mark = 0;
 	ldata->line_start = 0;
 	ldata->line_start = 0;
 
 
@@ -379,7 +355,7 @@ static void n_tty_flush_buffer(struct tty_struct *tty)
 {
 {
 	down_write(&tty->termios_rwsem);
 	down_write(&tty->termios_rwsem);
 	reset_buffer_flags(tty->disc_data);
 	reset_buffer_flags(tty->disc_data);
-	n_tty_set_room(tty);
+	n_tty_kick_worker(tty);
 
 
 	if (tty->link)
 	if (tty->link)
 		n_tty_packet_mode_flush(tty);
 		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:
  *	n_tty_receive_buf()/producer path:
  *		caller holds non-exclusive termios_rwsem
  *		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)
 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 when a signal is being sent due to terminal input.
  *	Called from the driver receive_buf path so serialized.
  *	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
  *	Locking: ctrl_lock
  */
  */
 
 
 static void isig(int sig, struct tty_struct *tty)
 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);
 	struct pid *tty_pgrp = tty_get_pgrp(tty);
 	if (tty_pgrp) {
 	if (tty_pgrp) {
 		kill_pgrp(tty_pgrp, sig, 1);
 		kill_pgrp(tty_pgrp, sig, 1);
 		put_pid(tty_pgrp);
 		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:
  *	n_tty_receive_buf()/producer path:
  *		caller holds non-exclusive termios_rwsem
  *		caller holds non-exclusive termios_rwsem
- *		publishes read_head via put_tty_queue()
  *
  *
  *	Note: may get exclusive termios_rwsem if flushing input buffer
  *	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;
 		return;
 	if (I_BRKINT(tty)) {
 	if (I_BRKINT(tty)) {
 		isig(SIGINT, 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;
 		return;
 	}
 	}
 	if (I_PARMRK(tty)) {
 	if (I_PARMRK(tty)) {
@@ -1209,7 +1202,6 @@ static void n_tty_receive_overrun(struct tty_struct *tty)
  *
  *
  *	n_tty_receive_buf()/producer path:
  *	n_tty_receive_buf()/producer path:
  *		caller holds non-exclusive termios_rwsem
  *		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)
 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
 static void
 n_tty_receive_signal_char(struct tty_struct *tty, int signal, unsigned char c)
 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))
 	if (I_IXON(tty))
 		start_tty(tty);
 		start_tty(tty);
 	if (L_ECHO(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);
 		commit_echoes(tty);
 	} else
 	} else
 		process_echoes(tty);
 		process_echoes(tty);
-	isig(signal, tty);
 	return;
 	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:
  *	n_tty_receive_buf()/producer path:
  *		caller holds non-exclusive termios_rwsem
  *		caller holds non-exclusive termios_rwsem
  *		publishes canon_head if canonical mode is active
  *		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
  *	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:
 handle_newline:
 			set_bit(ldata->read_head & (N_TTY_BUF_SIZE - 1), ldata->read_flags);
 			set_bit(ldata->read_head & (N_TTY_BUF_SIZE - 1), ldata->read_flags);
 			put_tty_queue(c, ldata);
 			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);
 			kill_fasync(&tty->fasync, SIGIO, POLL_IN);
 			if (waitqueue_active(&tty->read_wait))
 			if (waitqueue_active(&tty->read_wait))
 				wake_up_interruptible_poll(&tty->read_wait, POLLIN);
 				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_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
 static void
 n_tty_receive_buf_real_raw(struct tty_struct *tty, const unsigned char *cp,
 n_tty_receive_buf_real_raw(struct tty_struct *tty, const unsigned char *cp,
 			   char *fp, int count)
 			   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;
 	size_t n, head;
 
 
 	head = ldata->read_head & (N_TTY_BUF_SIZE - 1);
 	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);
 	memcpy(read_buf_addr(ldata, head), cp, n);
 	ldata->read_head += n;
 	ldata->read_head += n;
 	cp += n;
 	cp += n;
 	count -= n;
 	count -= n;
 
 
 	head = ldata->read_head & (N_TTY_BUF_SIZE - 1);
 	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);
 	memcpy(read_buf_addr(ldata, head), cp, n);
 	ldata->read_head += 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);
 			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);
 		kill_fasync(&tty->fasync, SIGIO, POLL_IN);
 		if (waitqueue_active(&tty->read_wait))
 		if (waitqueue_active(&tty->read_wait))
 			wake_up_interruptible_poll(&tty->read_wait, POLLIN);
 			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
 static int
 n_tty_receive_buf_common(struct tty_struct *tty, const unsigned char *cp,
 n_tty_receive_buf_common(struct tty_struct *tty, const unsigned char *cp,
 			 char *fp, int count, int flow)
 			 char *fp, int count, int flow)
 {
 {
 	struct n_tty_data *ldata = tty->disc_data;
 	struct n_tty_data *ldata = tty->disc_data;
-	int room, n, rcvd = 0;
+	int room, n, rcvd = 0, overflow;
 
 
 	down_read(&tty->termios_rwsem);
 	down_read(&tty->termios_rwsem);
 
 
 	while (1) {
 	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);
 		n = min(count, room);
-		if (!n) {
-			if (flow && !room)
-				ldata->no_room = 1;
+		if (!n)
 			break;
 			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;
 		cp += n;
 		if (fp)
 		if (fp)
 			fp += n;
 			fp += n;
@@ -1710,7 +1741,17 @@ n_tty_receive_buf_common(struct tty_struct *tty, const unsigned char *cp,
 	}
 	}
 
 
 	tty->receive_room = room;
 	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);
 	up_read(&tty->termios_rwsem);
 
 
 	return rcvd;
 	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->canon_head = ldata->read_head;
 			ldata->push = 1;
 			ldata->push = 1;
 		}
 		}
+		ldata->commit_head = ldata->read_head;
 		ldata->erasing = 0;
 		ldata->erasing = 0;
 		ldata->lnext = 0;
 		ldata->lnext = 0;
 	}
 	}
@@ -1817,7 +1859,6 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old)
 		else
 		else
 			ldata->real_raw = 0;
 			ldata->real_raw = 0;
 	}
 	}
-	n_tty_set_room(tty);
 	/*
 	/*
 	 * Fix tty hang when I_IXON(tty) is cleared, but the tty
 	 * Fix tty hang when I_IXON(tty) is cleared, but the tty
 	 * been stopped by STOP_CHAR(tty) before it.
 	 * 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))
 	if (ldata->icanon && !L_EXTPROC(tty))
 		return ldata->canon_head != ldata->read_tail;
 		return ldata->canon_head != ldata->read_tail;
 	else
 	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;
 	int retval;
 	size_t n;
 	size_t n;
 	bool is_eof;
 	bool is_eof;
+	size_t head = smp_load_acquire(&ldata->commit_head);
 	size_t tail = ldata->read_tail & (N_TTY_BUF_SIZE - 1);
 	size_t tail = ldata->read_tail & (N_TTY_BUF_SIZE - 1);
 
 
 	retval = 0;
 	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);
 	n = min(*nr, n);
 	if (n) {
 	if (n) {
 		retval = copy_to_user(*b, read_buf_addr(ldata, tail), 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);
 		is_eof = n == 1 && read_buf(ldata, tail) == EOF_CHAR(tty);
 		tty_audit_add_data(tty, read_buf_addr(ldata, tail), n,
 		tty_audit_add_data(tty, read_buf_addr(ldata, tail), n,
 				ldata->icanon);
 				ldata->icanon);
-		ldata->read_tail += n;
+		smp_store_release(&ldata->read_tail, ldata->read_tail + n);
 		/* Turn single EOF into zero-length read */
 		/* 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;
 			n = 0;
 		*b += n;
 		*b += n;
 		*nr -= n;
 		*nr -= n;
@@ -1993,7 +2036,7 @@ static int canon_copy_from_read_buf(struct tty_struct *tty,
 	bool eof_push = 0;
 	bool eof_push = 0;
 
 
 	/* N.B. avoid overrun if nr == 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)
 	if (!n)
 		return 0;
 		return 0;
 
 
@@ -2043,8 +2086,7 @@ static int canon_copy_from_read_buf(struct tty_struct *tty,
 
 
 	if (found)
 	if (found)
 		clear_bit(eol, ldata->read_flags);
 		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 (found) {
 		if (!ldata->push)
 		if (!ldata->push)
@@ -2130,6 +2172,7 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
 	ssize_t retval = 0;
 	ssize_t retval = 0;
 	long timeout;
 	long timeout;
 	int packet;
 	int packet;
+	size_t tail;
 
 
 	c = job_control(tty, file);
 	c = job_control(tty, file);
 	if (c < 0)
 	if (c < 0)
@@ -2166,6 +2209,7 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
 	}
 	}
 
 
 	packet = tty->packet;
 	packet = tty->packet;
+	tail = ldata->read_tail;
 
 
 	add_wait_queue(&tty->read_wait, &wait);
 	add_wait_queue(&tty->read_wait, &wait);
 	while (nr) {
 	while (nr) {
@@ -2208,7 +2252,6 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
 				retval = -ERESTARTSYS;
 				retval = -ERESTARTSYS;
 				break;
 				break;
 			}
 			}
-			n_tty_set_room(tty);
 			up_read(&tty->termios_rwsem);
 			up_read(&tty->termios_rwsem);
 
 
 			timeout = wait_woken(&wait, TASK_INTERRUPTIBLE,
 			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)
 		if (time)
 			timeout = time;
 			timeout = time;
 	}
 	}
-	n_tty_set_room(tty);
+	if (tail != ldata->read_tail)
+		n_tty_kick_worker(tty);
 	up_read(&tty->termios_rwsem);
 	up_read(&tty->termios_rwsem);
 
 
 	remove_wait_queue(&tty->read_wait, &wait);
 	remove_wait_queue(&tty->read_wait, &wait);

+ 16 - 15
drivers/tty/pty.c

@@ -87,19 +87,6 @@ static void pty_unthrottle(struct tty_struct *tty)
 	set_bit(TTY_THROTTLED, &tty->flags);
 	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
  *	pty_write		-	write to a pty
  *	@tty: the tty we write from
  *	@tty: the tty we write from
@@ -141,7 +128,7 @@ static int pty_write_room(struct tty_struct *tty)
 {
 {
 	if (tty->stopped)
 	if (tty->stopped)
 		return 0;
 		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;
 	struct pid *pgrp;
 
 
+	if (sig != SIGINT && sig != SIGQUIT && sig != SIGTSTP)
+		return -EINVAL;
+
 	if (tty->link) {
 	if (tty->link) {
 		pgrp = tty_get_pgrp(tty->link);
 		pgrp = tty_get_pgrp(tty->link);
 		if (pgrp)
 		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)
 static void pty_flush_buffer(struct tty_struct *tty)
 {
 {
 	struct tty_struct *to = tty->link;
 	struct tty_struct *to = tty->link;
+	struct tty_ldisc *ld;
 
 
 	if (!to)
 	if (!to)
 		return;
 		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) {
 	if (to->packet) {
 		spin_lock_irq(&tty->ctrl_lock);
 		spin_lock_irq(&tty->ctrl_lock);
 		tty->ctrl_status |= TIOCPKT_FLUSHWRITE;
 		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;
 		goto err_put_module;
 
 
 	tty_set_lock_subclass(o_tty);
 	tty_set_lock_subclass(o_tty);
+	lockdep_set_subclass(&o_tty->termios_rwsem, TTY_LOCK_SLAVE);
 
 
 	if (legacy) {
 	if (legacy) {
 		/* We always use new tty termios data so we can do this
 		/* 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;
 	o_tty->link = tty;
 	tty_port_init(ports[0]);
 	tty_port_init(ports[0]);
 	tty_port_init(ports[1]);
 	tty_port_init(ports[1]);
+	tty_buffer_set_limit(ports[0], 8192);
+	tty_buffer_set_limit(ports[1], 8192);
 	o_tty->port = ports[0];
 	o_tty->port = ports[0];
 	tty->port = ports[1];
 	tty->port = ports[1];
 	o_tty->port->itty = o_tty;
 	o_tty->port->itty = o_tty;
 
 
+	tty_buffer_set_lock_subclass(o_tty->port);
+
 	tty_driver_kref_get(driver);
 	tty_driver_kref_get(driver);
 	tty->count++;
 	tty->count++;
 	o_tty->count++;
 	o_tty->count++;

+ 2 - 2
drivers/tty/rocket.c

@@ -1390,7 +1390,7 @@ static void rp_unthrottle(struct tty_struct *tty)
 	       tty->ldisc.chars_in_buffer(tty));
 	       tty->ldisc.chars_in_buffer(tty));
 #endif
 #endif
 
 
-	if (rocket_paranoia_check(info, "rp_throttle"))
+	if (rocket_paranoia_check(info, "rp_unthrottle"))
 		return;
 		return;
 
 
 	if (I_IXOFF(tty))
 	if (I_IXOFF(tty))
@@ -1458,7 +1458,7 @@ static void rp_wait_until_sent(struct tty_struct *tty, int timeout)
 
 
 	orig_jiffies = jiffies;
 	orig_jiffies = jiffies;
 #ifdef ROCKET_DEBUG_WAIT_UNTIL_SENT
 #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);
 	       jiffies);
 	printk(KERN_INFO "cps=%d...\n", info->cps);
 	printk(KERN_INFO "cps=%d...\n", info->cps);
 #endif
 #endif

+ 162 - 68
drivers/tty/serial/8250/8250_core.c

@@ -329,6 +329,17 @@ static const struct serial8250_config uart_config[] = {
 		.fcr		= UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
 		.fcr		= UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
 		.flags		= UART_CAP_FIFO | UART_CAP_AFE,
 		.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 */
 /* Uart divisor latch read */
@@ -956,7 +967,17 @@ static void autoconfig_16550a(struct uart_8250_port *up)
 			up->port.type = PORT_16650;
 			up->port.type = PORT_16650;
 			up->capabilities |= UART_CAP_EFR | UART_CAP_SLEEP;
 			up->capabilities |= UART_CAP_EFR | UART_CAP_SLEEP;
 		} else {
 		} 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);
 		serial_out(up, UART_EFR, 0);
 		return;
 		return;
@@ -1355,9 +1376,11 @@ static void serial8250_start_tx(struct uart_port *port)
 	struct uart_8250_port *up = up_to_u8250p(port);
 	struct uart_8250_port *up = up_to_u8250p(port);
 
 
 	serial8250_rpm_get_tx(up);
 	serial8250_rpm_get_tx(up);
-	if (up->dma && !up->dma->tx_dma(up)) {
+
+	if (up->dma && !up->dma->tx_dma(up))
 		return;
 		return;
-	} else if (!(up->ier & UART_IER_THRI)) {
+
+	if (!(up->ier & UART_IER_THRI)) {
 		up->ier |= UART_IER_THRI;
 		up->ier |= UART_IER_THRI;
 		serial_port_out(port, UART_IER, up->ier);
 		serial_port_out(port, UART_IER, up->ier);
 
 
@@ -1365,7 +1388,7 @@ static void serial8250_start_tx(struct uart_port *port)
 			unsigned char lsr;
 			unsigned char lsr;
 			lsr = serial_in(up, UART_LSR);
 			lsr = serial_in(up, UART_LSR);
 			up->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS;
 			up->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS;
-			if (lsr & UART_LSR_TEMT)
+			if (lsr & UART_LSR_THRE)
 				serial8250_tx_chars(up);
 				serial8250_tx_chars(up);
 		}
 		}
 	}
 	}
@@ -1924,7 +1947,7 @@ static unsigned int serial8250_get_mctrl(struct uart_port *port)
 	return ret;
 	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);
 	struct uart_8250_port *up = up_to_u8250p(port);
 	unsigned char mcr = 0;
 	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);
 	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)
 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);
 		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;
 	unsigned int quot;
 
 
 	/*
 	/*
 	 * Handle magic divisors for baud rates above baud_base on
 	 * Handle magic divisors for baud rates above baud_base on
 	 * SMSC SuperIO chips.
 	 * SMSC SuperIO chips.
+	 *
 	 */
 	 */
 	if ((port->flags & UPF_MAGIC_MULTIPLIER) &&
 	if ((port->flags & UPF_MAGIC_MULTIPLIER) &&
 	    baud == (port->uartclk/4))
 	    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) &&
 	else if ((port->flags & UPF_MAGIC_MULTIPLIER) &&
 		 baud == (port->uartclk/8))
 		 baud == (port->uartclk/8))
 		quot = 0x8002;
 		quot = 0x8002;
+	else if (up->port.type == PORT_XR17V35X)
+		quot = xr17v35x_get_divisor(up, baud, frac);
 	else
 	else
 		quot = uart_get_divisor(port, baud);
 		quot = uart_get_divisor(port, baud);
 
 
+	/*
+	 * Oxford Semi 952 rev B workaround
+	 */
+	if (up->bugs & UART_BUG_QUOT && (quot & 0xff) == 0)
+		quot++;
+
 	return 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 char cval;
-	unsigned long flags;
-	unsigned int baud, quot;
 
 
-	switch (termios->c_cflag & CSIZE) {
+	switch (c_cflag & CSIZE) {
 	case CS5:
 	case CS5:
 		cval = UART_LCR_WLEN5;
 		cval = UART_LCR_WLEN5;
 		break;
 		break;
@@ -2427,33 +2483,80 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
 		break;
 		break;
 	}
 	}
 
 
-	if (termios->c_cflag & CSTOPB)
+	if (c_cflag & CSTOPB)
 		cval |= UART_LCR_STOP;
 		cval |= UART_LCR_STOP;
-	if (termios->c_cflag & PARENB) {
+	if (c_cflag & PARENB) {
 		cval |= UART_LCR_PARITY;
 		cval |= UART_LCR_PARITY;
 		if (up->bugs & UART_BUG_PARITY)
 		if (up->bugs & UART_BUG_PARITY)
 			up->fifo_bug = true;
 			up->fifo_bug = true;
 	}
 	}
-	if (!(termios->c_cflag & PARODD))
+	if (!(c_cflag & PARODD))
 		cval |= UART_LCR_EPAR;
 		cval |= UART_LCR_EPAR;
 #ifdef CMSPAR
 #ifdef CMSPAR
-	if (termios->c_cflag & CMSPAR)
+	if (c_cflag & CMSPAR)
 		cval |= UART_LCR_SPAR;
 		cval |= UART_LCR_SPAR;
 #endif
 #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.
 	 * Ask the core to calculate the divisor for us.
 	 */
 	 */
 	baud = uart_get_baud_rate(port, termios, old,
 	baud = uart_get_baud_rate(port, termios, old,
 				  port->uartclk / 16 / 0xffff,
 				  port->uartclk / 16 / 0xffff,
 				  port->uartclk / 16);
 				  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) {
 	if (up->capabilities & UART_CAP_FIFO && port->fifosize > 1) {
 		/* NOTE: If fifo_bug is not set, a user can set RX_trigger. */
 		/* 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;
 			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.
 	 * 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);
 			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
 	 * 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)
 	if (port->type == PORT_16750)
 		serial_port_out(port, UART_FCR, up->fcr);
 		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) {
 	if (port->type != PORT_16750) {
 		/* emulated UARTs (Lucent Venus 167x) need two steps */
 		/* emulated UARTs (Lucent Venus 167x) need two steps */
 		if (up->fcr & UART_FCR_ENABLE_FIFO)
 		if (up->fcr & UART_FCR_ENABLE_FIFO)
@@ -3208,6 +3267,27 @@ serial8250_console_write(struct console *co, const char *s, unsigned int count)
 	else
 	else
 		serial_port_out(port, UART_IER, 0);
 		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);
 	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)
 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_8250_port *up = &serial8250_ports[line];
 	struct uart_port *port = &up->port;
 	struct uart_port *port = &up->port;
 
 
+	up->canary = 0;
+
 	if (up->capabilities & UART_NATSEMI) {
 	if (up->capabilities & UART_NATSEMI) {
 		/* Ensure it's still in high speed mode */
 		/* Ensure it's still in high speed mode */
 		serial_port_out(port, UART_LCR, 0xE0);
 		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 */
 		/*  Possibly override set_termios call */
 		if (up->port.set_termios)
 		if (up->port.set_termios)
 			uart->port.set_termios = 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)
 		if (up->port.startup)
 			uart->port.startup = up->port.startup;
 			uart->port.startup = up->port.startup;
 		if (up->port.shutdown)
 		if (up->port.shutdown)

+ 5 - 1
drivers/tty/serial/8250/8250_dma.c

@@ -59,7 +59,6 @@ static void __dma_rx_complete(void *param)
 
 
 	dma->rx_running = 0;
 	dma->rx_running = 0;
 	dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state);
 	dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state);
-	dmaengine_terminate_all(dma->rxchan);
 
 
 	count = dma->rx_size - state.residue;
 	count = dma->rx_size - state.residue;
 
 
@@ -81,6 +80,10 @@ int serial8250_tx_dma(struct uart_8250_port *p)
 		return 0;
 		return 0;
 
 
 	dma->tx_size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
 	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,
 	desc = dmaengine_prep_slave_single(dma->txchan,
 					   dma->tx_addr + xmit->tail,
 					   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) {
 		if (dma->rx_running) {
 			dmaengine_pause(dma->rxchan);
 			dmaengine_pause(dma->rxchan);
 			__dma_rx_complete(p);
 			__dma_rx_complete(p);
+			dmaengine_terminate_all(dma->rxchan);
 		}
 		}
 		return -ETIMEDOUT;
 		return -ETIMEDOUT;
 	default:
 	default:

+ 11 - 0
drivers/tty/serial/8250/8250_dw.c

@@ -351,10 +351,20 @@ static int dw8250_probe_of(struct uart_port *p,
 static int dw8250_probe_acpi(struct uart_8250_port *up,
 static int dw8250_probe_acpi(struct uart_8250_port *up,
 			     struct dw8250_data *data)
 			     struct dw8250_data *data)
 {
 {
+	const struct acpi_device_id *id;
 	struct uart_port *p = &up->port;
 	struct uart_port *p = &up->port;
 
 
 	dw8250_setup_port(up);
 	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->iotype = UPIO_MEM32;
 	p->serial_in = dw8250_serial_in32;
 	p->serial_in = dw8250_serial_in32;
 	p->serial_out = dw8250_serial_out32;
 	p->serial_out = dw8250_serial_out32;
@@ -577,6 +587,7 @@ static const struct acpi_device_id dw8250_acpi_match[] = {
 	{ "INT3435", 0 },
 	{ "INT3435", 0 },
 	{ "80860F0A", 0 },
 	{ "80860F0A", 0 },
 	{ "8086228A", 0 },
 	{ "8086228A", 0 },
+	{ "APMC0D08", 0},
 	{ },
 	{ },
 };
 };
 MODULE_DEVICE_TABLE(acpi, dw8250_acpi_match);
 MODULE_DEVICE_TABLE(acpi, dw8250_acpi_match);

+ 9 - 4
drivers/tty/serial/8250/8250_early.c

@@ -93,15 +93,18 @@ static void __init early_serial8250_write(struct console *console,
 	struct uart_port *port = &early_device->port;
 	struct uart_port *port = &early_device->port;
 	unsigned int ier;
 	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);
 	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);
 	uart_console_write(port, s, count, serial_putc);
 
 
 	/* Wait for transmitter to become empty and restore the IER */
 	/* Wait for transmitter to become empty and restore the IER */
 	wait_for_xmitr(port);
 	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)
 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;
 	struct uart_port *port = &device->port;
 	unsigned int divisor;
 	unsigned int divisor;
 	unsigned char c;
 	unsigned char c;
+	unsigned int ier;
 
 
 	serial8250_early_out(port, UART_LCR, 0x3);	/* 8n1 */
 	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_FCR, 0);	/* no fifo */
 	serial8250_early_out(port, UART_MCR, 0x3);	/* DTR + RTS */
 	serial8250_early_out(port, UART_MCR, 0x3);	/* DTR + RTS */
 
 

+ 76 - 12
drivers/tty/serial/8250/8250_omap.c

@@ -106,6 +106,28 @@ static u32 uart_read(struct uart_8250_port *up, u32 reg)
 	return readl(up->port.membase + (reg << up->port.regshift));
 	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)
  * Work Around for Errata i202 (2430, 3430, 3630, 4430 and 4460)
  * The access to uart register after MDR1 Access
  * 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;
 	priv->efr = 0;
 	up->mcr &= ~(UART_MCR_RTS | UART_MCR_XONANY);
 	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) {
 	} else	if (up->port.flags & UPF_SOFT_FLOW) {
 		/*
 		/*
 		 * IXON Flag:
 		 * IXON Flag:
@@ -417,8 +439,10 @@ static void omap_8250_set_termios(struct uart_port *port,
 		 * Enable XON/XOFF flow control on output.
 		 * Enable XON/XOFF flow control on output.
 		 * Transmit XON1, XOFF1
 		 * Transmit XON1, XOFF1
 		 */
 		 */
-		if (termios->c_iflag & IXOFF)
+		if (termios->c_iflag & IXOFF) {
+			up->port.status |= UPSTAT_AUTOXOFF;
 			priv->efr |= OMAP_UART_SW_TX;
 			priv->efr |= OMAP_UART_SW_TX;
+		}
 
 
 		/*
 		/*
 		 * IXANY Flag:
 		 * 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,
 static void omap_8250_pm(struct uart_port *port, unsigned int state,
 			 unsigned int oldstate)
 			 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);
 	pm_runtime_get_sync(port->dev);
 	serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
 	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_LCR, 0);
 
 
 	serial_out(up, UART_IER, (state != 0) ? UART_IERX_SLEEP : 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_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);
 	serial_out(up, UART_LCR, 0);
 
 
 	pm_runtime_mark_last_busy(port->dev);
 	pm_runtime_mark_last_busy(port->dev);
@@ -1007,6 +1031,7 @@ static int omap8250_probe(struct platform_device *pdev)
 	up.capabilities |= UART_CAP_RPM;
 	up.capabilities |= UART_CAP_RPM;
 #endif
 #endif
 	up.port.set_termios = omap_8250_set_termios;
 	up.port.set_termios = omap_8250_set_termios;
+	up.port.set_mctrl = omap8250_set_mctrl;
 	up.port.pm = omap_8250_pm;
 	up.port.pm = omap_8250_pm;
 	up.port.startup = omap_8250_startup;
 	up.port.startup = omap_8250_startup;
 	up.port.shutdown = omap_8250_shutdown;
 	up.port.shutdown = omap_8250_shutdown;
@@ -1248,6 +1273,46 @@ static int omap8250_runtime_resume(struct device *dev)
 }
 }
 #endif
 #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 = {
 static const struct dev_pm_ops omap8250_dev_pm_ops = {
 	SET_SYSTEM_SLEEP_PM_OPS(omap8250_suspend, omap8250_resume)
 	SET_SYSTEM_SLEEP_PM_OPS(omap8250_suspend, omap8250_resume)
 	SET_RUNTIME_PM_OPS(omap8250_runtime_suspend,
 	SET_RUNTIME_PM_OPS(omap8250_runtime_suspend,
@@ -1269,7 +1334,6 @@ static struct platform_driver omap8250_platform_driver = {
 		.name		= "omap8250",
 		.name		= "omap8250",
 		.pm		= &omap8250_dev_pm_ops,
 		.pm		= &omap8250_dev_pm_ops,
 		.of_match_table = omap8250_dt_ids,
 		.of_match_table = omap8250_dt_ids,
-		.owner		= THIS_MODULE,
 	},
 	},
 	.probe			= omap8250_probe,
 	.probe			= omap8250_probe,
 	.remove			= omap8250_remove,
 	.remove			= omap8250_remove,

+ 2 - 2
drivers/tty/serial/8250/8250_pci.c

@@ -221,13 +221,13 @@ pci_hp_diva_setup(struct serial_private *priv,
  */
  */
 static int pci_inteli960ni_init(struct pci_dev *dev)
 static int pci_inteli960ni_init(struct pci_dev *dev)
 {
 {
-	unsigned long oldval;
+	u32 oldval;
 
 
 	if (!(dev->subsystem_device & 0x1000))
 	if (!(dev->subsystem_device & 0x1000))
 		return -ENODEV;
 		return -ENODEV;
 
 
 	/* is firmware started? */
 	/* is firmware started? */
-	pci_read_config_dword(dev, 0x44, (void *)&oldval);
+	pci_read_config_dword(dev, 0x44, &oldval);
 	if (oldval == 0x00001000L) { /* RESET value */
 	if (oldval == 0x00001000L) { /* RESET value */
 		dev_dbg(&dev->dev, "Local i960 firmware missing\n");
 		dev_dbg(&dev->dev, "Local i960 firmware missing\n");
 		return -ENODEV;
 		return -ENODEV;

+ 7 - 1
drivers/tty/serial/8250/8250_pnp.c

@@ -426,7 +426,7 @@ static int serial_pnp_guess_board(struct pnp_dev *dev)
 static int
 static int
 serial_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id)
 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;
 	int ret, line, flags = dev_id->driver_data;
 
 
 	if (flags & UNKNOWN_DEV) {
 	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))
 	if (line < 0 || (flags & CIR_PORT))
 		return -ENODEV;
 		return -ENODEV;
 
 
+	port = serial8250_get_port(line);
+	if (uart_console(&port->port))
+		dev->capabilities |= PNP_CONSOLE;
+
 	pnp_set_drvdata(dev, (void *)((long)line + 1));
 	pnp_set_drvdata(dev, (void *)((long)line + 1));
 	return 0;
 	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)
 static void serial_pnp_remove(struct pnp_dev *dev)
 {
 {
 	long line = (long)pnp_get_drvdata(dev);
 	long line = (long)pnp_get_drvdata(dev);
+
+	dev->capabilities &= ~PNP_CONSOLE;
 	if (line)
 	if (line)
 		serial8250_unregister_port(line - 1);
 		serial8250_unregister_port(line - 1);
 }
 }

+ 19 - 0
drivers/tty/serial/8250/Kconfig

@@ -308,6 +308,25 @@ config SERIAL_8250_OMAP
 
 
 	  This driver uses ttyS instead of ttyO.
 	  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
 config SERIAL_8250_FINTEK
 	tristate "Support for Fintek F81216A LPC to 4 UART"
 	tristate "Support for Fintek F81216A LPC to 4 UART"
 	depends on SERIAL_8250 && PNP
 	depends on SERIAL_8250 && PNP

+ 44 - 10
drivers/tty/serial/Kconfig

@@ -241,6 +241,7 @@ config SERIAL_SAMSUNG
 	tristate "Samsung SoC serial support"
 	tristate "Samsung SoC serial support"
 	depends on PLAT_SAMSUNG || ARCH_EXYNOS
 	depends on PLAT_SAMSUNG || ARCH_EXYNOS
 	select SERIAL_CORE
 	select SERIAL_CORE
+	select SERIAL_EARLYCON
 	help
 	help
 	  Support for the on-chip UARTs on the Samsung S3C24XX series CPUs,
 	  Support for the on-chip UARTs on the Samsung S3C24XX series CPUs,
 	  providing /dev/ttySAC0, 1 and 2 (note, some machines may not
 	  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
 	  your boot loader (lilo or loadlin) about how to pass options to the
 	  kernel at boot time.)
 	  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
 config SERIAL_MFD_HSU
 	tristate "Medfield High Speed UART support"
 	tristate "Medfield High Speed UART support"
 	depends on PCI
 	depends on PCI
@@ -1094,6 +1085,16 @@ config SERIAL_VT8500_CONSOLE
 	depends on SERIAL_VT8500=y
 	depends on SERIAL_VT8500=y
 	select SERIAL_CORE_CONSOLE
 	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
 config SERIAL_NETX
 	tristate "NetX serial port support"
 	tristate "NetX serial port support"
 	depends on ARCH_NETX
 	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,
 	  If you have enabled the lpuart serial port on the Freescale SoCs,
 	  you can make it the console by answering Y to this option.
 	  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
 config SERIAL_ST_ASC
 	tristate "ST ASC serial port support"
 	tristate "ST ASC serial port support"
 	select SERIAL_CORE
 	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
 	  This driver can also be build as a module. If so, the module will be called
 	  men_z135_uart.ko
 	  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
 endmenu
 
 
 config SERIAL_MCTRL_GPIO
 config SERIAL_MCTRL_GPIO

+ 3 - 1
drivers/tty/serial/Makefile

@@ -51,6 +51,7 @@ obj-$(CONFIG_SERIAL_MPSC) += mpsc.o
 obj-$(CONFIG_SERIAL_MESON) += meson_uart.o
 obj-$(CONFIG_SERIAL_MESON) += meson_uart.o
 obj-$(CONFIG_SERIAL_SB1250_DUART) += sb1250-duart.o
 obj-$(CONFIG_SERIAL_SB1250_DUART) += sb1250-duart.o
 obj-$(CONFIG_ETRAX_SERIAL) += crisv10.o
 obj-$(CONFIG_ETRAX_SERIAL) += crisv10.o
+obj-$(CONFIG_SERIAL_ETRAXFS) += etraxfs-uart.o
 obj-$(CONFIG_SERIAL_SCCNXP) += sccnxp.o
 obj-$(CONFIG_SERIAL_SCCNXP) += sccnxp.o
 obj-$(CONFIG_SERIAL_SC16IS7XX) += sc16is7xx.o
 obj-$(CONFIG_SERIAL_SC16IS7XX) += sc16is7xx.o
 obj-$(CONFIG_SERIAL_JSM) += jsm/
 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_GRLIB_GAISLER_APBUART) += apbuart.o
 obj-$(CONFIG_SERIAL_ALTERA_JTAGUART) += altera_jtaguart.o
 obj-$(CONFIG_SERIAL_ALTERA_JTAGUART) += altera_jtaguart.o
 obj-$(CONFIG_SERIAL_VT8500) += vt8500_serial.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_MFD_HSU)	+= mfd.o
 obj-$(CONFIG_SERIAL_IFX6X60)  	+= ifx6x60.o
 obj-$(CONFIG_SERIAL_IFX6X60)  	+= ifx6x60.o
 obj-$(CONFIG_SERIAL_PCH_UART)	+= pch_uart.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_ARC)	+= arc_uart.o
 obj-$(CONFIG_SERIAL_RP2)	+= rp2.o
 obj-$(CONFIG_SERIAL_RP2)	+= rp2.o
 obj-$(CONFIG_SERIAL_FSL_LPUART)	+= fsl_lpuart.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_MEN_Z135)	+= men_z135_uart.o
+obj-$(CONFIG_SERIAL_SPRD) += sprd_serial.o
 
 
 # GPIOLIB helpers for modem control lines
 # GPIOLIB helpers for modem control lines
 obj-$(CONFIG_SERIAL_MCTRL_GPIO)	+= serial_mctrl_gpio.o
 obj-$(CONFIG_SERIAL_MCTRL_GPIO)	+= serial_mctrl_gpio.o

+ 1 - 0
drivers/tty/serial/altera_jtaguart.c

@@ -441,6 +441,7 @@ static int altera_jtaguart_probe(struct platform_device *pdev)
 	port->iotype = SERIAL_IO_MEM;
 	port->iotype = SERIAL_IO_MEM;
 	port->ops = &altera_jtaguart_ops;
 	port->ops = &altera_jtaguart_ops;
 	port->flags = UPF_BOOT_AUTOCONF;
 	port->flags = UPF_BOOT_AUTOCONF;
+	port->dev = &pdev->dev;
 
 
 	uart_add_one_port(&altera_jtaguart_driver, port);
 	uart_add_one_port(&altera_jtaguart_driver, port);
 
 

+ 1 - 0
drivers/tty/serial/altera_uart.c

@@ -589,6 +589,7 @@ static int altera_uart_probe(struct platform_device *pdev)
 	port->iotype = SERIAL_IO_MEM;
 	port->iotype = SERIAL_IO_MEM;
 	port->ops = &altera_uart_ops;
 	port->ops = &altera_uart_ops;
 	port->flags = UPF_BOOT_AUTOCONF;
 	port->flags = UPF_BOOT_AUTOCONF;
+	port->dev = &pdev->dev;
 
 
 	platform_set_drvdata(pdev, port);
 	platform_set_drvdata(pdev, port);
 
 

+ 101 - 52
drivers/tty/serial/atmel_serial.c

@@ -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)
 static void atmel_set_mctrl(struct uart_port *port, u_int mctrl)
 {
 {
 	unsigned int control = 0;
 	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);
 	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)
 	if (mctrl & TIOCM_RTS)
-		control |= ATMEL_US_RTSEN;
+		control |= rts_ready;
 	else
 	else
-		control |= ATMEL_US_RTSDIS;
+		control |= rts_paused;
 
 
 	if (mctrl & TIOCM_DTR)
 	if (mctrl & TIOCM_DTR)
 		control |= ATMEL_US_DTREN;
 		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);
 	mctrl_gpio_set(atmel_port->gpios, mctrl);
 
 
 	/* Local loopback mode? */
 	/* Local loopback mode? */
-	mode = UART_GET_MR(port) & ~ATMEL_US_CHMODE;
+	mode &= ~ATMEL_US_CHMODE;
 	if (mctrl & TIOCM_LOOP)
 	if (mctrl & TIOCM_LOOP)
 		mode |= ATMEL_US_CHMODE_LOC_LOOP;
 		mode |= ATMEL_US_CHMODE_LOC_LOOP;
 	else
 	else
 		mode |= ATMEL_US_CHMODE_NORMAL;
 		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);
 	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)
 	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
 		uart_write_wakeup(port);
 		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))
 	if (!uart_circ_empty(xmit))
 		tasklet_schedule(&atmel_port->tasklet);
 		tasklet_schedule(&atmel_port->tasklet);
 
 
@@ -784,17 +801,17 @@ static void atmel_tx_dma(struct uart_port *port)
 		BUG_ON(!sg_dma_len(sg));
 		BUG_ON(!sg_dma_len(sg));
 
 
 		desc = dmaengine_prep_slave_sg(chan,
 		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) {
 		if (!desc) {
 			dev_err(port->dev, "Failed to send via dma!\n");
 			dev_err(port->dev, "Failed to send via dma!\n");
 			return;
 			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;
 		atmel_port->desc_tx = desc;
 		desc->callback = atmel_complete_tx_dma;
 		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,
 	dma_sync_sg_for_cpu(port->dev,
 			    &atmel_port->sg_rx,
 			    &atmel_port->sg_rx,
 			    1,
 			    1,
-			    DMA_DEV_TO_MEM);
+			    DMA_FROM_DEVICE);
 
 
 	/*
 	/*
 	 * ring->head points to the end of data already written by the DMA.
 	 * 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,
 	dma_sync_sg_for_device(port->dev,
 			       &atmel_port->sg_rx,
 			       &atmel_port->sg_rx,
 			       1,
 			       1,
-			       DMA_DEV_TO_MEM);
+			       DMA_FROM_DEVICE);
 
 
 	/*
 	/*
 	 * Drop the lock here since it might end up calling
 	 * 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. */
 	/* UART circular rx buffer is an aligned page. */
 	BUG_ON((int)port->state->xmit.buf & ~PAGE_MASK);
 	BUG_ON((int)port->state->xmit.buf & ~PAGE_MASK);
 	sg_set_page(&atmel_port->sg_rx,
 	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) {
 	if (!nent) {
 		dev_dbg(port->dev, "need to release resource of dma\n");
 		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
 	 * each one is half ring buffer size
 	 */
 	 */
 	desc = dmaengine_prep_dma_cyclic(atmel_port->chan_rx,
 	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 = atmel_complete_rx_dma;
 	desc->callback_param = port;
 	desc->callback_param = port;
 	atmel_port->desc_rx = desc;
 	atmel_port->desc_rx = desc;
@@ -1921,12 +1938,14 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios,
 			      struct ktermios *old)
 			      struct ktermios *old)
 {
 {
 	unsigned long flags;
 	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);
 	baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16);
 	quot = uart_get_divisor(port, baud);
 	quot = uart_get_divisor(port, baud);
@@ -1971,12 +1990,6 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios,
 	} else
 	} else
 		mode |= ATMEL_US_PAR_NONE;
 		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);
 	spin_lock_irqsave(&port->lock, flags);
 
 
 	port->read_status_mask = ATMEL_US_OVRE;
 	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 */
 	/* disable receiver and transmitter */
 	UART_PUT_CR(port, ATMEL_US_TXDIS | ATMEL_US_RXDIS);
 	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.flags & SER_RS485_ENABLED) {
 		if ((port->rs485.delay_rts_after_send) > 0)
 		if ((port->rs485.delay_rts_after_send) > 0)
 			UART_PUT_TTGR(port, port->rs485.delay_rts_after_send);
 			UART_PUT_TTGR(port, port->rs485.delay_rts_after_send);
 		mode |= ATMEL_US_USMODE_RS485;
 		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);
 	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 */
 	/* set the baud rate */
 	UART_PUT_BRGR(port, quot);
 	UART_PUT_BRGR(port, quot);
 	UART_PUT_CR(port, ATMEL_US_RSTSTA | ATMEL_US_RSTRX);
 	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);
 	ret = atmel_init_port(port, pdev);
 	if (ret)
 	if (ret)
-		goto err;
+		goto err_clear_bit;
 
 
 	if (!atmel_use_pdc_rx(&port->uart)) {
 	if (!atmel_use_pdc_rx(&port->uart)) {
 		ret = -ENOMEM;
 		ret = -ENOMEM;
@@ -2596,6 +2631,12 @@ static int atmel_serial_probe(struct platform_device *pdev)
 	device_init_wakeup(&pdev->dev, 1);
 	device_init_wakeup(&pdev->dev, 1);
 	platform_set_drvdata(pdev, port);
 	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) {
 	if (rs485_enabled) {
 		UART_PUT_MR(&port->uart, ATMEL_US_USMODE_NORMAL);
 		UART_PUT_MR(&port->uart, ATMEL_US_USMODE_NORMAL);
 		UART_PUT_CR(&port->uart, ATMEL_US_RTSEN);
 		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);
 	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;
 	return 0;
 
 
 err_add_port:
 err_add_port:
@@ -2616,6 +2663,8 @@ err_alloc_ring:
 		clk_put(port->clk);
 		clk_put(port->clk);
 		port->clk = NULL;
 		port->clk = NULL;
 	}
 	}
+err_clear_bit:
+	clear_bit(port->uart.line, atmel_ports_in_use);
 err:
 err:
 	return ret;
 	return ret;
 }
 }

+ 560 - 0
drivers/tty/serial/digicolor-usart.c

@@ -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");

+ 996 - 0
drivers/tty/serial/etraxfs-uart.c

@@ -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);

+ 77 - 58
drivers/tty/serial/fsl_lpuart.c

@@ -237,7 +237,8 @@ struct lpuart_port {
 	unsigned int		rxfifo_size;
 	unsigned int		rxfifo_size;
 	bool			lpuart32;
 	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_tx_chan;
 	struct dma_chan		*dma_rx_chan;
 	struct dma_chan		*dma_rx_chan;
 	struct dma_async_tx_descriptor  *dma_tx_desc;
 	struct dma_async_tx_descriptor  *dma_tx_desc;
@@ -454,6 +455,15 @@ static int lpuart_dma_rx(struct lpuart_port *sport)
 	return 0;
 	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)
 static void lpuart_dma_rx_complete(void *arg)
 {
 {
 	struct lpuart_port *sport = arg;
 	struct lpuart_port *sport = arg;
@@ -461,6 +471,7 @@ static void lpuart_dma_rx_complete(void *arg)
 	unsigned long flags;
 	unsigned long flags;
 
 
 	async_tx_ack(sport->dma_rx_desc);
 	async_tx_ack(sport->dma_rx_desc);
+	mod_timer(&sport->lpuart_timer, jiffies + sport->dma_rx_timeout);
 
 
 	spin_lock_irqsave(&sport->port.lock, flags);
 	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);
 	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;
 	sport->lpuart_timer.expires = jiffies + sport->dma_rx_timeout;
 	add_timer(&sport->lpuart_timer);
 	add_timer(&sport->lpuart_timer);
 
 
@@ -571,7 +579,7 @@ static void lpuart_start_tx(struct uart_port *port)
 	temp = readb(port->membase + UARTCR2);
 	temp = readb(port->membase + UARTCR2);
 	writeb(temp | UARTCR2_TIE, 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)
 		if (!uart_circ_empty(xmit) && !sport->dma_tx_in_progress)
 			lpuart_prepare_tx(sport);
 			lpuart_prepare_tx(sport);
 	} else {
 	} else {
@@ -758,19 +766,19 @@ out:
 static irqreturn_t lpuart_int(int irq, void *dev_id)
 static irqreturn_t lpuart_int(int irq, void *dev_id)
 {
 {
 	struct lpuart_port *sport = dev_id;
 	struct lpuart_port *sport = dev_id;
-	unsigned char sts;
+	unsigned char sts, crdma;
 
 
 	sts = readb(sport->port.membase + UARTSR1);
 	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);
 			lpuart_prepare_rx(sport);
 		else
 		else
 			lpuart_rxint(irq, dev_id);
 			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);
 			lpuart_pio_tx(sport);
 		else
 		else
 			lpuart_txint(irq, dev_id);
 			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 *sport = container_of(port,
 					struct lpuart_port, port);
 					struct lpuart_port, port);
-	struct dma_chan *tx_chan;
 	struct dma_slave_config dma_tx_sconfig;
 	struct dma_slave_config dma_tx_sconfig;
 	dma_addr_t dma_bus;
 	dma_addr_t dma_bus;
 	unsigned char *dma_buf;
 	unsigned char *dma_buf;
 	int ret;
 	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,
 				sport->port.state->xmit.buf,
 				UART_XMIT_SIZE, DMA_TO_DEVICE);
 				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");
 		dev_err(sport->port.dev, "dma_map_single tx failed\n");
-		dma_release_channel(tx_chan);
 		return -ENOMEM;
 		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_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
 	dma_tx_sconfig.dst_maxburst = sport->txfifo_size;
 	dma_tx_sconfig.dst_maxburst = sport->txfifo_size;
 	dma_tx_sconfig.direction = DMA_MEM_TO_DEV;
 	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) {
 	if (ret < 0) {
 		dev_err(sport->port.dev,
 		dev_err(sport->port.dev,
 				"Dma slave config failed, err = %d\n", ret);
 				"Dma slave config failed, err = %d\n", ret);
-		dma_release_channel(tx_chan);
 		return ret;
 		return ret;
 	}
 	}
 
 
-	sport->dma_tx_chan = tx_chan;
 	sport->dma_tx_buf_virt = dma_buf;
 	sport->dma_tx_buf_virt = dma_buf;
 	sport->dma_tx_buf_bus = dma_bus;
 	sport->dma_tx_buf_bus = dma_bus;
 	sport->dma_tx_in_progress = 0;
 	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 *sport = container_of(port,
 					struct lpuart_port, port);
 					struct lpuart_port, port);
-	struct dma_chan *rx_chan;
 	struct dma_slave_config dma_rx_sconfig;
 	struct dma_slave_config dma_rx_sconfig;
 	dma_addr_t dma_bus;
 	dma_addr_t dma_bus;
 	unsigned char *dma_buf;
 	unsigned char *dma_buf;
 	int ret;
 	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,
 	dma_buf = devm_kzalloc(sport->port.dev,
 				FSL_UART_RX_DMA_BUFFER_SIZE, GFP_KERNEL);
 				FSL_UART_RX_DMA_BUFFER_SIZE, GFP_KERNEL);
 
 
 	if (!dma_buf) {
 	if (!dma_buf) {
 		dev_err(sport->port.dev, "Dma rx alloc failed\n");
 		dev_err(sport->port.dev, "Dma rx alloc failed\n");
-		dma_release_channel(rx_chan);
 		return -ENOMEM;
 		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);
 				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");
 		dev_err(sport->port.dev, "dma_map_single rx failed\n");
-		dma_release_channel(rx_chan);
 		return -ENOMEM;
 		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_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
 	dma_rx_sconfig.src_maxburst = 1;
 	dma_rx_sconfig.src_maxburst = 1;
 	dma_rx_sconfig.direction = DMA_DEV_TO_MEM;
 	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) {
 	if (ret < 0) {
 		dev_err(sport->port.dev,
 		dev_err(sport->port.dev,
 				"Dma slave config failed, err = %d\n", ret);
 				"Dma slave config failed, err = %d\n", ret);
-		dma_release_channel(rx_chan);
 		return ret;
 		return ret;
 	}
 	}
 
 
-	sport->dma_rx_chan = rx_chan;
 	sport->dma_rx_buf_virt = dma_buf;
 	sport->dma_rx_buf_virt = dma_buf;
 	sport->dma_rx_buf_bus = dma_bus;
 	sport->dma_rx_buf_bus = dma_bus;
 	sport->dma_rx_in_progress = 0;
 	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 *sport = container_of(port,
 					struct lpuart_port, port);
 					struct lpuart_port, port);
-	struct dma_chan *dma_chan;
 
 
 	dma_unmap_single(sport->port.dev, sport->dma_tx_buf_bus,
 	dma_unmap_single(sport->port.dev, sport->dma_tx_buf_bus,
 			UART_XMIT_SIZE, DMA_TO_DEVICE);
 			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_bus = 0;
 	sport->dma_tx_buf_virt = NULL;
 	sport->dma_tx_buf_virt = NULL;
-	dma_release_channel(dma_chan);
 }
 }
 
 
 static void lpuart_dma_rx_free(struct uart_port *port)
 static void lpuart_dma_rx_free(struct uart_port *port)
 {
 {
 	struct lpuart_port *sport = container_of(port,
 	struct lpuart_port *sport = container_of(port,
 					struct lpuart_port, port);
 					struct lpuart_port, port);
-	struct dma_chan *dma_chan;
 
 
 	dma_unmap_single(sport->port.dev, sport->dma_rx_buf_bus,
 	dma_unmap_single(sport->port.dev, sport->dma_rx_buf_bus,
 			FSL_UART_RX_DMA_BUFFER_SIZE, DMA_FROM_DEVICE);
 			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_bus = 0;
 	sport->dma_rx_buf_virt = NULL;
 	sport->dma_rx_buf_virt = NULL;
-	dma_release_channel(dma_chan);
 }
 }
 
 
 static int lpuart_startup(struct uart_port *port)
 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) &
 	sport->rxfifo_size = 0x1 << (((temp >> UARTPFIFO_RXSIZE_OFF) &
 		UARTPFIFO_FIFOSIZE_MASK) + 1);
 		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 = readb(port->membase + UARTCR5);
+		temp &= ~UARTCR5_RDMAS;
 		writeb(temp | UARTCR5_TDMAS, port->membase + UARTCR5);
 		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,
 	ret = devm_request_irq(port->dev, port->irq, lpuart_int, 0,
 				DRIVER_NAME, sport);
 				DRIVER_NAME, sport);
@@ -1179,10 +1164,13 @@ static void lpuart_shutdown(struct uart_port *port)
 
 
 	devm_free_irq(port->dev, port->irq, sport);
 	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)
 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 */
 	/* update the per-port timeout */
 	uart_update_timeout(port, termios->c_cflag, baud);
 	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 */
 		/* Calculate delay for 1.5 DMA buffers */
 		sport->dma_rx_timeout = (sport->port.timeout - HZ / 50) *
 		sport->dma_rx_timeout = (sport->port.timeout - HZ / 50) *
 					FSL_UART_RX_DMA_BUFFER_SIZE * 3 /
 					FSL_UART_RX_DMA_BUFFER_SIZE * 3 /
@@ -1517,6 +1505,7 @@ static struct uart_ops lpuart_pops = {
 	.release_port	= lpuart_release_port,
 	.release_port	= lpuart_release_port,
 	.config_port	= lpuart_config_port,
 	.config_port	= lpuart_config_port,
 	.verify_port	= lpuart_verify_port,
 	.verify_port	= lpuart_verify_port,
+	.flush_buffer	= lpuart_flush_buffer,
 };
 };
 
 
 static struct uart_ops lpuart32_pops = {
 static struct uart_ops lpuart32_pops = {
@@ -1535,6 +1524,7 @@ static struct uart_ops lpuart32_pops = {
 	.release_port	= lpuart_release_port,
 	.release_port	= lpuart_release_port,
 	.config_port	= lpuart_config_port,
 	.config_port	= lpuart_config_port,
 	.verify_port	= lpuart_verify_port,
 	.verify_port	= lpuart_verify_port,
+	.flush_buffer	= lpuart_flush_buffer,
 };
 };
 
 
 static struct lpuart_port *lpuart_ports[UART_NR];
 static struct lpuart_port *lpuart_ports[UART_NR];
@@ -1833,6 +1823,16 @@ static int lpuart_probe(struct platform_device *pdev)
 		return ret;
 		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;
 	return 0;
 }
 }
 
 
@@ -1844,6 +1844,12 @@ static int lpuart_remove(struct platform_device *pdev)
 
 
 	clk_disable_unprepare(sport->clk);
 	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;
 	return 0;
 }
 }
 
 
@@ -1851,6 +1857,19 @@ static int lpuart_remove(struct platform_device *pdev)
 static int lpuart_suspend(struct device *dev)
 static int lpuart_suspend(struct device *dev)
 {
 {
 	struct lpuart_port *sport = dev_get_drvdata(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);
 	uart_suspend_port(&lpuart_reg, &sport->port);
 
 

+ 154 - 36
drivers/tty/serial/imx.c

@@ -74,6 +74,7 @@
 #define IMX21_UTS 0xb4 /* UART Test Register on all other i.mx*/
 #define IMX21_UTS 0xb4 /* UART Test Register on all other i.mx*/
 
 
 /* UART Control Register Bit Fields.*/
 /* UART Control Register Bit Fields.*/
+#define URXD_DUMMY_READ (1<<16)
 #define URXD_CHARRDY	(1<<15)
 #define URXD_CHARRDY	(1<<15)
 #define URXD_ERR	(1<<14)
 #define URXD_ERR	(1<<14)
 #define URXD_OVRRUN	(1<<13)
 #define URXD_OVRRUN	(1<<13)
@@ -463,13 +464,17 @@ static void imx_enable_ms(struct uart_port *port)
 	mod_timer(&sport->timer, jiffies);
 	mod_timer(&sport->timer, jiffies);
 }
 }
 
 
+static void imx_dma_tx(struct imx_port *sport);
 static inline void imx_transmit_buffer(struct imx_port *sport)
 static inline void imx_transmit_buffer(struct imx_port *sport)
 {
 {
 	struct circ_buf *xmit = &sport->port.state->xmit;
 	struct circ_buf *xmit = &sport->port.state->xmit;
+	unsigned long temp;
 
 
 	if (sport->port.x_char) {
 	if (sport->port.x_char) {
 		/* Send next char */
 		/* Send next char */
 		writel(sport->port.x_char, sport->port.membase + URTX0);
 		writel(sport->port.x_char, sport->port.membase + URTX0);
+		sport->port.icount.tx++;
+		sport->port.x_char = 0;
 		return;
 		return;
 	}
 	}
 
 
@@ -478,6 +483,22 @@ static inline void imx_transmit_buffer(struct imx_port *sport)
 		return;
 		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) &&
 	while (!uart_circ_empty(xmit) &&
 	       !(readl(sport->port.membase + uts_reg(sport)) & UTS_TXFULL)) {
 	       !(readl(sport->port.membase + uts_reg(sport)) & UTS_TXFULL)) {
 		/* send xmit->buf[xmit->tail]
 		/* send xmit->buf[xmit->tail]
@@ -500,26 +521,39 @@ static void dma_tx_callback(void *data)
 	struct scatterlist *sgl = &sport->tx_sgl[0];
 	struct scatterlist *sgl = &sport->tx_sgl[0];
 	struct circ_buf *xmit = &sport->port.state->xmit;
 	struct circ_buf *xmit = &sport->port.state->xmit;
 	unsigned long flags;
 	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);
 	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 */
 	/* update the stat */
-	spin_lock_irqsave(&sport->port.lock, flags);
 	xmit->tail = (xmit->tail + sport->tx_bytes) & (UART_XMIT_SIZE - 1);
 	xmit->tail = (xmit->tail + sport->tx_bytes) & (UART_XMIT_SIZE - 1);
 	sport->port.icount.tx += sport->tx_bytes;
 	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");
 	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)) {
 	if (waitqueue_active(&sport->dma_wait)) {
 		wake_up(&sport->dma_wait);
 		wake_up(&sport->dma_wait);
 		dev_dbg(sport->port.dev, "exit in %s.\n", __func__);
 		dev_dbg(sport->port.dev, "exit in %s.\n", __func__);
 		return;
 		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)
 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_async_tx_descriptor *desc;
 	struct dma_chan	*chan = sport->dma_chan_tx;
 	struct dma_chan	*chan = sport->dma_chan_tx;
 	struct device *dev = sport->port.dev;
 	struct device *dev = sport->port.dev;
-	enum dma_status status;
+	unsigned long temp;
 	int ret;
 	int ret;
 
 
-	status = dmaengine_tx_status(chan, (dma_cookie_t)0, NULL);
-	if (DMA_IN_PROGRESS == status)
+	if (sport->dma_is_txing)
 		return;
 		return;
 
 
 	sport->tx_bytes = uart_circ_chars_pending(xmit);
 	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;
 		sport->dma_tx_nents = 2;
 		sg_init_table(sgl, 2);
 		sg_init_table(sgl, 2);
 		sg_set_buf(sgl, xmit->buf + xmit->tail,
 		sg_set_buf(sgl, xmit->buf + xmit->tail,
 				UART_XMIT_SIZE - xmit->tail);
 				UART_XMIT_SIZE - xmit->tail);
 		sg_set_buf(sgl + 1, xmit->buf, xmit->head);
 		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);
 	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,
 	desc = dmaengine_prep_slave_sg(chan, sgl, sport->dma_tx_nents,
 					DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT);
 					DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT);
 	if (!desc) {
 	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");
 		dev_err(dev, "We cannot prepare for the TX slave dma!\n");
 		return;
 		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",
 	dev_dbg(dev, "TX: prepare to send %lu bytes by DMA.\n",
 			uart_circ_chars_pending(xmit));
 			uart_circ_chars_pending(xmit));
+
+	temp = readl(sport->port.membase + UCR1);
+	temp |= UCR1_TDMAEN;
+	writel(temp, sport->port.membase + UCR1);
+
 	/* fire it */
 	/* fire it */
 	sport->dma_is_txing = 1;
 	sport->dma_is_txing = 1;
 	dmaengine_submit(desc);
 	dmaengine_submit(desc);
@@ -590,13 +630,6 @@ static void imx_start_tx(struct uart_port *port)
 		temp &= ~(UCR1_RRDYEN);
 		temp &= ~(UCR1_RRDYEN);
 		writel(temp, sport->port.membase + UCR1);
 		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) {
 	if (!sport->dma_is_enabled) {
 		temp = readl(sport->port.membase + UCR1);
 		temp = readl(sport->port.membase + UCR1);
@@ -614,15 +647,21 @@ static void imx_start_tx(struct uart_port *port)
 	}
 	}
 
 
 	if (sport->dma_is_enabled) {
 	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) &&
 		if (!uart_circ_empty(&port->state->xmit) &&
 		    !uart_tx_stopped(port))
 		    !uart_tx_stopped(port))
 			imx_dma_tx(sport);
 			imx_dma_tx(sport);
 		return;
 		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)
 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;
 				continue;
 			}
 			}
 
 
-			rx &= sport->port.read_status_mask;
+			rx &= (sport->port.read_status_mask | 0xFF);
 
 
 			if (rx & URXD_BRK)
 			if (rx & URXD_BRK)
 				flg = TTY_BREAK;
 				flg = TTY_BREAK;
@@ -710,6 +749,9 @@ static irqreturn_t imx_rxint(int irq, void *dev_id)
 #endif
 #endif
 		}
 		}
 
 
+		if (sport->port.ignore_status_mask & URXD_DUMMY_READ)
+			goto out;
+
 		tty_insert_flip_char(port, rx, flg);
 		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)
 static void imx_dma_rxint(struct imx_port *sport)
 {
 {
 	unsigned long temp;
 	unsigned long temp;
+	unsigned long flags;
+
+	spin_lock_irqsave(&sport->port.lock, flags);
 
 
 	temp = readl(sport->port.membase + USR2);
 	temp = readl(sport->port.membase + USR2);
 	if ((temp & USR2_RDR) && !sport->dma_is_rxing) {
 	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. */
 		/* tell the DMA to receive the data. */
 		start_rx_dma(sport);
 		start_rx_dma(sport);
 	}
 	}
+
+	spin_unlock_irqrestore(&sport->port.lock, flags);
 }
 }
 
 
 static irqreturn_t imx_int(int irq, void *dev_id)
 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)
 static void imx_rx_dma_done(struct imx_port *sport)
 {
 {
 	unsigned long temp;
 	unsigned long temp;
+	unsigned long flags;
+
+	spin_lock_irqsave(&sport->port.lock, flags);
 
 
 	/* Enable this interrupt when the RXFIFO is empty. */
 	/* Enable this interrupt when the RXFIFO is empty. */
 	temp = readl(sport->port.membase + UCR1);
 	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? */
 	/* Is the shutdown waiting for us? */
 	if (waitqueue_active(&sport->dma_wait))
 	if (waitqueue_active(&sport->dma_wait))
 		wake_up(&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);
 	dev_dbg(sport->port.dev, "We get %d bytes.\n", count);
 
 
 	if (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);
 		tty_flip_buffer_push(port);
 
 
 		start_rx_dma(sport);
 		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);
 		imx_rx_dma_done(sport);
+	}
 }
 }
 
 
 static int start_rx_dma(struct imx_port *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,
 	desc = dmaengine_prep_slave_sg(chan, sgl, 1, DMA_DEV_TO_MEM,
 					DMA_PREP_INTERRUPT);
 					DMA_PREP_INTERRUPT);
 	if (!desc) {
 	if (!desc) {
+		dma_unmap_sg(dev, sgl, 1, DMA_FROM_DEVICE);
 		dev_err(dev, "We cannot prepare for the RX slave dma!\n");
 		dev_err(dev, "We cannot prepare for the RX slave dma!\n");
 		return -EINVAL;
 		return -EINVAL;
 	}
 	}
@@ -1108,12 +1175,20 @@ static int imx_startup(struct uart_port *port)
 	while (!(readl(sport->port.membase + UCR2) & UCR2_SRST) && (--i > 0))
 	while (!(readl(sport->port.membase + UCR2) & UCR2_SRST) && (--i > 0))
 		udelay(1);
 		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);
 	spin_lock_irqsave(&sport->port.lock, flags);
 	/*
 	/*
 	 * Finally, clear and enable interrupts
 	 * Finally, clear and enable interrupts
 	 */
 	 */
 	writel(USR1_RTSD, sport->port.membase + USR1);
 	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 = readl(sport->port.membase + UCR1);
 	temp |= UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN;
 	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);
 	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 = readl(sport->port.membase + UCR2);
 	temp |= (UCR2_RXEN | UCR2_TXEN);
 	temp |= (UCR2_RXEN | UCR2_TXEN);
 	if (!sport->have_rtscts)
 	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_tx);
 			dmaengine_terminate_all(sport->dma_chan_rx);
 			dmaengine_terminate_all(sport->dma_chan_rx);
 		}
 		}
+		spin_lock_irqsave(&sport->port.lock, flags);
 		imx_stop_tx(port);
 		imx_stop_tx(port);
 		imx_stop_rx(port);
 		imx_stop_rx(port);
 		imx_disable_dma(sport);
 		imx_disable_dma(sport);
+		spin_unlock_irqrestore(&sport->port.lock, flags);
 		imx_uart_dma_exit(sport);
 		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)
 static void imx_flush_buffer(struct uart_port *port)
 {
 {
 	struct imx_port *sport = (struct imx_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
 static void
@@ -1280,11 +1402,6 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
 		if (sport->have_rtscts) {
 		if (sport->have_rtscts) {
 			ucr2 &= ~UCR2_IRTS;
 			ucr2 &= ~UCR2_IRTS;
 			ucr2 |= UCR2_CTSC;
 			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 {
 		} else {
 			termios->c_cflag &= ~CRTSCTS;
 			termios->c_cflag &= ~CRTSCTS;
 		}
 		}
@@ -1319,7 +1436,7 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
 	 */
 	 */
 	sport->port.ignore_status_mask = 0;
 	sport->port.ignore_status_mask = 0;
 	if (termios->c_iflag & IGNPAR)
 	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) {
 	if (termios->c_iflag & IGNBRK) {
 		sport->port.ignore_status_mask |= URXD_BRK;
 		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;
 			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.
 	 * 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))
 	if (UART_ENABLE_MS(&sport->port, termios->c_cflag))
 		imx_enable_ms(&sport->port);
 		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);
 	spin_unlock_irqrestore(&sport->port.lock, flags);
 }
 }
 
 

+ 3 - 4
drivers/tty/serial/mcf.c

@@ -3,7 +3,7 @@
 /*
 /*
  *	mcf.c -- Freescale ColdFire UART driver
  *	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
  * 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
  * 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,
 static void mcf_set_termios(struct uart_port *port, struct ktermios *termios,
 	struct ktermios *old)
 	struct ktermios *old)
 {
 {
-	struct mcf_uart *pp = container_of(port, struct mcf_uart, port);
 	unsigned long flags;
 	unsigned long flags;
 	unsigned int baud, baudclk;
 	unsigned int baud, baudclk;
 #if defined(CONFIG_M5272)
 #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 */
 /* Enable or disable the RS485 support */
 static int mcf_config_rs485(struct uart_port *port, struct serial_rs485 *rs485)
 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;
 	unsigned char mr1, mr2;
 
 
 	/* Get mode registers */
 	/* Get mode registers */
@@ -631,6 +629,7 @@ static int mcf_probe(struct platform_device *pdev)
 		port->mapbase = platp[i].mapbase;
 		port->mapbase = platp[i].mapbase;
 		port->membase = (platp[i].membase) ? platp[i].membase :
 		port->membase = (platp[i].membase) ? platp[i].membase :
 			(unsigned char __iomem *) platp[i].mapbase;
 			(unsigned char __iomem *) platp[i].mapbase;
+		port->dev = &pdev->dev;
 		port->iotype = SERIAL_IO_MEM;
 		port->iotype = SERIAL_IO_MEM;
 		port->irq = platp[i].irq;
 		port->irq = platp[i].irq;
 		port->uartclk = MCF_BUSCLK;
 		port->uartclk = MCF_BUSCLK;
@@ -702,7 +701,7 @@ static void __exit mcf_exit(void)
 module_init(mcf_init);
 module_init(mcf_init);
 module_exit(mcf_exit);
 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_DESCRIPTION("Freescale ColdFire UART driver");
 MODULE_LICENSE("GPL");
 MODULE_LICENSE("GPL");
 MODULE_ALIAS("platform:mcfuart");
 MODULE_ALIAS("platform:mcfuart");

+ 109 - 46
drivers/tty/serial/men_z135_uart.c

@@ -23,7 +23,6 @@
 #define MEN_Z135_MAX_PORTS		12
 #define MEN_Z135_MAX_PORTS		12
 #define MEN_Z135_BASECLK		29491200
 #define MEN_Z135_BASECLK		29491200
 #define MEN_Z135_FIFO_SIZE		1024
 #define MEN_Z135_FIFO_SIZE		1024
-#define MEN_Z135_NUM_MSI_VECTORS	2
 #define MEN_Z135_FIFO_WATERMARK		1020
 #define MEN_Z135_FIFO_WATERMARK		1020
 
 
 #define MEN_Z135_STAT_REG		0x0
 #define MEN_Z135_STAT_REG		0x0
@@ -34,12 +33,11 @@
 #define MEN_Z135_CONF_REG		0x808
 #define MEN_Z135_CONF_REG		0x808
 #define MEN_Z135_UART_FREQ		0x80c
 #define MEN_Z135_UART_FREQ		0x80c
 #define MEN_Z135_BAUD_REG		0x810
 #define MEN_Z135_BAUD_REG		0x810
-#define MENZ135_TIMEOUT			0x814
+#define MEN_Z135_TIMEOUT		0x814
 
 
 #define MEN_Z135_MEM_SIZE		0x818
 #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_RXCIEN BIT(0)		/* RX Space IRQ */
 #define MEN_Z135_IER_TXCIEN BIT(1)		/* TX Space IRQ */
 #define MEN_Z135_IER_TXCIEN BIT(1)		/* TX Space IRQ */
@@ -94,11 +92,11 @@
 #define MEN_Z135_LSR_TEXP BIT(6)
 #define MEN_Z135_LSR_TEXP BIT(6)
 #define MEN_Z135_LSR_RXFIFOERR BIT(7)
 #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)
 #define LCR(x) (((x) >> MEN_Z135_LCR_SHIFT) & 0xff)
 
 
@@ -118,12 +116,18 @@ static int align;
 module_param(align, int, S_IRUGO);
 module_param(align, int, S_IRUGO);
 MODULE_PARM_DESC(align, "Keep hardware FIFO write pointer aligned, default 0");
 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 men_z135_port {
 	struct uart_port port;
 	struct uart_port port;
 	struct mcb_device *mdev;
 	struct mcb_device *mdev;
 	unsigned char *rxbuf;
 	unsigned char *rxbuf;
 	u32 stat_reg;
 	u32 stat_reg;
 	spinlock_t lock;
 	spinlock_t lock;
+	bool automode;
 };
 };
 #define to_men_z135(port) container_of((port), struct men_z135_port, port)
 #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)
 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_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_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)
 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;
 	txfree = MEN_Z135_FIFO_WATERMARK - txc;
 	if (txfree <= 0) {
 	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);
 			txfree, qlen);
 		goto irq_en;
 		goto irq_en;
 	}
 	}
@@ -373,43 +382,54 @@ out:
  * @irq: The IRQ number
  * @irq: The IRQ number
  * @data: Pointer to UART port
  * @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)
 static irqreturn_t men_z135_intr(int irq, void *data)
 {
 {
 	struct men_z135_port *uart = (struct men_z135_port *)data;
 	struct men_z135_port *uart = (struct men_z135_port *)data;
 	struct uart_port *port = &uart->port;
 	struct uart_port *port = &uart->port;
+	bool handled = false;
+	unsigned long flags;
 	int irq_id;
 	int irq_id;
 
 
 	uart->stat_reg = ioread32(port->membase + MEN_Z135_STAT_REG);
 	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);
 	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);
 		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)
 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)
 	if (mctrl & TIOCM_RTS)
 		conf_reg |= MEN_Z135_MCR_RTS;
 		conf_reg |= MEN_Z135_MCR_RTS;
+	else
+		conf_reg &= ~MEN_Z135_MCR_RTS;
+
 	if (mctrl & TIOCM_DTR)
 	if (mctrl & TIOCM_DTR)
 		conf_reg |= MEN_Z135_MCR_DTR;
 		conf_reg |= MEN_Z135_MCR_DTR;
+	else
+		conf_reg &= ~MEN_Z135_MCR_DTR;
+
 	if (mctrl & TIOCM_OUT1)
 	if (mctrl & TIOCM_OUT1)
 		conf_reg |= MEN_Z135_MCR_OUT1;
 		conf_reg |= MEN_Z135_MCR_OUT1;
+	else
+		conf_reg &= ~MEN_Z135_MCR_OUT1;
+
 	if (mctrl & TIOCM_OUT2)
 	if (mctrl & TIOCM_OUT2)
 		conf_reg |= MEN_Z135_MCR_OUT2;
 		conf_reg |= MEN_Z135_MCR_OUT2;
+	else
+		conf_reg &= ~MEN_Z135_MCR_OUT2;
+
 	if (mctrl & TIOCM_LOOP)
 	if (mctrl & TIOCM_LOOP)
 		conf_reg |= MEN_Z135_MCR_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)
 static unsigned int men_z135_get_mctrl(struct uart_port *port)
 {
 {
 	unsigned int mctrl = 0;
 	unsigned int mctrl = 0;
-	u32 stat_reg;
 	u8 msr;
 	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)
 	if (msr & MEN_Z135_MSR_CTS)
 		mctrl |= TIOCM_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_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
  * men_z135_start_tx() - Start transmitting characters
  * @port: The UART port
  * @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);
 	struct men_z135_port *uart = to_men_z135(port);
 
 
+	if (uart->automode)
+		men_z135_disable_ms(port);
+
 	men_z135_handle_tx(uart);
 	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);
 	iowrite32(conf_reg, port->membase + MEN_Z135_CONF_REG);
 
 
+	if (rx_timeout)
+		iowrite32(rx_timeout, port->membase + MEN_Z135_TIMEOUT);
+
 	return 0;
 	return 0;
 }
 }
 
 
@@ -603,6 +655,7 @@ static void men_z135_set_termios(struct uart_port *port,
 				struct ktermios *termios,
 				struct ktermios *termios,
 				struct ktermios *old)
 				struct ktermios *old)
 {
 {
+	struct men_z135_port *uart = to_men_z135(port);
 	unsigned int baud;
 	unsigned int baud;
 	u32 conf_reg;
 	u32 conf_reg;
 	u32 bd_reg;
 	u32 bd_reg;
@@ -643,6 +696,16 @@ static void men_z135_set_termios(struct uart_port *port,
 	} else
 	} else
 		lcr |= MEN_Z135_PAR_DIS << MEN_Z135_PEN_SHIFT;
 		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 */
 	termios->c_cflag &= ~CMSPAR; /* Mark/Space parity is not supported */
 
 
 	conf_reg |= lcr << MEN_Z135_LCR_SHIFT;
 	conf_reg |= lcr << MEN_Z135_LCR_SHIFT;

+ 0 - 909
drivers/tty/serial/mrst_max3110.c

@@ -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");

+ 0 - 61
drivers/tty/serial/mrst_max3110.h

@@ -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

+ 4 - 23
drivers/tty/serial/msm_serial.c

@@ -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)
 static int __init msm_console_setup(struct console *co, char *options)
 {
 {
 	struct uart_port *port;
 	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))
 	if (unlikely(co->index >= UART_NR || co->index < 0))
 		return -ENXIO;
 		return -ENXIO;
 
 
 	port = get_port_from_line(co->index);
 	port = get_port_from_line(co->index);
-	msm_port = UART_TO_MSM(port);
 
 
 	if (unlikely(!port->membase))
 	if (unlikely(!port->membase))
 		return -ENXIO;
 		return -ENXIO;
@@ -937,23 +938,6 @@ static int __init msm_console_setup(struct console *co, char *options)
 	if (options)
 	if (options)
 		uart_parse_options(options, &baud, &parity, &bits, &flow);
 		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);
 	pr_info("msm_serial: console setup on port #%d\n", port->line);
 
 
 	return uart_set_options(port, co, baud, parity, bits, flow);
 	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)
 static void __exit msm_serial_exit(void)
 {
 {
-#ifdef CONFIG_SERIAL_MSM_CONSOLE
-	unregister_console(&msm_console);
-#endif
 	platform_driver_unregister(&msm_platform_driver);
 	platform_driver_unregister(&msm_platform_driver);
 	uart_unregister_driver(&msm_uart_driver);
 	uart_unregister_driver(&msm_uart_driver);
 }
 }

+ 20 - 35
drivers/tty/serial/mxs-auart.c

@@ -152,8 +152,6 @@ struct mxs_auart_port {
 	unsigned int mctrl_prev;
 	unsigned int mctrl_prev;
 	enum mxs_auart_type devtype;
 	enum mxs_auart_type devtype;
 
 
-	unsigned int irq;
-
 	struct clk *clk;
 	struct clk *clk;
 	struct device *dev;
 	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);
 			of_match_device(mxs_auart_dt_ids, &pdev->dev);
 	struct mxs_auart_port *s;
 	struct mxs_auart_port *s;
 	u32 version;
 	u32 version;
-	int ret = 0;
+	int ret, irq;
 	struct resource *r;
 	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);
 	ret = serial_mxs_probe_dt(s, pdev);
 	if (ret > 0)
 	if (ret > 0)
 		s->port.line = pdev->id < 0 ? 0 : pdev->id;
 		s->port.line = pdev->id < 0 ? 0 : pdev->id;
 	else if (ret < 0)
 	else if (ret < 0)
-		goto out_free;
+		return ret;
 
 
 	if (of_id) {
 	if (of_id) {
 		pdev->id_entry = of_id->data;
 		pdev->id_entry = of_id->data;
 		s->devtype = pdev->id_entry->driver_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);
 	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.mapbase = r->start;
 	s->port.membase = ioremap(r->start, resource_size(r));
 	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->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)
 	if (ret)
-		goto out_free_clk;
+		return ret;
 
 
 	platform_set_drvdata(pdev, s);
 	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);
 	ret = mxs_auart_request_gpio_irq(s);
 	if (ret)
 	if (ret)
-		goto out_free_irq;
+		return ret;
 
 
 	auart_port[s->port.line] = s;
 	auart_port[s->port.line] = s;
 
 
@@ -1307,14 +1304,7 @@ static int mxs_auart_probe(struct platform_device *pdev)
 
 
 out_free_gpio_irq:
 out_free_gpio_irq:
 	mxs_auart_free_gpio_irq(s);
 	mxs_auart_free_gpio_irq(s);
-out_free_irq:
 	auart_port[pdev->id] = NULL;
 	auart_port[pdev->id] = NULL;
-	free_irq(s->irq, s);
-out_free_clk:
-	clk_put(s->clk);
-out_free:
-	kfree(s);
-out:
 	return ret;
 	return ret;
 }
 }
 
 
@@ -1323,13 +1313,8 @@ static int mxs_auart_remove(struct platform_device *pdev)
 	struct mxs_auart_port *s = platform_get_drvdata(pdev);
 	struct mxs_auart_port *s = platform_get_drvdata(pdev);
 
 
 	uart_remove_one_port(&auart_driver, &s->port);
 	uart_remove_one_port(&auart_driver, &s->port);
-
 	auart_port[pdev->id] = NULL;
 	auart_port[pdev->id] = NULL;
-
 	mxs_auart_free_gpio_irq(s);
 	mxs_auart_free_gpio_irq(s);
-	clk_put(s->clk);
-	free_irq(s->irq, s);
-	kfree(s);
 
 
 	return 0;
 	return 0;
 }
 }

+ 13 - 0
drivers/tty/serial/of_serial.c

@@ -102,6 +102,11 @@ static int of_platform_serial_setup(struct platform_device *ofdev,
 	if (of_property_read_u32(np, "fifo-size", &prop) == 0)
 	if (of_property_read_u32(np, "fifo-size", &prop) == 0)
 		port->fifosize = prop;
 		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->irq = irq_of_parse_and_map(np, 0);
 	port->iotype = UPIO_MEM;
 	port->iotype = UPIO_MEM;
 	if (of_property_read_u32(np, "reg-io-width", &prop) == 0) {
 	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))
 	if (of_find_property(np, "no-loopback-test", NULL))
 		port->flags |= UPF_SKIP_TEST;
 		port->flags |= UPF_SKIP_TEST;
 
 
+	ret = of_alias_get_id(np, "serial");
+	if (ret >= 0)
+		port->line = ret;
+
 	port->dev = &ofdev->dev;
 	port->dev = &ofdev->dev;
 
 
 	switch (type) {
 	switch (type) {
@@ -331,6 +340,10 @@ static struct of_device_id of_platform_serial_table[] = {
 		.data = (void *)PORT_ALTR_16550_F64, },
 		.data = (void *)PORT_ALTR_16550_F64, },
 	{ .compatible = "altr,16550-FIFO128",
 	{ .compatible = "altr,16550-FIFO128",
 		.data = (void *)PORT_ALTR_16550_F128, },
 		.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
 #ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL
 	{ .compatible = "ibm,qpace-nwp-serial",
 	{ .compatible = "ibm,qpace-nwp-serial",
 		.data = (void *)PORT_NWPSERIAL, },
 		.data = (void *)PORT_NWPSERIAL, },

+ 24 - 13
drivers/tty/serial/omap-serial.c

@@ -63,7 +63,7 @@
 #define UART_ERRATA_i202_MDR1_ACCESS	BIT(0)
 #define UART_ERRATA_i202_MDR1_ACCESS	BIT(0)
 #define UART_ERRATA_i291_DMA_FORCEIDLE	BIT(1)
 #define UART_ERRATA_i291_DMA_FORCEIDLE	BIT(1)
 
 
-#define DEFAULT_CLK_SPEED 48000000 /* 48Mhz*/
+#define DEFAULT_CLK_SPEED 48000000 /* 48Mhz */
 
 
 /* SCR register bitmasks */
 /* SCR register bitmasks */
 #define OMAP_UART_SCR_RX_TRIG_GRANU1_MASK		(1 << 7)
 #define OMAP_UART_SCR_RX_TRIG_GRANU1_MASK		(1 << 7)
@@ -93,7 +93,7 @@
 /* WER = 0x7F
 /* WER = 0x7F
  * Enable module level wakeup in WER reg
  * 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 */
 /* Enable XON/XOFF flow control on output */
 #define OMAP_UART_SW_TX		0x08
 #define OMAP_UART_SW_TX		0x08
@@ -114,7 +114,7 @@ struct uart_omap_dma {
 	dma_addr_t		tx_buf_dma_phys;
 	dma_addr_t		tx_buf_dma_phys;
 	unsigned int		uart_base;
 	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.
 	 * comes from port structure.
 	 */
 	 */
 	unsigned char		*rx_buf;
 	unsigned char		*rx_buf;
@@ -151,7 +151,7 @@ struct uart_omap_port {
 	int			use_dma;
 	int			use_dma;
 	/*
 	/*
 	 * Some bits in registers are cleared on a read, so they must
 	 * 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.
 	 * be immediately processed.
 	 */
 	 */
 	unsigned int		lsr_break_flag;
 	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)
 static void serial_omap_set_mctrl(struct uart_port *port, unsigned int mctrl)
 {
 {
 	struct uart_omap_port *up = to_uart_omap_port(port);
 	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);
 	dev_dbg(up->port.dev, "serial_omap_set_mctrl+%d\n", up->port.line);
 	if (mctrl & TIOCM_RTS)
 	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);
 		     UART_MCR_DTR | UART_MCR_RTS);
 	up->mcr = old_mcr | mcr;
 	up->mcr = old_mcr | mcr;
 	serial_out(up, UART_MCR, up->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_mark_last_busy(up->dev);
 	pm_runtime_put_autosuspend(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())
 	 * (they will be reenabled in set_termios())
 	 */
 	 */
 	serial_omap_clear_fifos(up);
 	serial_omap_clear_fifos(up);
-	/* For Hardware flow control */
-	serial_out(up, UART_MCR, UART_MCR_RTS);
 
 
 	/*
 	/*
 	 * Clear the interrupt registers.
 	 * 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);
 	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 {
 	} else {
 		/* Disable AUTORTS and AUTOCTS */
 		/* Disable AUTORTS and AUTOCTS */
 		up->efr &= ~(UART_EFR_CTS | UART_EFR_RTS);
 		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.
 		 * Enable XON/XOFF flow control on output.
 		 * Transmit XON1, XOFF1
 		 * Transmit XON1, XOFF1
 		 */
 		 */
-		if (termios->c_iflag & IXOFF)
+		if (termios->c_iflag & IXOFF) {
+			up->port.status |= UPSTAT_AUTOXOFF;
 			up->efr |= OMAP_UART_SW_TX;
 			up->efr |= OMAP_UART_SW_TX;
+		}
 
 
 		/*
 		/*
 		 * IXANY Flag:
 		 * IXANY Flag:

+ 662 - 18
drivers/tty/serial/samsung.c

@@ -28,6 +28,9 @@
 #define SUPPORT_SYSRQ
 #define SUPPORT_SYSRQ
 #endif
 #endif
 
 
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
 #include <linux/module.h>
 #include <linux/module.h>
 #include <linux/ioport.h>
 #include <linux/ioport.h>
 #include <linux/io.h>
 #include <linux/io.h>
@@ -78,6 +81,10 @@ static void dbg(const char *fmt, ...)
 #define S3C24XX_SERIAL_MAJOR	204
 #define S3C24XX_SERIAL_MAJOR	204
 #define S3C24XX_SERIAL_MINOR	64
 #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 */
 /* macros to change one thing to another */
 
 
 #define tx_enabled(port) ((port)->unused[0])
 #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)
 static void s3c24xx_serial_stop_tx(struct uart_port *port)
 {
 {
 	struct s3c24xx_uart_port *ourport = to_ourport(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 s3c24xx_uart_port *ourport = to_ourport(port);
+	struct circ_buf *xmit = &port->state->xmit;
 
 
 	if (!tx_enabled(port)) {
 	if (!tx_enabled(port)) {
 		if (port->flags & UPF_CONS_FLOW)
 		if (port->flags & UPF_CONS_FLOW)
 			s3c24xx_serial_rx_disable(port);
 			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;
 		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)
 static void s3c24xx_serial_stop_rx(struct uart_port *port)
 {
 {
 	struct s3c24xx_uart_port *ourport = to_ourport(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)) {
 	if (rx_enabled(port)) {
 		dbg("s3c24xx_serial_stop_rx: port=%p\n", 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);
 			disable_irq_nosync(ourport->rx_irq);
 		rx_enabled(port) = 0;
 		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
 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;
 	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?? */
 /* ? - where has parity gone?? */
 #define S3C2410_UERSTAT_PARITY (0x1000)
 #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 s3c24xx_uart_port *ourport = dev_id;
 	struct uart_port *port = &ourport->port;
 	struct uart_port *port = &ourport->port;
@@ -324,16 +720,33 @@ out:
 	return IRQ_HANDLED;
 	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)
 static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id)
 {
 {
 	struct s3c24xx_uart_port *ourport = id;
 	struct s3c24xx_uart_port *ourport = id;
 	struct uart_port *port = &ourport->port;
 	struct uart_port *port = &ourport->port;
 	struct circ_buf *xmit = &port->state->xmit;
 	struct circ_buf *xmit = &port->state->xmit;
 	unsigned long flags;
 	unsigned long flags;
-	int count = port->fifosize;
+	int count;
 
 
 	spin_lock_irqsave(&port->lock, flags);
 	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) {
 	if (port->x_char) {
 		wr_regb(port, S3C2410_UTXH, port->x_char);
 		wr_regb(port, S3C2410_UTXH, port->x_char);
 		port->icount.tx++;
 		port->icount.tx++;
@@ -352,6 +765,7 @@ static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id)
 
 
 	/* try and drain the buffer... */
 	/* try and drain the buffer... */
 
 
+	count = port->fifosize;
 	while (!uart_circ_empty(xmit) && count-- > 0) {
 	while (!uart_circ_empty(xmit) && count-- > 0) {
 		if (rd_regl(port, S3C2410_UFSTAT) & ourport->info->tx_fifofull)
 		if (rd_regl(port, S3C2410_UFSTAT) & ourport->info->tx_fifofull)
 			break;
 			break;
@@ -453,6 +867,93 @@ static void s3c24xx_serial_break_ctl(struct uart_port *port, int break_state)
 	spin_unlock_irqrestore(&port->lock, flags);
 	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)
 static void s3c24xx_serial_shutdown(struct uart_port *port)
 {
 {
 	struct s3c24xx_uart_port *ourport = to_ourport(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_UINTP, 0xf);
 		wr_regl(port, S3C64XX_UINTM, 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)
 static int s3c24xx_serial_startup(struct uart_port *port)
@@ -529,12 +1035,21 @@ err:
 static int s3c64xx_serial_startup(struct uart_port *port)
 static int s3c64xx_serial_startup(struct uart_port *port)
 {
 {
 	struct s3c24xx_uart_port *ourport = to_ourport(port);
 	struct s3c24xx_uart_port *ourport = to_ourport(port);
+	unsigned long flags;
+	unsigned int ufcon;
 	int ret;
 	int ret;
 
 
 	dbg("s3c64xx_serial_startup: port=%p (%08llx,%p)\n",
 	dbg("s3c64xx_serial_startup: port=%p (%08llx,%p)\n",
 	    port, (unsigned long long)port->mapbase, port->membase);
 	    port, (unsigned long long)port->mapbase, port->membase);
 
 
 	wr_regl(port, S3C64XX_UINTM, 0xf);
 	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,
 	ret = request_irq(port->irq, s3c64xx_serial_handle_irq, IRQF_SHARED,
 			  s3c24xx_serial_portname(port), ourport);
 			  s3c24xx_serial_portname(port), ourport);
@@ -549,8 +1064,20 @@ static int s3c64xx_serial_startup(struct uart_port *port)
 	tx_enabled(port) = 0;
 	tx_enabled(port) = 0;
 	ourport->tx_claimed = 1;
 	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 */
 	/* Enable Rx Interrupt */
 	__clear_bit(S3C64XX_UINTM_RXD, portaddrl(port, S3C64XX_UINTM));
 	__clear_bit(S3C64XX_UINTM_RXD, portaddrl(port, S3C64XX_UINTM));
+
 	dbg("s3c64xx_serial_startup ok\n");
 	dbg("s3c64xx_serial_startup ok\n");
 	return ret;
 	return ret;
 }
 }
@@ -1209,6 +1736,18 @@ static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport,
 	ret = platform_get_irq(platdev, 1);
 	ret = platform_get_irq(platdev, 1);
 	if (ret > 0)
 	if (ret > 0)
 		ourport->tx_irq = ret;
 		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");
 	ourport->clk	= clk_get(&platdev->dev, "uart");
 	if (IS_ERR(ourport->clk)) {
 	if (IS_ERR(ourport->clk)) {
@@ -1857,6 +2396,111 @@ static struct platform_driver samsung_serial_driver = {
 
 
 module_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_ALIAS("platform:samsung-uart");
 MODULE_DESCRIPTION("Samsung SoC Serial port driver");
 MODULE_DESCRIPTION("Samsung SoC Serial port driver");
 MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
 MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");

+ 42 - 0
drivers/tty/serial/samsung.h

@@ -12,6 +12,8 @@
  * published by the Free Software Foundation.
  * published by the Free Software Foundation.
 */
 */
 
 
+#include <linux/dmaengine.h>
+
 struct s3c24xx_uart_info {
 struct s3c24xx_uart_info {
 	char			*name;
 	char			*name;
 	unsigned int		type;
 	unsigned int		type;
@@ -41,6 +43,40 @@ struct s3c24xx_serial_drv_data {
 	unsigned int			fifosize[CONFIG_SERIAL_SAMSUNG_UARTS];
 	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 {
 struct s3c24xx_uart_port {
 	unsigned char			rx_claimed;
 	unsigned char			rx_claimed;
 	unsigned char			tx_claimed;
 	unsigned char			tx_claimed;
@@ -50,6 +86,10 @@ struct s3c24xx_uart_port {
 	unsigned int			rx_irq;
 	unsigned int			rx_irq;
 	unsigned int			tx_irq;
 	unsigned int			tx_irq;
 
 
+	unsigned int			tx_in_progress;
+	unsigned int			tx_mode;
+	unsigned int			rx_mode;
+
 	struct s3c24xx_uart_info	*info;
 	struct s3c24xx_uart_info	*info;
 	struct clk			*clk;
 	struct clk			*clk;
 	struct clk			*baudclk;
 	struct clk			*baudclk;
@@ -59,6 +99,8 @@ struct s3c24xx_uart_port {
 	/* reference to platform data */
 	/* reference to platform data */
 	struct s3c2410_uartcfg		*cfg;
 	struct s3c2410_uartcfg		*cfg;
 
 
+	struct s3c24xx_uart_dma		*dma;
+
 #ifdef CONFIG_CPU_FREQ
 #ifdef CONFIG_CPU_FREQ
 	struct notifier_block		freq_transition;
 	struct notifier_block		freq_transition;
 #endif
 #endif

+ 46 - 65
drivers/tty/serial/serial_core.c

@@ -179,14 +179,6 @@ static int uart_port_startup(struct tty_struct *tty, struct uart_state *state,
 			if (tty->termios.c_cflag & CBAUD)
 			if (tty->termios.c_cflag & CBAUD)
 				uart_set_mctrl(uport, TIOCM_RTS | TIOCM_DTR);
 				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 uart_port *uport = state->uart_port;
 	struct ktermios *termios;
 	struct ktermios *termios;
+	int hw_stopped;
 
 
 	/*
 	/*
 	 * If we have no tty, termios, or the port does not exist,
 	 * 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;
 		uport->status &= ~UPSTAT_DCD_ENABLE;
 	else
 	else
 		uport->status |= UPSTAT_DCD_ENABLE;
 		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);
 	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_state *state = tty->driver_data;
 	struct uart_port *port = state->uart_port;
 	struct uart_port *port = state->uart_port;
-	upf_t mask = 0;
+	upstat_t mask = 0;
 
 
 	if (I_IXOFF(tty))
 	if (I_IXOFF(tty))
-		mask |= UPF_SOFT_FLOW;
+		mask |= UPSTAT_AUTOXOFF;
 	if (tty->termios.c_cflag & CRTSCTS)
 	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);
 		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));
 		uart_send_xchar(tty, STOP_CHAR(tty));
 
 
-	if (mask & UPF_HARD_FLOW)
+	if (mask & UPSTAT_AUTORTS)
 		uart_clear_mctrl(port, TIOCM_RTS);
 		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_state *state = tty->driver_data;
 	struct uart_port *port = state->uart_port;
 	struct uart_port *port = state->uart_port;
-	upf_t mask = 0;
+	upstat_t mask = 0;
 
 
 	if (I_IXOFF(tty))
 	if (I_IXOFF(tty))
-		mask |= UPF_SOFT_FLOW;
+		mask |= UPSTAT_AUTOXOFF;
 	if (tty->termios.c_cflag & CRTSCTS)
 	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);
 		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));
 		uart_send_xchar(tty, START_CHAR(tty));
 
 
-	if (mask & UPF_HARD_FLOW)
+	if (mask & UPSTAT_AUTORTS)
 		uart_set_mctrl(port, TIOCM_RTS);
 		uart_set_mctrl(port, TIOCM_RTS);
 }
 }
 
 
@@ -1351,30 +1356,6 @@ static void uart_set_termios(struct tty_struct *tty,
 			mask |= TIOCM_RTS;
 			mask |= TIOCM_RTS;
 		uart_set_mctrl(uport, mask);
 		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);
 	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) {
 	if (port->flags & ASYNC_INITIALIZED) {
 		const struct uart_ops *ops = uport->ops;
 		const struct uart_ops *ops = uport->ops;
 		int tries;
 		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.
 		 * 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->dev_name,
 				drv->tty_driver->name_base + uport->line);
 				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.
 	 * Disable the console device before suspending.
 	 */
 	 */
-	if (console_suspend_enabled && uart_console(uport))
+	if (uart_console(uport))
 		console_stop(uport->cons);
 		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);
 	mutex_unlock(&port->mutex);
 
 
 	return 0;
 	return 0;
@@ -2856,7 +2836,7 @@ void uart_handle_cts_change(struct uart_port *uport, unsigned int status)
 
 
 	uport->icount.cts++;
 	uport->icount.cts++;
 
 
-	if (uart_cts_enabled(uport)) {
+	if (uart_softcts_mode(uport)) {
 		if (uport->hw_stopped) {
 		if (uport->hw_stopped) {
 			if (status) {
 			if (status) {
 				uport->hw_stopped = 0;
 				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);
 				uport->ops->stop_tx(uport);
 			}
 			}
 		}
 		}
+
 	}
 	}
 }
 }
 EXPORT_SYMBOL_GPL(uart_handle_cts_change);
 EXPORT_SYMBOL_GPL(uart_handle_cts_change);

+ 17 - 8
drivers/tty/serial/sh-sci.c

@@ -858,7 +858,7 @@ static int sci_handle_fifo_overrun(struct uart_port *port)
 		tty_insert_flip_char(tport, 0, TTY_OVERRUN);
 		tty_insert_flip_char(tport, 0, TTY_OVERRUN);
 		tty_flip_buffer_push(tport);
 		tty_flip_buffer_push(tport);
 
 
-		dev_notice(port->dev, "overrun error\n");
+		dev_dbg(port->dev, "overrun error\n");
 		copied++;
 		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)
 static irqreturn_t sci_mpxed_interrupt(int irq, void *ptr)
 {
 {
 	unsigned short ssr_status, scr_status, err_enabled;
 	unsigned short ssr_status, scr_status, err_enabled;
+	unsigned short slr_status = 0;
 	struct uart_port *port = ptr;
 	struct uart_port *port = ptr;
 	struct sci_port *s = to_sci_port(port);
 	struct sci_port *s = to_sci_port(port);
 	irqreturn_t ret = IRQ_NONE;
 	irqreturn_t ret = IRQ_NONE;
 
 
 	ssr_status = serial_port_in(port, SCxSR);
 	ssr_status = serial_port_in(port, SCxSR);
 	scr_status = serial_port_in(port, SCSCR);
 	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);
 	err_enabled = scr_status & port_rx_irq_mask(port);
 
 
 	/* Tx Interrupt */
 	/* Tx Interrupt */
@@ -1015,8 +1018,11 @@ static irqreturn_t sci_mpxed_interrupt(int irq, void *ptr)
 	 * DR flags
 	 * DR flags
 	 */
 	 */
 	if (((ssr_status & SCxSR_RDxF(port)) || s->chan_rx) &&
 	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);
 		ret = sci_rx_interrupt(irq, ptr);
+	}
 
 
 	/* Error Interrupt */
 	/* Error Interrupt */
 	if ((ssr_status & SCxSR_ERRORS(port)) && err_enabled)
 	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)
 	if ((ssr_status & SCxSR_BRK(port)) && err_enabled)
 		ret = sci_br_interrupt(irq, ptr);
 		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;
 	return ret;
 }
 }
 
 
@@ -2605,7 +2617,7 @@ static int sci_probe(struct platform_device *dev)
 	return 0;
 	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);
 	struct sci_port *sport = dev_get_drvdata(dev);
 
 
@@ -2615,7 +2627,7 @@ static int sci_suspend(struct device *dev)
 	return 0;
 	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);
 	struct sci_port *sport = dev_get_drvdata(dev);
 
 
@@ -2625,10 +2637,7 @@ static int sci_resume(struct device *dev)
 	return 0;
 	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 = {
 static struct platform_driver sci_driver = {
 	.probe		= sci_probe,
 	.probe		= sci_probe,

+ 20 - 20
drivers/tty/serial/sirfsoc_uart.c

@@ -177,7 +177,7 @@ static void sirfsoc_uart_stop_tx(struct uart_port *port)
 			dmaengine_pause(sirfport->tx_dma_chan);
 			dmaengine_pause(sirfport->tx_dma_chan);
 			sirfport->tx_dma_state = TX_DMA_PAUSE;
 			sirfport->tx_dma_state = TX_DMA_PAUSE;
 		} else {
 		} else {
-			if (!sirfport->is_marco)
+			if (!sirfport->is_atlas7)
 				wr_regl(port, ureg->sirfsoc_int_en_reg,
 				wr_regl(port, ureg->sirfsoc_int_en_reg,
 				rd_regl(port, ureg->sirfsoc_int_en_reg) &
 				rd_regl(port, ureg->sirfsoc_int_en_reg) &
 				~uint_en->sirfsoc_txfifo_empty_en);
 				~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);
 				uint_en->sirfsoc_txfifo_empty_en);
 		}
 		}
 	} else {
 	} else {
-		if (!sirfport->is_marco)
+		if (!sirfport->is_atlas7)
 			wr_regl(port, ureg->sirfsoc_int_en_reg,
 			wr_regl(port, ureg->sirfsoc_int_en_reg,
 				rd_regl(port, ureg->sirfsoc_int_en_reg) &
 				rd_regl(port, ureg->sirfsoc_int_en_reg) &
 				~uint_en->sirfsoc_txfifo_empty_en);
 				~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)
 	if (sirfport->tx_dma_state == TX_DMA_RUNNING)
 		return;
 		return;
-	if (!sirfport->is_marco)
+	if (!sirfport->is_atlas7)
 		wr_regl(port, ureg->sirfsoc_int_en_reg,
 		wr_regl(port, ureg->sirfsoc_int_en_reg,
 				rd_regl(port, ureg->sirfsoc_int_en_reg)&
 				rd_regl(port, ureg->sirfsoc_int_en_reg)&
 				~(uint_en->sirfsoc_txfifo_empty_en));
 				~(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)
 		if (tran_size < 4)
 			sirfsoc_uart_pio_tx_chars(sirfport, tran_size);
 			sirfsoc_uart_pio_tx_chars(sirfport, tran_size);
-		if (!sirfport->is_marco)
+		if (!sirfport->is_atlas7)
 			wr_regl(port, ureg->sirfsoc_int_en_reg,
 			wr_regl(port, ureg->sirfsoc_int_en_reg,
 				rd_regl(port, ureg->sirfsoc_int_en_reg)|
 				rd_regl(port, ureg->sirfsoc_int_en_reg)|
 				uint_en->sirfsoc_txfifo_empty_en);
 				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_pio_tx_chars(sirfport,
 			SIRFSOC_UART_IO_TX_REASONABLE_CNT);
 			SIRFSOC_UART_IO_TX_REASONABLE_CNT);
 		wr_regl(port, ureg->sirfsoc_tx_fifo_op, SIRFUART_FIFO_START);
 		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,
 			wr_regl(port, ureg->sirfsoc_int_en_reg,
 					rd_regl(port, ureg->sirfsoc_int_en_reg)|
 					rd_regl(port, ureg->sirfsoc_int_en_reg)|
 					uint_en->sirfsoc_txfifo_empty_en);
 					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);
 	wr_regl(port, ureg->sirfsoc_rx_fifo_op, 0);
 	if (sirfport->rx_dma_chan) {
 	if (sirfport->rx_dma_chan) {
-		if (!sirfport->is_marco)
+		if (!sirfport->is_atlas7)
 			wr_regl(port, ureg->sirfsoc_int_en_reg,
 			wr_regl(port, ureg->sirfsoc_int_en_reg,
 				rd_regl(port, ureg->sirfsoc_int_en_reg) &
 				rd_regl(port, ureg->sirfsoc_int_en_reg) &
 				~(SIRFUART_RX_DMA_INT_EN(port, uint_en) |
 				~(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);
 					uint_en->sirfsoc_rx_done_en);
 		dmaengine_terminate_all(sirfport->rx_dma_chan);
 		dmaengine_terminate_all(sirfport->rx_dma_chan);
 	} else {
 	} else {
-		if (!sirfport->is_marco)
+		if (!sirfport->is_atlas7)
 			wr_regl(port, ureg->sirfsoc_int_en_reg,
 			wr_regl(port, ureg->sirfsoc_int_en_reg,
 				rd_regl(port, ureg->sirfsoc_int_en_reg)&
 				rd_regl(port, ureg->sirfsoc_int_en_reg)&
 				~(SIRFUART_RX_IO_INT_EN(port, uint_en)));
 				~(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) {
 	if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) {
 		wr_regl(port, ureg->sirfsoc_afc_ctrl,
 		wr_regl(port, ureg->sirfsoc_afc_ctrl,
 				rd_regl(port, ureg->sirfsoc_afc_ctrl) & ~0x3FF);
 				rd_regl(port, ureg->sirfsoc_afc_ctrl) & ~0x3FF);
-		if (!sirfport->is_marco)
+		if (!sirfport->is_atlas7)
 			wr_regl(port, ureg->sirfsoc_int_en_reg,
 			wr_regl(port, ureg->sirfsoc_int_en_reg,
 					rd_regl(port, ureg->sirfsoc_int_en_reg)&
 					rd_regl(port, ureg->sirfsoc_int_en_reg)&
 					~uint_en->sirfsoc_cts_en);
 					~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,
 		wr_regl(port, ureg->sirfsoc_afc_ctrl,
 				rd_regl(port, ureg->sirfsoc_afc_ctrl) |
 				rd_regl(port, ureg->sirfsoc_afc_ctrl) |
 				SIRFUART_AFC_TX_EN | SIRFUART_AFC_RX_EN);
 				SIRFUART_AFC_TX_EN | SIRFUART_AFC_RX_EN);
-		if (!sirfport->is_marco)
+		if (!sirfport->is_atlas7)
 			wr_regl(port, ureg->sirfsoc_int_en_reg,
 			wr_regl(port, ureg->sirfsoc_int_en_reg,
 					rd_regl(port, ureg->sirfsoc_int_en_reg)
 					rd_regl(port, ureg->sirfsoc_int_en_reg)
 					| uint_en->sirfsoc_cts_en);
 					| uint_en->sirfsoc_cts_en);
@@ -544,7 +544,7 @@ static void sirfsoc_rx_tmo_process_tl(unsigned long param)
 		sirfport->rx_io_count = 0;
 		sirfport->rx_io_count = 0;
 		wr_regl(port, ureg->sirfsoc_int_st_reg,
 		wr_regl(port, ureg->sirfsoc_int_st_reg,
 				uint_st->sirfsoc_rx_done);
 				uint_st->sirfsoc_rx_done);
-		if (!sirfport->is_marco)
+		if (!sirfport->is_atlas7)
 			wr_regl(port, ureg->sirfsoc_int_en_reg,
 			wr_regl(port, ureg->sirfsoc_int_en_reg,
 				rd_regl(port, ureg->sirfsoc_int_en_reg) &
 				rd_regl(port, ureg->sirfsoc_int_en_reg) &
 				~(uint_en->sirfsoc_rx_done_en));
 				~(uint_en->sirfsoc_rx_done_en));
@@ -555,7 +555,7 @@ static void sirfsoc_rx_tmo_process_tl(unsigned long param)
 	} else {
 	} else {
 		wr_regl(port, ureg->sirfsoc_int_st_reg,
 		wr_regl(port, ureg->sirfsoc_int_st_reg,
 				uint_st->sirfsoc_rx_done);
 				uint_st->sirfsoc_rx_done);
-		if (!sirfport->is_marco)
+		if (!sirfport->is_atlas7)
 			wr_regl(port, ureg->sirfsoc_int_en_reg,
 			wr_regl(port, ureg->sirfsoc_int_en_reg,
 				rd_regl(port, ureg->sirfsoc_int_en_reg) |
 				rd_regl(port, ureg->sirfsoc_int_en_reg) |
 				(uint_en->sirfsoc_rx_done_en));
 				(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);
 	dmaengine_terminate_all(sirfport->rx_dma_chan);
 	sirfport->rx_dma_items[sirfport->rx_issued].xmit.head =
 	sirfport->rx_dma_items[sirfport->rx_issued].xmit.head =
 		SIRFSOC_RX_DMA_BUF_SIZE - tx_state.residue;
 		SIRFSOC_RX_DMA_BUF_SIZE - tx_state.residue;
-	if (!sirfport->is_marco)
+	if (!sirfport->is_atlas7)
 		wr_regl(port, ureg->sirfsoc_int_en_reg,
 		wr_regl(port, ureg->sirfsoc_int_en_reg,
 			rd_regl(port, ureg->sirfsoc_int_en_reg) &
 			rd_regl(port, ureg->sirfsoc_int_en_reg) &
 			~(uint_en->sirfsoc_rx_timeout_en));
 			~(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);
 	sirfsoc_uart_pio_rx_chars(port, 4 - sirfport->rx_io_count);
 	if (sirfport->rx_io_count == 4) {
 	if (sirfport->rx_io_count == 4) {
 		sirfport->rx_io_count = 0;
 		sirfport->rx_io_count = 0;
-		if (!sirfport->is_marco)
+		if (!sirfport->is_atlas7)
 			wr_regl(port, ureg->sirfsoc_int_en_reg,
 			wr_regl(port, ureg->sirfsoc_int_en_reg,
 				rd_regl(port, ureg->sirfsoc_int_en_reg) &
 				rd_regl(port, ureg->sirfsoc_int_en_reg) &
 				~(uint_en->sirfsoc_rx_done_en));
 				~(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++)
 	for (i = 0; i < SIRFSOC_RX_LOOP_BUF_CNT; i++)
 		sirfsoc_rx_submit_one_dma_desc(port, i);
 		sirfsoc_rx_submit_one_dma_desc(port, i);
 	sirfport->rx_completed = sirfport->rx_issued = 0;
 	sirfport->rx_completed = sirfport->rx_issued = 0;
-	if (!sirfport->is_marco)
+	if (!sirfport->is_atlas7)
 		wr_regl(port, ureg->sirfsoc_int_en_reg,
 		wr_regl(port, ureg->sirfsoc_int_en_reg,
 				rd_regl(port, ureg->sirfsoc_int_en_reg) |
 				rd_regl(port, ureg->sirfsoc_int_en_reg) |
 				SIRFUART_RX_DMA_INT_EN(port, uint_en));
 				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)
 	if (sirfport->rx_dma_chan)
 		sirfsoc_uart_start_next_rx_dma(port);
 		sirfsoc_uart_start_next_rx_dma(port);
 	else {
 	else {
-		if (!sirfport->is_marco)
+		if (!sirfport->is_atlas7)
 			wr_regl(port, ureg->sirfsoc_int_en_reg,
 			wr_regl(port, ureg->sirfsoc_int_en_reg,
 				rd_regl(port, ureg->sirfsoc_int_en_reg) |
 				rd_regl(port, ureg->sirfsoc_int_en_reg) |
 				SIRFUART_RX_IO_INT_EN(port, uint_en));
 				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_uart_port *sirfport = to_sirfport(port);
 	struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg;
 	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);
 		wr_regl(port, ureg->sirfsoc_int_en_reg, 0);
 	else
 	else
 		wr_regl(port, SIRFUART_INT_EN_CLR, ~0UL);
 		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[] = {
 static struct of_device_id sirfsoc_uart_ids[] = {
 	{ .compatible = "sirf,prima2-uart", .data = &sirfsoc_uart,},
 	{ .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},
 	{ .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);
 		gpio_direction_output(sirfport->rts_gpio, 1);
 	}
 	}
 usp_no_flow_control:
 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,
 	if (of_property_read_u32(pdev->dev.of_node,
 			"fifosize",
 			"fifosize",
@@ -1393,7 +1393,7 @@ usp_no_flow_control:
 		goto err;
 		goto err;
 	}
 	}
 	port->uartclk = clk_get_rate(sirfport->clk);
 	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");
 		sirfport->clk_general = devm_clk_get(&pdev->dev, "general");
 		if (IS_ERR(sirfport->clk_general)) {
 		if (IS_ERR(sirfport->clk_general)) {
 			ret = PTR_ERR(sirfport->clk_general);
 			ret = PTR_ERR(sirfport->clk_general);

+ 2 - 2
drivers/tty/serial/sirfsoc_uart.h

@@ -421,8 +421,8 @@ struct sirfsoc_uart_port {
 	bool				is_bt_uart;
 	bool				is_bt_uart;
 	struct clk			*clk_general;
 	struct clk			*clk_general;
 	struct clk			*clk_noc;
 	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 sirfsoc_uart_register	*uart_reg;
 	struct dma_chan			*rx_dma_chan;
 	struct dma_chan			*rx_dma_chan;
 	struct dma_chan			*tx_dma_chan;
 	struct dma_chan			*tx_dma_chan;

+ 793 - 0
drivers/tty/serial/sprd_serial.c

@@ -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");

+ 6 - 4
drivers/tty/serial/xilinx_uartps.c

@@ -637,10 +637,12 @@ static void cdns_uart_set_termios(struct uart_port *port,
 
 
 	spin_lock_irqsave(&port->lock, flags);
 	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 */
 	/* Disable the TX and RX to set baud rate */

+ 6 - 0
drivers/tty/tty_buffer.c

@@ -557,3 +557,9 @@ int tty_buffer_set_limit(struct tty_port *port, int limit)
 	return 0;
 	return 0;
 }
 }
 EXPORT_SYMBOL_GPL(tty_buffer_set_limit);
 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);
+}

+ 1 - 2
drivers/tty/tty_ioctl.c

@@ -530,7 +530,7 @@ EXPORT_SYMBOL(tty_termios_hw_change);
  *	Locking: termios_rwsem
  *	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 ktermios old_termios;
 	struct tty_ldisc *ld;
 	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);
 	up_write(&tty->termios_rwsem);
 	return 0;
 	return 0;
 }
 }
-EXPORT_SYMBOL_GPL(tty_set_termios);
 
 
 /**
 /**
  *	set_termios		-	set termios values for a tty
  *	set_termios		-	set termios values for a tty

+ 2 - 16
drivers/tty/tty_mutex.c

@@ -4,18 +4,8 @@
 #include <linux/semaphore.h>
 #include <linux/semaphore.h>
 #include <linux/sched.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 */
 /* Legacy tty mutex glue */
 
 
-enum {
-	TTY_MUTEX_NORMAL,
-	TTY_MUTEX_SLAVE,
-};
-
 /*
 /*
  * Getting the big tty mutex.
  * Getting the big tty mutex.
  */
  */
@@ -46,12 +36,8 @@ EXPORT_SYMBOL(tty_unlock);
 
 
 void __lockfunc tty_lock_slave(struct tty_struct *tty)
 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);
 		tty_lock(tty);
-	}
 }
 }
 
 
 void __lockfunc tty_unlock_slave(struct tty_struct *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)
 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);
 }
 }

+ 9 - 9
drivers/tty/vt/vt.c

@@ -500,6 +500,7 @@ void invert_screen(struct vc_data *vc, int offset, int count, int viewed)
 #endif
 #endif
 	if (DO_UPDATE(vc))
 	if (DO_UPDATE(vc))
 		do_update_region(vc, (unsigned long) p, count);
 		do_update_region(vc, (unsigned long) p, count);
+	notify_update(vc);
 }
 }
 
 
 /* used by selection: complement pointer position */
 /* 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));
 		scr_writew(old, screenpos(vc, old_offset, 1));
 		if (DO_UPDATE(vc))
 		if (DO_UPDATE(vc))
 			vc->vc_sw->con_putc(vc, old, oldy, oldx);
 			vc->vc_sw->con_putc(vc, old, oldy, oldx);
+		notify_update(vc);
 	}
 	}
 
 
 	old_offset = offset;
 	old_offset = offset;
@@ -533,8 +535,8 @@ void complement_pos(struct vc_data *vc, int offset)
 			oldy = (offset >> 1) / vc->vc_cols;
 			oldy = (offset >> 1) / vc->vc_cols;
 			vc->vc_sw->con_putc(vc, new, oldy, oldx);
 			vc->vc_sw->con_putc(vc, new, oldy, oldx);
 		}
 		}
+		notify_update(vc);
 	}
 	}
-
 }
 }
 
 
 static void insert_char(struct vc_data *vc, unsigned int nr)
 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)
 		if (first == 0 && last == MAX_NR_CONSOLES -1)
 			deflt = 1;
 			deflt = 1;
 
 
-		if (first != -1) {
-			console_lock();
+		if (first != -1)
 			do_bind_con_driver(csw, first, last, deflt);
 			do_bind_con_driver(csw, first, last, deflt);
-			console_unlock();
-		}
 
 
 		first = -1;
 		first = -1;
 		last = -1;
 		last = -1;
@@ -3362,9 +3361,7 @@ static int vt_unbind(struct con_driver *con)
 			deflt = 1;
 			deflt = 1;
 
 
 		if (first != -1) {
 		if (first != -1) {
-			console_lock();
 			ret = do_unbind_con_driver(csw, first, last, deflt);
 			ret = do_unbind_con_driver(csw, first, last, deflt);
-			console_unlock();
 			if (ret != 0)
 			if (ret != 0)
 				return ret;
 				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);
 	struct con_driver *con = dev_get_drvdata(dev);
 	int bind = simple_strtoul(buf, NULL, 0);
 	int bind = simple_strtoul(buf, NULL, 0);
 
 
+	console_lock();
+
 	if (bind)
 	if (bind)
 		vt_bind(con);
 		vt_bind(con);
 	else
 	else
 		vt_unbind(con);
 		vt_unbind(con);
 
 
+	console_unlock();
+
 	return count;
 	return count;
 }
 }
 
 
@@ -3665,8 +3666,7 @@ int do_unregister_con_driver(const struct consw *csw)
 	for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
 	for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
 		struct con_driver *con_driver = &registered_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);
 			vtconsole_deinit_device(con_driver);
 			device_destroy(vtconsole_class,
 			device_destroy(vtconsole_class,
 				       MKDEV(0, con_driver->node));
 				       MKDEV(0, con_driver->node));

+ 10 - 2
include/linux/pnp.h

@@ -12,6 +12,7 @@
 #include <linux/list.h>
 #include <linux/list.h>
 #include <linux/errno.h>
 #include <linux/errno.h>
 #include <linux/mod_devicetable.h>
 #include <linux/mod_devicetable.h>
+#include <linux/console.h>
 
 
 #define PNP_NAME_LEN		50
 #define PNP_NAME_LEN		50
 
 
@@ -309,15 +310,22 @@ struct pnp_fixup {
 #define PNP_DISABLE		0x0004
 #define PNP_DISABLE		0x0004
 #define PNP_CONFIGURABLE	0x0008
 #define PNP_CONFIGURABLE	0x0008
 #define PNP_REMOVABLE		0x0010
 #define PNP_REMOVABLE		0x0010
+#define PNP_CONSOLE		0x0020
 
 
 #define pnp_can_read(dev)	(((dev)->protocol->get) && \
 #define pnp_can_read(dev)	(((dev)->protocol->get) && \
 				 ((dev)->capabilities & PNP_READ))
 				 ((dev)->capabilities & PNP_READ))
 #define pnp_can_write(dev)	(((dev)->protocol->set) && \
 #define pnp_can_write(dev)	(((dev)->protocol->set) && \
 				 ((dev)->capabilities & PNP_WRITE))
 				 ((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) && \
 #define pnp_can_configure(dev)	((!(dev)->active) && \
 				 ((dev)->capabilities & PNP_CONFIGURABLE))
 				 ((dev)->capabilities & PNP_CONFIGURABLE))
+#define pnp_can_suspend(dev)	(((dev)->protocol->suspend) &&		  \
+				 (!((dev)->capabilities & PNP_CONSOLE) || \
+				  console_suspend_enabled))
+
 
 
 #ifdef CONFIG_ISAPNP
 #ifdef CONFIG_ISAPNP
 extern struct pnp_protocol isapnp_protocol;
 extern struct pnp_protocol isapnp_protocol;

+ 4 - 0
include/linux/serial_8250.h

@@ -85,6 +85,9 @@ struct uart_8250_port {
 	unsigned char		mcr_force;	/* mask of forced bits */
 	unsigned char		mcr_force;	/* mask of forced bits */
 	unsigned char		cur_iotype;	/* Running I/O type */
 	unsigned char		cur_iotype;	/* Running I/O type */
 	unsigned int		rpm_tx_active;
 	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
 	 * 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_shutdown(struct uart_port *port);
 extern void serial8250_do_pm(struct uart_port *port, unsigned int state,
 extern void serial8250_do_pm(struct uart_port *port, unsigned int state,
 			     unsigned int oldstate);
 			     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);
 extern int fsl8250_handle_irq(struct uart_port *port);
 int serial8250_handle_irq(struct uart_port *port, unsigned int iir);
 int serial8250_handle_irq(struct uart_port *port, unsigned int iir);
 unsigned char serial8250_rx_chars(struct uart_8250_port *up, unsigned char lsr);
 unsigned char serial8250_rx_chars(struct uart_8250_port *up, unsigned char lsr);

+ 19 - 3
include/linux/serial_core.h

@@ -123,6 +123,7 @@ struct uart_port {
 	void			(*set_termios)(struct uart_port *,
 	void			(*set_termios)(struct uart_port *,
 				               struct ktermios *new,
 				               struct ktermios *new,
 				               struct ktermios *old);
 				               struct ktermios *old);
+	void			(*set_mctrl)(struct uart_port *, unsigned int);
 	int			(*startup)(struct uart_port *port);
 	int			(*startup)(struct uart_port *port);
 	void			(*shutdown)(struct uart_port *port);
 	void			(*shutdown)(struct uart_port *port);
 	void			(*throttle)(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_NO_TXEN_TEST	((__force upf_t) (1 << 15))
 #define UPF_MAGIC_MULTIPLIER	((__force upf_t) ASYNC_MAGIC_MULTIPLIER /* 16 */ )
 #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 */
 /* Port has hardware-assisted s/w flow control */
 #define UPF_SOFT_FLOW		((__force upf_t) (1 << 22))
 #define UPF_SOFT_FLOW		((__force upf_t) (1 << 22))
 #define UPF_CONS_FLOW		((__force upf_t) (1 << 23))
 #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
 #error Change mask not equivalent to userspace-visible bit defines
 #endif
 #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;
 	upstat_t		status;
 
 
 #define UPSTAT_CTS_ENABLE	((__force upstat_t) (1 << 0))
 #define UPSTAT_CTS_ENABLE	((__force upstat_t) (1 << 0))
 #define UPSTAT_DCD_ENABLE	((__force upstat_t) (1 << 1))
 #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 */
 	int			hw_stopped;		/* sw-assisted CTS flow state */
 	unsigned int		mctrl;			/* current modem ctrl settings */
 	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);
 	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.
  * The following are helper functions for the low level drivers.
  */
  */

+ 28 - 0
include/linux/serial_s3c.h

@@ -104,6 +104,31 @@
 				   S3C2410_UCON_RXIRQMODE | \
 				   S3C2410_UCON_RXIRQMODE | \
 				   S3C2410_UCON_RXFIFO_TOI)
 				   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_FIFOMODE	  (1<<0)
 #define S3C2410_UFCON_TXTRIG0	  (0<<6)
 #define S3C2410_UFCON_TXTRIG0	  (0<<6)
 #define S3C2410_UFCON_RXTRIG8	  (1<<4)
 #define S3C2410_UFCON_RXTRIG8	  (1<<4)
@@ -155,6 +180,7 @@
 #define S3C2440_UFSTAT_TXMASK	  (63<<8)
 #define S3C2440_UFSTAT_TXMASK	  (63<<8)
 #define S3C2440_UFSTAT_RXMASK	  (63)
 #define S3C2440_UFSTAT_RXMASK	  (63)
 
 
+#define S3C2410_UTRSTAT_TIMEOUT   (1<<3)
 #define S3C2410_UTRSTAT_TXE	  (1<<2)
 #define S3C2410_UTRSTAT_TXE	  (1<<2)
 #define S3C2410_UTRSTAT_TXFE	  (1<<1)
 #define S3C2410_UTRSTAT_TXFE	  (1<<1)
 #define S3C2410_UTRSTAT_RXDR	  (1<<0)
 #define S3C2410_UTRSTAT_RXDR	  (1<<0)
@@ -179,8 +205,10 @@
 #define S3C64XX_UINTM		0x38
 #define S3C64XX_UINTM		0x38
 
 
 #define S3C64XX_UINTM_RXD	(0)
 #define S3C64XX_UINTM_RXD	(0)
+#define S3C64XX_UINTM_ERROR     (1)
 #define S3C64XX_UINTM_TXD	(2)
 #define S3C64XX_UINTM_TXD	(2)
 #define S3C64XX_UINTM_RXD_MSK	(1 << S3C64XX_UINTM_RXD)
 #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)
 #define S3C64XX_UINTM_TXD_MSK	(1 << S3C64XX_UINTM_TXD)
 
 
 /* Following are specific to S5PV210 */
 /* Following are specific to S5PV210 */

+ 24 - 1
include/linux/tty.h

@@ -14,6 +14,29 @@
 #include <linux/llist.h>
 #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
  * (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_free_all(struct tty_port *port);
 extern void tty_buffer_flush(struct tty_struct *tty, struct tty_ldisc *ld);
 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_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_baud_rate(struct ktermios *termios);
 extern speed_t tty_termios_input_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,
 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 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_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 struct tty_ldisc *tty_ldisc_ref(struct tty_struct *);
 extern void tty_ldisc_deref(struct tty_ldisc *);
 extern void tty_ldisc_deref(struct tty_ldisc *);

+ 0 - 4
include/linux/vt_buffer.h

@@ -21,10 +21,6 @@
 #ifndef VT_BUF_HAVE_RW
 #ifndef VT_BUF_HAVE_RW
 #define scr_writew(val, addr) (*(addr) = (val))
 #define scr_writew(val, addr) (*(addr) = (val))
 #define scr_readw(addr) (*(addr))
 #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
 #endif
 
 
 #ifndef VT_BUF_HAVE_MEMSETW
 #ifndef VT_BUF_HAVE_MEMSETW

+ 11 - 1
include/uapi/linux/serial_core.h

@@ -55,7 +55,8 @@
 #define PORT_ALTR_16550_F64 27	/* Altera 16550 UART with 64 FIFOs */
 #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_ALTR_16550_F128 28 /* Altera 16550 UART with 128 FIFOs */
 #define PORT_RT2880	29	/* Ralink RT2880 internal UART */
 #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
  * ARM specific type numbers.  These are not currently guaranteed
@@ -248,4 +249,13 @@
 /* MESON */
 /* MESON */
 #define PORT_MESON	109
 #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 */
 #endif /* _UAPILINUX_SERIAL_CORE_H */

+ 2 - 1
include/uapi/linux/serial_reg.h

@@ -86,7 +86,8 @@
 #define UART_FCR6_T_TRIGGER_8	0x10 /* Mask for transmit trigger set at 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_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_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_SHIFT		6
 #define UART_FCR_R_TRIG_BITS(x)		\
 #define UART_FCR_R_TRIG_BITS(x)		\