Browse Source

Merge tag 'tty-3.12-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 pull request for 3.12-rc1.

  Lots of n_tty reworks to resolve some very long-standing issues,
  removing the 3-4 different locks that were taken for every character.
  This code has been beaten on for a long time in linux-next with no
  reported regressions.

  Other than that, a range of serial and tty driver updates and
  revisions.  Full details in the shortlog"

* tag 'tty-3.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty: (226 commits)
  hvc_xen: Remove unnecessary __GFP_ZERO from kzalloc
  serial: imx: initialize the local variable
  tty: ar933x_uart: add device tree support and binding documentation
  tty: ar933x_uart: allow to build the driver as a module
  ARM: dts: msm: Update uartdm compatible strings
  devicetree: serial: Document msm_serial bindings
  serial: unify serial bindings into a single dir
  serial: fsl-imx-uart: Cleanup duplicate device tree binding
  tty: ar933x_uart: use config_enabled() macro to clean up ifdefs
  tty: ar933x_uart: remove superfluous assignment of ar933x_uart_driver.nr
  tty: ar933x_uart: use the clk API to get the uart clock
  tty: serial: cpm_uart: Adding proper request of GPIO used by cpm_uart driver
  serial: sirf: fix the amount of serial ports
  serial: sirf: define macro for some magic numbers of USP
  serial: icom: move array overflow checks earlier
  TTY: amiserial, remove unnecessary platform_set_drvdata()
  serial: st-asc: remove unnecessary platform_set_drvdata()
  msm_serial: Send more than 1 character on the console w/ UARTDM
  msm_serial: Add support for non-GSBI UARTDM devices
  msm_serial: Switch clock consumer strings and simplify code
  ...
Linus Torvalds 12 years ago
parent
commit
2f01ea908b
100 changed files with 6168 additions and 2326 deletions
  1. 0 0
      Documentation/devicetree/bindings/serial/arc-uart.txt
  2. 17 1
      Documentation/devicetree/bindings/serial/atmel-usart.txt
  3. 0 0
      Documentation/devicetree/bindings/serial/efm32-uart.txt
  4. 8 14
      Documentation/devicetree/bindings/serial/fsl-imx-uart.txt
  5. 0 0
      Documentation/devicetree/bindings/serial/fsl-lpuart.txt
  6. 4 0
      Documentation/devicetree/bindings/serial/fsl-mxs-auart.txt
  7. 0 0
      Documentation/devicetree/bindings/serial/nxp-lpc32xx-hsuart.txt
  8. 0 0
      Documentation/devicetree/bindings/serial/of-serial.txt
  9. 25 0
      Documentation/devicetree/bindings/serial/qcom,msm-uart.txt
  10. 53 0
      Documentation/devicetree/bindings/serial/qcom,msm-uartdm.txt
  11. 33 0
      Documentation/devicetree/bindings/serial/sirf-uart.txt
  12. 0 0
      Documentation/devicetree/bindings/serial/snps-dw-apb-uart.txt
  13. 18 0
      Documentation/devicetree/bindings/serial/st-asc.txt
  14. 0 0
      Documentation/devicetree/bindings/serial/via,vt8500-uart.txt
  15. 0 22
      Documentation/devicetree/bindings/tty/serial/fsl-imx-uart.txt
  16. 0 27
      Documentation/devicetree/bindings/tty/serial/msm_serial.txt
  17. 34 0
      Documentation/devicetree/bindings/tty/serial/qca,ar9330-uart.txt
  18. 10 0
      Documentation/kernel-parameters.txt
  19. 1 0
      arch/arm/boot/dts/imx28-evk.dts
  20. 1 1
      arch/arm/boot/dts/msm8660-surf.dts
  21. 1 1
      arch/arm/boot/dts/msm8960-cdp.dts
  22. 3 3
      arch/arm/mach-msm/devices-msm7x00.c
  23. 1 1
      arch/arm/mach-msm/devices-msm7x30.c
  24. 3 3
      arch/arm/mach-msm/devices-qsd8x50.c
  25. 0 1
      arch/mips/sni/a20r.c
  26. 4 4
      drivers/net/irda/irtty-sir.c
  27. 26 0
      drivers/of/base.c
  28. 0 1
      drivers/staging/comedi/comedidev.h
  29. 2 0
      drivers/staging/dgrp/dgrp_tty.c
  30. 0 2
      drivers/tty/amiserial.c
  31. 10 1
      drivers/tty/hvc/hvc_console.c
  32. 3 0
      drivers/tty/hvc/hvc_console.h
  33. 51 13
      drivers/tty/hvc/hvc_iucv.c
  34. 3 3
      drivers/tty/hvc/hvc_xen.c
  35. 16 13
      drivers/tty/n_gsm.c
  36. 845 569
      drivers/tty/n_tty.c
  37. 6 12
      drivers/tty/pty.c
  38. 1 1
      drivers/tty/serial/8250/8250_core.c
  39. 26 8
      drivers/tty/serial/8250/8250_dw.c
  40. 1 1
      drivers/tty/serial/8250/8250_early.c
  41. 8 19
      drivers/tty/serial/8250/8250_em.c
  42. 11 4
      drivers/tty/serial/8250/8250_pci.c
  43. 2 0
      drivers/tty/serial/8250/Kconfig
  44. 26 7
      drivers/tty/serial/Kconfig
  45. 1 0
      drivers/tty/serial/Makefile
  46. 4 1
      drivers/tty/serial/altera_jtaguart.c
  47. 3 1
      drivers/tty/serial/altera_uart.c
  48. 1 1
      drivers/tty/serial/amba-pl010.c
  49. 11 7
      drivers/tty/serial/amba-pl011.c
  50. 2 0
      drivers/tty/serial/apbuart.c
  51. 67 44
      drivers/tty/serial/ar933x_uart.c
  52. 20 15
      drivers/tty/serial/arc_uart.c
  53. 709 148
      drivers/tty/serial/atmel_serial.c
  54. 2 1
      drivers/tty/serial/bcm63xx_uart.c
  55. 8 6
      drivers/tty/serial/bfin_sport_uart.c
  56. 9 12
      drivers/tty/serial/bfin_uart.c
  57. 2 9
      drivers/tty/serial/clps711x.c
  58. 26 2
      drivers/tty/serial/cpm_uart/cpm_uart_core.c
  59. 16 13
      drivers/tty/serial/efm32-uart.c
  60. 6 1
      drivers/tty/serial/fsl_lpuart.c
  61. 56 47
      drivers/tty/serial/icom.c
  62. 1 1
      drivers/tty/serial/ifx6x60.c
  63. 471 49
      drivers/tty/serial/imx.c
  64. 2 2
      drivers/tty/serial/ioc4_serial.c
  65. 5 2
      drivers/tty/serial/lantiq.c
  66. 4 3
      drivers/tty/serial/lpc32xx_hs.c
  67. 3 0
      drivers/tty/serial/m32r_sio.c
  68. 1 1
      drivers/tty/serial/max3100.c
  69. 522 469
      drivers/tty/serial/max310x.c
  70. 4 1
      drivers/tty/serial/mcf.c
  71. 10 4
      drivers/tty/serial/mfd.c
  72. 10 5
      drivers/tty/serial/mpsc.c
  73. 2 2
      drivers/tty/serial/mrst_max3110.c
  74. 158 118
      drivers/tty/serial/msm_serial.c
  75. 5 14
      drivers/tty/serial/msm_serial.h
  76. 1 1
      drivers/tty/serial/msm_serial_hs.c
  77. 5 11
      drivers/tty/serial/mxs-auart.c
  78. 4 4
      drivers/tty/serial/netx-serial.c
  79. 3 0
      drivers/tty/serial/nwpserial.c
  80. 199 15
      drivers/tty/serial/omap-serial.c
  81. 56 28
      drivers/tty/serial/pch_uart.c
  82. 0 1
      drivers/tty/serial/pmac_zilog.c
  83. 3 2
      drivers/tty/serial/pnx8xxx_uart.c
  84. 3 30
      drivers/tty/serial/pxa.c
  85. 2 0
      drivers/tty/serial/rp2.c
  86. 3 2
      drivers/tty/serial/sa1100.c
  87. 6 3
      drivers/tty/serial/samsung.c
  88. 2 1
      drivers/tty/serial/samsung.h
  89. 1 1
      drivers/tty/serial/sc26xx.c
  90. 158 181
      drivers/tty/serial/sccnxp.c
  91. 11 5
      drivers/tty/serial/serial-tegra.c
  92. 2 2
      drivers/tty/serial/serial_core.c
  93. 1 1
      drivers/tty/serial/serial_txx9.c
  94. 2 2
      drivers/tty/serial/sh-sci.c
  95. 981 214
      drivers/tty/serial/sirfsoc_uart.c
  96. 394 107
      drivers/tty/serial/sirfsoc_uart.h
  97. 932 0
      drivers/tty/serial/st-asc.c
  98. 2 2
      drivers/tty/serial/timbuart.c
  99. 1 1
      drivers/tty/serial/vr41xx_siu.c
  100. 2 1
      drivers/tty/serial/vt8500_serial.c

+ 0 - 0
Documentation/devicetree/bindings/tty/serial/arc-uart.txt → Documentation/devicetree/bindings/serial/arc-uart.txt


+ 17 - 1
Documentation/devicetree/bindings/tty/serial/atmel-usart.txt → Documentation/devicetree/bindings/serial/atmel-usart.txt

@@ -10,13 +10,18 @@ Required properties:
 Optional properties:
 Optional properties:
 - atmel,use-dma-rx: use of PDC or DMA for receiving data
 - atmel,use-dma-rx: use of PDC or DMA for receiving data
 - atmel,use-dma-tx: use of PDC or DMA for transmitting data
 - atmel,use-dma-tx: use of PDC or DMA for transmitting data
+- add dma bindings for dma transfer:
+	- dmas: DMA specifier, consisting of a phandle to DMA controller node,
+		memory peripheral interface and USART DMA channel ID, FIFO configuration.
+		Refer to dma.txt and atmel-dma.txt for details.
+	- dma-names: "rx" for RX channel, "tx" for TX channel.
 
 
 <chip> compatible description:
 <chip> compatible description:
 - at91rm9200:  legacy USART support
 - at91rm9200:  legacy USART support
 - at91sam9260: generic USART implementation for SAM9 SoCs
 - at91sam9260: generic USART implementation for SAM9 SoCs
 
 
 Example:
 Example:
-
+- use PDC:
 	usart0: serial@fff8c000 {
 	usart0: serial@fff8c000 {
 		compatible = "atmel,at91sam9260-usart";
 		compatible = "atmel,at91sam9260-usart";
 		reg = <0xfff8c000 0x4000>;
 		reg = <0xfff8c000 0x4000>;
@@ -25,3 +30,14 @@ Example:
 		atmel,use-dma-tx;
 		atmel,use-dma-tx;
 	};
 	};
 
 
+- use DMA:
+	usart0: serial@f001c000 {
+		compatible = "atmel,at91sam9260-usart";
+		reg = <0xf001c000 0x100>;
+		interrupts = <12 4 5>;
+		atmel,use-dma-rx;
+		atmel,use-dma-tx;
+		dmas = <&dma0 2 0x3>,
+		       <&dma0 2 0x204>;
+		dma-names = "tx", "rx";
+	};

+ 0 - 0
Documentation/devicetree/bindings/tty/serial/efm32-uart.txt → Documentation/devicetree/bindings/serial/efm32-uart.txt


+ 8 - 14
Documentation/devicetree/bindings/serial/fsl-imx-uart.txt

@@ -1,35 +1,29 @@
-* Freescale i.MX UART controller
+* Freescale i.MX Universal Asynchronous Receiver/Transmitter (UART)
 
 
 Required properties:
 Required properties:
-- compatible : should be "fsl,imx21-uart"
+- compatible : Should be "fsl,<soc>-uart"
 - reg : Address and length of the register set for the device
 - reg : Address and length of the register set for the device
-- interrupts : Should contain UART interrupt number
+- interrupts : Should contain uart interrupt
 
 
 Optional properties:
 Optional properties:
-- fsl,uart-has-rtscts: indicate that RTS/CTS signals are used
+- fsl,uart-has-rtscts : Indicate the uart has rts and cts
+- fsl,irda-mode : Indicate the uart supports irda mode
+- fsl,dte-mode : Indicate the uart works in DTE mode. The uart works
+                  is DCE mode by default.
 
 
 Note: Each uart controller should have an alias correctly numbered
 Note: Each uart controller should have an alias correctly numbered
 in "aliases" node.
 in "aliases" node.
 
 
 Example:
 Example:
 
 
-- From imx51.dtsi:
 aliases {
 aliases {
 	serial0 = &uart1;
 	serial0 = &uart1;
-	serial1 = &uart2;
-	serial2 = &uart3;
 };
 };
 
 
 uart1: serial@73fbc000 {
 uart1: serial@73fbc000 {
 	compatible = "fsl,imx51-uart", "fsl,imx21-uart";
 	compatible = "fsl,imx51-uart", "fsl,imx21-uart";
 	reg = <0x73fbc000 0x4000>;
 	reg = <0x73fbc000 0x4000>;
 	interrupts = <31>;
 	interrupts = <31>;
-	status = "disabled";
-}
-
-- From imx51-babbage.dts:
-uart1: serial@73fbc000 {
 	fsl,uart-has-rtscts;
 	fsl,uart-has-rtscts;
-	status = "okay";
+	fsl,dte-mode;
 };
 };
-

+ 0 - 0
Documentation/devicetree/bindings/tty/serial/fsl-lpuart.txt → Documentation/devicetree/bindings/serial/fsl-lpuart.txt


+ 4 - 0
Documentation/devicetree/bindings/tty/serial/fsl-mxs-auart.txt → Documentation/devicetree/bindings/serial/fsl-mxs-auart.txt

@@ -10,6 +10,10 @@ Required properties:
   Refer to dma.txt and fsl-mxs-dma.txt for details.
   Refer to dma.txt and fsl-mxs-dma.txt for details.
 - dma-names: "rx" for RX channel, "tx" for TX channel.
 - dma-names: "rx" for RX channel, "tx" for TX channel.
 
 
+Optional properties:
+- fsl,uart-has-rtscts : Indicate the UART has RTS and CTS lines,
+	it also means you enable the DMA support for this UART.
+
 Example:
 Example:
 auart0: serial@8006a000 {
 auart0: serial@8006a000 {
 	compatible = "fsl,imx28-auart", "fsl,imx23-auart";
 	compatible = "fsl,imx28-auart", "fsl,imx23-auart";

+ 0 - 0
Documentation/devicetree/bindings/tty/serial/nxp-lpc32xx-hsuart.txt → Documentation/devicetree/bindings/serial/nxp-lpc32xx-hsuart.txt


+ 0 - 0
Documentation/devicetree/bindings/tty/serial/of-serial.txt → Documentation/devicetree/bindings/serial/of-serial.txt


+ 25 - 0
Documentation/devicetree/bindings/serial/qcom,msm-uart.txt

@@ -0,0 +1,25 @@
+* MSM Serial UART
+
+The MSM serial UART hardware is designed for low-speed use cases where a
+dma-engine isn't needed. From a software perspective it's mostly compatible
+with the MSM serial UARTDM except that it only supports reading and writing one
+character at a time.
+
+Required properties:
+- compatible: Should contain "qcom,msm-uart"
+- reg: Should contain UART register location and length.
+- interrupts: Should contain UART interrupt.
+- clocks: Should contain the core clock.
+- clock-names: Should be "core".
+
+Example:
+
+A uart device at 0xa9c00000 with interrupt 11.
+
+serial@a9c00000 {
+	compatible = "qcom,msm-uart";
+	reg = <0xa9c00000 0x1000>;
+	interrupts = <11>;
+	clocks = <&uart_cxc>;
+	clock-names = "core";
+};

+ 53 - 0
Documentation/devicetree/bindings/serial/qcom,msm-uartdm.txt

@@ -0,0 +1,53 @@
+* MSM Serial UARTDM
+
+The MSM serial UARTDM hardware is designed for high-speed use cases where the
+transmit and/or receive channels can be offloaded to a dma-engine. From a
+software perspective it's mostly compatible with the MSM serial UART except
+that it supports reading and writing multiple characters at a time.
+
+Required properties:
+- compatible: Should contain at least "qcom,msm-uartdm".
+              A more specific property should be specified as follows depending
+	      on the version:
+		"qcom,msm-uartdm-v1.1"
+		"qcom,msm-uartdm-v1.2"
+		"qcom,msm-uartdm-v1.3"
+		"qcom,msm-uartdm-v1.4"
+- reg: Should contain UART register locations and lengths. The first
+       register shall specify the main control registers. An optional second
+       register location shall specify the GSBI control region.
+       "qcom,msm-uartdm-v1.3" is the only compatible value that might
+       need the GSBI control region.
+- interrupts: Should contain UART interrupt.
+- clocks: Should contain the core clock and the AHB clock.
+- clock-names: Should be "core" for the core clock and "iface" for the
+	       AHB clock.
+
+Optional properties:
+- dmas: Should contain dma specifiers for transmit and receive channels
+- dma-names: Should contain "tx" for transmit and "rx" for receive channels
+
+Examples:
+
+A uartdm v1.4 device with dma capabilities.
+
+serial@f991e000 {
+	compatible = "qcom,msm-uartdm-v1.4", "qcom,msm-uartdm";
+	reg = <0xf991e000 0x1000>;
+	interrupts = <0 108 0x0>;
+	clocks = <&blsp1_uart2_apps_cxc>, <&blsp1_ahb_cxc>;
+	clock-names = "core", "iface";
+	dmas = <&dma0 0>, <&dma0 1>;
+	dma-names = "tx", "rx";
+};
+
+A uartdm v1.3 device without dma capabilities and part of a GSBI complex.
+
+serial@19c40000 {
+	compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm";
+	reg = <0x19c40000 0x1000>,
+	      <0x19c00000 0x1000>;
+	interrupts = <0 195 0x0>;
+	clocks = <&gsbi5_uart_cxc>, <&gsbi5_ahb_cxc>;
+	clock-names = "core", "iface";
+};

+ 33 - 0
Documentation/devicetree/bindings/serial/sirf-uart.txt

@@ -0,0 +1,33 @@
+* CSR SiRFprimaII/atlasVI Universal Synchronous Asynchronous Receiver/Transmitter *
+
+Required properties:
+- compatible : Should be "sirf,prima2-uart" or "sirf, prima2-usp-uart"
+- reg : Offset and length of the register set for the device
+- interrupts : Should contain uart interrupt
+- fifosize : Should define hardware rx/tx fifo size
+- clocks : Should contain uart clock number
+
+Optional properties:
+- sirf,uart-has-rtscts: we have hardware flow controller pins in hardware
+- rts-gpios: RTS pin for USP-based UART if sirf,uart-has-rtscts is true
+- cts-gpios: CTS pin for USP-based UART if sirf,uart-has-rtscts is true
+
+Example:
+
+uart0: uart@b0050000 {
+	cell-index = <0>;
+	compatible = "sirf,prima2-uart";
+	reg = <0xb0050000 0x1000>;
+	interrupts = <17>;
+	fifosize = <128>;
+	clocks = <&clks 13>;
+};
+
+On the board-specific dts, we can put rts-gpios and cts-gpios like
+
+usp@b0090000 {
+	compatible = "sirf,prima2-usp-uart";
+	sirf,uart-has-rtscts;
+	rts-gpios = <&gpio 15 0>;
+	cts-gpios = <&gpio 46 0>;
+};

+ 0 - 0
Documentation/devicetree/bindings/tty/serial/snps-dw-apb-uart.txt → Documentation/devicetree/bindings/serial/snps-dw-apb-uart.txt


+ 18 - 0
Documentation/devicetree/bindings/serial/st-asc.txt

@@ -0,0 +1,18 @@
+*st-asc(Serial Port)
+
+Required properties:
+- compatible : Should be "st,asc".
+- reg, reg-names, interrupts, interrupt-names	: Standard way to define device
+			resources with names. look in
+			Documentation/devicetree/bindings/resource-names.txt
+
+Optional properties:
+- st,hw-flow-ctrl	bool flag to enable hardware flow control.
+- st,force-m1		bool flat to force asc to be in Mode-1 recommeded
+			for high bit rates (above 19.2K)
+Example:
+serial@fe440000{
+    compatible    = "st,asc";
+    reg         = <0xfe440000 0x2c>;
+    interrupts     =  <0 209 0>;
+};

+ 0 - 0
Documentation/devicetree/bindings/tty/serial/via,vt8500-uart.txt → Documentation/devicetree/bindings/serial/via,vt8500-uart.txt


+ 0 - 22
Documentation/devicetree/bindings/tty/serial/fsl-imx-uart.txt

@@ -1,22 +0,0 @@
-* Freescale i.MX Universal Asynchronous Receiver/Transmitter (UART)
-
-Required properties:
-- compatible : Should be "fsl,<soc>-uart"
-- reg : Address and length of the register set for the device
-- interrupts : Should contain uart interrupt
-
-Optional properties:
-- fsl,uart-has-rtscts : Indicate the uart has rts and cts
-- fsl,irda-mode : Indicate the uart supports irda mode
-- fsl,dte-mode : Indicate the uart works in DTE mode. The uart works
-                  is DCE mode by default.
-
-Example:
-
-serial@73fbc000 {
-	compatible = "fsl,imx51-uart", "fsl,imx21-uart";
-	reg = <0x73fbc000 0x4000>;
-	interrupts = <31>;
-	fsl,uart-has-rtscts;
-	fsl,dte-mode;
-};

+ 0 - 27
Documentation/devicetree/bindings/tty/serial/msm_serial.txt

@@ -1,27 +0,0 @@
-* Qualcomm MSM UART
-
-Required properties:
-- compatible :
-	- "qcom,msm-uart", and one of "qcom,msm-hsuart" or
-	  "qcom,msm-lsuart".
-- reg : offset and length of the register set for the device
-	for the hsuart operating in compatible mode, there should be a
-	second pair describing the gsbi registers.
-- interrupts : should contain the uart interrupt.
-
-There are two different UART blocks used in MSM devices,
-"qcom,msm-hsuart" and "qcom,msm-lsuart".  The msm-serial driver is
-able to handle both of these, and matches against the "qcom,msm-uart"
-as the compatibility.
-
-The registers for the "qcom,msm-hsuart" device need to specify both
-register blocks, even for the common driver.
-
-Example:
-
-	uart@19c400000 {
-		compatible = "qcom,msm-hsuart", "qcom,msm-uart";
-		reg = <0x19c40000 0x1000>,
-		      <0x19c00000 0x1000>;
-		interrupts = <195>;
-	};

+ 34 - 0
Documentation/devicetree/bindings/tty/serial/qca,ar9330-uart.txt

@@ -0,0 +1,34 @@
+* Qualcomm Atheros AR9330 High-Speed UART
+
+Required properties:
+
+- compatible: Must be "qca,ar9330-uart"
+
+- reg: Specifies the physical base address of the controller and
+  the length of the memory mapped region.
+
+- interrupt-parent: The phandle for the interrupt controller that
+  services interrupts for this device.
+
+- interrupts: Specifies the interrupt source of the parent interrupt
+  controller. The format of the interrupt specifier depends on the
+  parent interrupt controller.
+
+Additional requirements:
+
+  Each UART port must have an alias correctly numbered in "aliases"
+  node.
+
+Example:
+
+	aliases {
+		serial0 = &uart0;
+	};
+
+	uart0: uart@18020000 {
+		compatible = "qca,ar9330-uart";
+		reg = <0x18020000 0x14>;
+
+		interrupt-parent = <&intc>;
+		interrupts = <3>;
+	};

+ 10 - 0
Documentation/kernel-parameters.txt

@@ -3322,6 +3322,10 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
 			            them quite hard to use for exploits but
 			            them quite hard to use for exploits but
 			            might break your system.
 			            might break your system.
 
 
+	vt.color=	[VT] Default text color.
+			Format: 0xYX, X = foreground, Y = background.
+			Default: 0x07 = light gray on black.
+
 	vt.cur_default=	[VT] Default cursor shape.
 	vt.cur_default=	[VT] Default cursor shape.
 			Format: 0xCCBBAA, where AA, BB, and CC are the same as
 			Format: 0xCCBBAA, where AA, BB, and CC are the same as
 			the parameters of the <Esc>[?A;B;Cc escape sequence;
 			the parameters of the <Esc>[?A;B;Cc escape sequence;
@@ -3361,6 +3365,12 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
 			overridden by individual drivers. 0 will hide
 			overridden by individual drivers. 0 will hide
 			cursors, 1 will display them.
 			cursors, 1 will display them.
 
 
+	vt.italic=	[VT] Default color for italic text; 0-15.
+			Default: 2 = green.
+
+	vt.underline=	[VT] Default color for underlined text; 0-15.
+			Default: 3 = cyan.
+
 	watchdog timers	[HW,WDT] For information on watchdog timers,
 	watchdog timers	[HW,WDT] For information on watchdog timers,
 			see Documentation/watchdog/watchdog-parameters.txt
 			see Documentation/watchdog/watchdog-parameters.txt
 			or other driver-specific files in the
 			or other driver-specific files in the

+ 1 - 0
arch/arm/boot/dts/imx28-evk.dts

@@ -220,6 +220,7 @@
 			auart0: serial@8006a000 {
 			auart0: serial@8006a000 {
 				pinctrl-names = "default";
 				pinctrl-names = "default";
 				pinctrl-0 = <&auart0_pins_a>;
 				pinctrl-0 = <&auart0_pins_a>;
+				fsl,uart-has-rtscts;
 				status = "okay";
 				status = "okay";
 			};
 			};
 
 

+ 1 - 1
arch/arm/boot/dts/msm8660-surf.dts

@@ -38,7 +38,7 @@
 	};
 	};
 
 
 	serial@19c40000 {
 	serial@19c40000 {
-		compatible = "qcom,msm-hsuart", "qcom,msm-uart";
+		compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm";
 		reg = <0x19c40000 0x1000>,
 		reg = <0x19c40000 0x1000>,
 		      <0x19c00000 0x1000>;
 		      <0x19c00000 0x1000>;
 		interrupts = <0 195 0x0>;
 		interrupts = <0 195 0x0>;

+ 1 - 1
arch/arm/boot/dts/msm8960-cdp.dts

@@ -38,7 +38,7 @@
 	};
 	};
 
 
 	serial@16440000 {
 	serial@16440000 {
-		compatible = "qcom,msm-hsuart", "qcom,msm-uart";
+		compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm";
 		reg = <0x16440000 0x1000>,
 		reg = <0x16440000 0x1000>,
 		      <0x16400000 0x1000>;
 		      <0x16400000 0x1000>;
 		interrupts = <0 154 0x0>;
 		interrupts = <0 154 0x0>;

+ 3 - 3
arch/arm/mach-msm/devices-msm7x00.c

@@ -456,9 +456,9 @@ static struct clk_pcom_desc msm_clocks_7x01a[] = {
 	CLK_PCOM("tsif_ref_clk",	TSIF_REF_CLK,	NULL, 0),
 	CLK_PCOM("tsif_ref_clk",	TSIF_REF_CLK,	NULL, 0),
 	CLK_PCOM("tv_dac_clk",	TV_DAC_CLK,	NULL, 0),
 	CLK_PCOM("tv_dac_clk",	TV_DAC_CLK,	NULL, 0),
 	CLK_PCOM("tv_enc_clk",	TV_ENC_CLK,	NULL, 0),
 	CLK_PCOM("tv_enc_clk",	TV_ENC_CLK,	NULL, 0),
-	CLK_PCOM("uart_clk",	UART1_CLK,	"msm_serial.0", OFF),
-	CLK_PCOM("uart_clk",	UART2_CLK,	"msm_serial.1", 0),
-	CLK_PCOM("uart_clk",	UART3_CLK,	"msm_serial.2", OFF),
+	CLK_PCOM("core",	UART1_CLK,	"msm_serial.0", OFF),
+	CLK_PCOM("core",	UART2_CLK,	"msm_serial.1", 0),
+	CLK_PCOM("core",	UART3_CLK,	"msm_serial.2", OFF),
 	CLK_PCOM("uart1dm_clk",	UART1DM_CLK,	NULL, OFF),
 	CLK_PCOM("uart1dm_clk",	UART1DM_CLK,	NULL, OFF),
 	CLK_PCOM("uart2dm_clk",	UART2DM_CLK,	NULL, 0),
 	CLK_PCOM("uart2dm_clk",	UART2DM_CLK,	NULL, 0),
 	CLK_PCOM("usb_hs_clk",	USB_HS_CLK,	"msm_hsusb", OFF),
 	CLK_PCOM("usb_hs_clk",	USB_HS_CLK,	"msm_hsusb", OFF),

+ 1 - 1
arch/arm/mach-msm/devices-msm7x30.c

@@ -211,7 +211,7 @@ static struct clk_pcom_desc msm_clocks_7x30[] = {
 	CLK_PCOM("spi_pclk",	SPI_P_CLK,	NULL, 0),
 	CLK_PCOM("spi_pclk",	SPI_P_CLK,	NULL, 0),
 	CLK_PCOM("tv_dac_clk",	TV_DAC_CLK,	NULL, 0),
 	CLK_PCOM("tv_dac_clk",	TV_DAC_CLK,	NULL, 0),
 	CLK_PCOM("tv_enc_clk",	TV_ENC_CLK,	NULL, 0),
 	CLK_PCOM("tv_enc_clk",	TV_ENC_CLK,	NULL, 0),
-	CLK_PCOM("uart_clk",	UART2_CLK,	"msm_serial.1", 0),
+	CLK_PCOM("core",	UART2_CLK,	"msm_serial.1", 0),
 	CLK_PCOM("usb_phy_clk",	USB_PHY_CLK,	NULL, 0),
 	CLK_PCOM("usb_phy_clk",	USB_PHY_CLK,	NULL, 0),
 	CLK_PCOM("usb_hs_clk",		USB_HS_CLK,		NULL, OFF),
 	CLK_PCOM("usb_hs_clk",		USB_HS_CLK,		NULL, OFF),
 	CLK_PCOM("usb_hs_pclk",		USB_HS_P_CLK,		NULL, OFF),
 	CLK_PCOM("usb_hs_pclk",		USB_HS_P_CLK,		NULL, OFF),

+ 3 - 3
arch/arm/mach-msm/devices-qsd8x50.c

@@ -358,9 +358,9 @@ static struct clk_pcom_desc msm_clocks_8x50[] = {
 	CLK_PCOM("tsif_ref_clk",	TSIF_REF_CLK,	NULL, 0),
 	CLK_PCOM("tsif_ref_clk",	TSIF_REF_CLK,	NULL, 0),
 	CLK_PCOM("tv_dac_clk",	TV_DAC_CLK,	NULL, 0),
 	CLK_PCOM("tv_dac_clk",	TV_DAC_CLK,	NULL, 0),
 	CLK_PCOM("tv_enc_clk",	TV_ENC_CLK,	NULL, 0),
 	CLK_PCOM("tv_enc_clk",	TV_ENC_CLK,	NULL, 0),
-	CLK_PCOM("uart_clk",	UART1_CLK,	NULL, OFF),
-	CLK_PCOM("uart_clk",	UART2_CLK,	NULL, 0),
-	CLK_PCOM("uart_clk",	UART3_CLK,	"msm_serial.2", OFF),
+	CLK_PCOM("core",	UART1_CLK,	NULL, OFF),
+	CLK_PCOM("core",	UART2_CLK,	NULL, 0),
+	CLK_PCOM("core",	UART3_CLK,	"msm_serial.2", OFF),
 	CLK_PCOM("uartdm_clk",	UART1DM_CLK,	NULL, OFF),
 	CLK_PCOM("uartdm_clk",	UART1DM_CLK,	NULL, OFF),
 	CLK_PCOM("uartdm_clk",	UART2DM_CLK,	NULL, 0),
 	CLK_PCOM("uartdm_clk",	UART2DM_CLK,	NULL, 0),
 	CLK_PCOM("usb_hs_clk",	USB_HS_CLK,	NULL, OFF),
 	CLK_PCOM("usb_hs_clk",	USB_HS_CLK,	NULL, OFF),

+ 0 - 1
arch/mips/sni/a20r.c

@@ -122,7 +122,6 @@ static struct resource sc26xx_rsrc[] = {
 
 
 static struct sccnxp_pdata sccnxp_data = {
 static struct sccnxp_pdata sccnxp_data = {
 	.reg_shift	= 2,
 	.reg_shift	= 2,
-	.frequency	= 3686400,
 	.mctrl_cfg[0]	= MCTRL_SIG(DTR_OP, LINE_OP7) |
 	.mctrl_cfg[0]	= MCTRL_SIG(DTR_OP, LINE_OP7) |
 			  MCTRL_SIG(RTS_OP, LINE_OP3) |
 			  MCTRL_SIG(RTS_OP, LINE_OP3) |
 			  MCTRL_SIG(DSR_IP, LINE_IP5) |
 			  MCTRL_SIG(DSR_IP, LINE_IP5) |

+ 4 - 4
drivers/net/irda/irtty-sir.c

@@ -123,14 +123,14 @@ static int irtty_change_speed(struct sir_dev *dev, unsigned speed)
 
 
 	tty = priv->tty;
 	tty = priv->tty;
 
 
-	mutex_lock(&tty->termios_mutex);
+	down_write(&tty->termios_rwsem);
 	old_termios = tty->termios;
 	old_termios = tty->termios;
 	cflag = tty->termios.c_cflag;
 	cflag = tty->termios.c_cflag;
 	tty_encode_baud_rate(tty, speed, speed);
 	tty_encode_baud_rate(tty, speed, speed);
 	if (tty->ops->set_termios)
 	if (tty->ops->set_termios)
 		tty->ops->set_termios(tty, &old_termios);
 		tty->ops->set_termios(tty, &old_termios);
 	priv->io.speed = speed;
 	priv->io.speed = speed;
-	mutex_unlock(&tty->termios_mutex);
+	up_write(&tty->termios_rwsem);
 
 
 	return 0;
 	return 0;
 }
 }
@@ -280,7 +280,7 @@ static inline void irtty_stop_receiver(struct tty_struct *tty, int stop)
 	struct ktermios old_termios;
 	struct ktermios old_termios;
 	int cflag;
 	int cflag;
 
 
-	mutex_lock(&tty->termios_mutex);
+	down_write(&tty->termios_rwsem);
 	old_termios = tty->termios;
 	old_termios = tty->termios;
 	cflag = tty->termios.c_cflag;
 	cflag = tty->termios.c_cflag;
 	
 	
@@ -292,7 +292,7 @@ static inline void irtty_stop_receiver(struct tty_struct *tty, int stop)
 	tty->termios.c_cflag = cflag;
 	tty->termios.c_cflag = cflag;
 	if (tty->ops->set_termios)
 	if (tty->ops->set_termios)
 		tty->ops->set_termios(tty, &old_termios);
 		tty->ops->set_termios(tty, &old_termios);
-	mutex_unlock(&tty->termios_mutex);
+	up_write(&tty->termios_rwsem);
 }
 }
 
 
 /*****************************************************************/
 /*****************************************************************/

+ 26 - 0
drivers/of/base.c

@@ -32,6 +32,7 @@ struct device_node *of_allnodes;
 EXPORT_SYMBOL(of_allnodes);
 EXPORT_SYMBOL(of_allnodes);
 struct device_node *of_chosen;
 struct device_node *of_chosen;
 struct device_node *of_aliases;
 struct device_node *of_aliases;
+static struct device_node *of_stdout;
 
 
 DEFINE_MUTEX(of_aliases_mutex);
 DEFINE_MUTEX(of_aliases_mutex);
 
 
@@ -1595,6 +1596,15 @@ void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align))
 	of_chosen = of_find_node_by_path("/chosen");
 	of_chosen = of_find_node_by_path("/chosen");
 	if (of_chosen == NULL)
 	if (of_chosen == NULL)
 		of_chosen = of_find_node_by_path("/chosen@0");
 		of_chosen = of_find_node_by_path("/chosen@0");
+
+	if (of_chosen) {
+		const char *name;
+
+		name = of_get_property(of_chosen, "linux,stdout-path", NULL);
+		if (name)
+			of_stdout = of_find_node_by_path(name);
+	}
+
 	of_aliases = of_find_node_by_path("/aliases");
 	of_aliases = of_find_node_by_path("/aliases");
 	if (!of_aliases)
 	if (!of_aliases)
 		return;
 		return;
@@ -1703,3 +1713,19 @@ const char *of_prop_next_string(struct property *prop, const char *cur)
 	return curv;
 	return curv;
 }
 }
 EXPORT_SYMBOL_GPL(of_prop_next_string);
 EXPORT_SYMBOL_GPL(of_prop_next_string);
+
+/**
+ * of_device_is_stdout_path - check if a device node matches the
+ *                            linux,stdout-path property
+ *
+ * Check if this device node matches the linux,stdout-path property
+ * in the chosen node. return true if yes, false otherwise.
+ */
+int of_device_is_stdout_path(struct device_node *dn)
+{
+	if (!of_stdout)
+		return false;
+
+	return of_stdout == dn;
+}
+EXPORT_SYMBOL_GPL(of_device_is_stdout_path);

+ 0 - 1
drivers/staging/comedi/comedidev.h

@@ -390,7 +390,6 @@ void comedi_driver_unregister(struct comedi_driver *);
  */
  */
 #define PCI_VENDOR_ID_KOLTER		0x1001
 #define PCI_VENDOR_ID_KOLTER		0x1001
 #define PCI_VENDOR_ID_ICP		0x104c
 #define PCI_VENDOR_ID_ICP		0x104c
-#define PCI_VENDOR_ID_AMCC		0x10e8
 #define PCI_VENDOR_ID_DT		0x1116
 #define PCI_VENDOR_ID_DT		0x1116
 #define PCI_VENDOR_ID_IOTECH		0x1616
 #define PCI_VENDOR_ID_IOTECH		0x1616
 #define PCI_VENDOR_ID_CONTEC		0x1221
 #define PCI_VENDOR_ID_CONTEC		0x1221

+ 2 - 0
drivers/staging/dgrp/dgrp_tty.c

@@ -1120,7 +1120,9 @@ static void dgrp_tty_close(struct tty_struct *tty, struct file *file)
 				if (!sent_printer_offstr)
 				if (!sent_printer_offstr)
 					dgrp_tty_flush_buffer(tty);
 					dgrp_tty_flush_buffer(tty);
 
 
+				spin_unlock_irqrestore(&nd->nd_lock, lock_flags);
 				tty_ldisc_flush(tty);
 				tty_ldisc_flush(tty);
+				spin_lock_irqsave(&nd->nd_lock, lock_flags);
 				break;
 				break;
 		}
 		}
 
 

+ 0 - 2
drivers/tty/amiserial.c

@@ -1785,8 +1785,6 @@ static int __exit amiga_serial_remove(struct platform_device *pdev)
 	free_irq(IRQ_AMIGA_TBE, state);
 	free_irq(IRQ_AMIGA_TBE, state);
 	free_irq(IRQ_AMIGA_RBF, state);
 	free_irq(IRQ_AMIGA_RBF, state);
 
 
-	platform_set_drvdata(pdev, NULL);
-
 	return error;
 	return error;
 }
 }
 
 

+ 10 - 1
drivers/tty/hvc/hvc_console.c

@@ -361,7 +361,12 @@ static int hvc_open(struct tty_struct *tty, struct file * filp)
 		tty->driver_data = NULL;
 		tty->driver_data = NULL;
 		tty_port_put(&hp->port);
 		tty_port_put(&hp->port);
 		printk(KERN_ERR "hvc_open: request_irq failed with rc %d.\n", rc);
 		printk(KERN_ERR "hvc_open: request_irq failed with rc %d.\n", rc);
-	}
+	} else
+		/* We are ready... raise DTR/RTS */
+		if (C_BAUD(tty))
+			if (hp->ops->dtr_rts)
+				hp->ops->dtr_rts(hp, 1);
+
 	/* Force wakeup of the polling thread */
 	/* Force wakeup of the polling thread */
 	hvc_kick();
 	hvc_kick();
 
 
@@ -393,6 +398,10 @@ static void hvc_close(struct tty_struct *tty, struct file * filp)
 		/* We are done with the tty pointer now. */
 		/* We are done with the tty pointer now. */
 		tty_port_tty_set(&hp->port, NULL);
 		tty_port_tty_set(&hp->port, NULL);
 
 
+		if (C_HUPCL(tty))
+			if (hp->ops->dtr_rts)
+				hp->ops->dtr_rts(hp, 0);
+
 		if (hp->ops->notifier_del)
 		if (hp->ops->notifier_del)
 			hp->ops->notifier_del(hp, hp->data);
 			hp->ops->notifier_del(hp, hp->data);
 
 

+ 3 - 0
drivers/tty/hvc/hvc_console.h

@@ -75,6 +75,9 @@ struct hv_ops {
 	/* tiocmget/set implementation */
 	/* tiocmget/set implementation */
 	int (*tiocmget)(struct hvc_struct *hp);
 	int (*tiocmget)(struct hvc_struct *hp);
 	int (*tiocmset)(struct hvc_struct *hp, unsigned int set, unsigned int clear);
 	int (*tiocmset)(struct hvc_struct *hp, unsigned int set, unsigned int clear);
+
+	/* Callbacks to handle tty ports */
+	void (*dtr_rts)(struct hvc_struct *hp, int raise);
 };
 };
 
 
 /* Register a vterm and a slot index for use as a console (console_init) */
 /* Register a vterm and a slot index for use as a console (console_init) */

+ 51 - 13
drivers/tty/hvc/hvc_iucv.c

@@ -655,6 +655,49 @@ static void hvc_iucv_notifier_hangup(struct hvc_struct *hp, int id)
 	spin_unlock_bh(&priv->lock);
 	spin_unlock_bh(&priv->lock);
 }
 }
 
 
+/**
+ * hvc_iucv_dtr_rts() - HVC notifier for handling DTR/RTS
+ * @hp:		Pointer the HVC device (struct hvc_struct)
+ * @raise:	Non-zero to raise or zero to lower DTR/RTS lines
+ *
+ * This routine notifies the HVC back-end to raise or lower DTR/RTS
+ * lines.  Raising DTR/RTS is ignored.  Lowering DTR/RTS indicates to
+ * drop the IUCV connection (similar to hang up the modem).
+ */
+static void hvc_iucv_dtr_rts(struct hvc_struct *hp, int raise)
+{
+	struct hvc_iucv_private *priv;
+	struct iucv_path        *path;
+
+	/* Raising the DTR/RTS is ignored as IUCV connections can be
+	 * established at any times.
+	 */
+	if (raise)
+		return;
+
+	priv = hvc_iucv_get_private(hp->vtermno);
+	if (!priv)
+		return;
+
+	/* Lowering the DTR/RTS lines disconnects an established IUCV
+	 * connection.
+	 */
+	flush_sndbuf_sync(priv);
+
+	spin_lock_bh(&priv->lock);
+	path = priv->path;		/* save reference to IUCV path */
+	priv->path = NULL;
+	priv->iucv_state = IUCV_DISCONN;
+	spin_unlock_bh(&priv->lock);
+
+	/* Sever IUCV path outside of priv->lock due to lock ordering of:
+	 * priv->lock <--> iucv_table_lock */
+	if (path) {
+		iucv_path_sever(path, NULL);
+		iucv_path_free(path);
+	}
+}
+
 /**
 /**
  * hvc_iucv_notifier_del() - HVC notifier for closing a TTY for the last time.
  * hvc_iucv_notifier_del() - HVC notifier for closing a TTY for the last time.
  * @hp:		Pointer to the HVC device (struct hvc_struct)
  * @hp:		Pointer to the HVC device (struct hvc_struct)
@@ -662,15 +705,15 @@ static void hvc_iucv_notifier_hangup(struct hvc_struct *hp, int id)
  *		the index of an struct hvc_iucv_private instance.
  *		the index of an struct hvc_iucv_private instance.
  *
  *
  * This routine notifies the HVC back-end that the last tty device fd has been
  * This routine notifies the HVC back-end that the last tty device fd has been
- * closed.  The function calls hvc_iucv_cleanup() to clean up the struct
- * hvc_iucv_private instance.
+ * closed.  The function cleans up tty resources.  The clean-up of the IUCV
+ * connection is done in hvc_iucv_dtr_rts() and depends on the HUPCL termios
+ * control setting.
  *
  *
  * Locking:	struct hvc_iucv_private->lock
  * Locking:	struct hvc_iucv_private->lock
  */
  */
 static void hvc_iucv_notifier_del(struct hvc_struct *hp, int id)
 static void hvc_iucv_notifier_del(struct hvc_struct *hp, int id)
 {
 {
 	struct hvc_iucv_private *priv;
 	struct hvc_iucv_private *priv;
-	struct iucv_path	*path;
 
 
 	priv = hvc_iucv_get_private(id);
 	priv = hvc_iucv_get_private(id);
 	if (!priv)
 	if (!priv)
@@ -679,17 +722,11 @@ static void hvc_iucv_notifier_del(struct hvc_struct *hp, int id)
 	flush_sndbuf_sync(priv);
 	flush_sndbuf_sync(priv);
 
 
 	spin_lock_bh(&priv->lock);
 	spin_lock_bh(&priv->lock);
-	path = priv->path;		/* save reference to IUCV path */
-	priv->path = NULL;
-	hvc_iucv_cleanup(priv);
+	destroy_tty_buffer_list(&priv->tty_outqueue);
+	destroy_tty_buffer_list(&priv->tty_inqueue);
+	priv->tty_state = TTY_CLOSED;
+	priv->sndbuf_len = 0;
 	spin_unlock_bh(&priv->lock);
 	spin_unlock_bh(&priv->lock);
-
-	/* sever IUCV path outside of priv->lock due to lock ordering of:
-	 * priv->lock <--> iucv_table_lock */
-	if (path) {
-		iucv_path_sever(path, NULL);
-		iucv_path_free(path);
-	}
 }
 }
 
 
 /**
 /**
@@ -931,6 +968,7 @@ static const struct hv_ops hvc_iucv_ops = {
 	.notifier_add = hvc_iucv_notifier_add,
 	.notifier_add = hvc_iucv_notifier_add,
 	.notifier_del = hvc_iucv_notifier_del,
 	.notifier_del = hvc_iucv_notifier_del,
 	.notifier_hangup = hvc_iucv_notifier_hangup,
 	.notifier_hangup = hvc_iucv_notifier_hangup,
+	.dtr_rts = hvc_iucv_dtr_rts,
 };
 };
 
 
 /* Suspend / resume device operations */
 /* Suspend / resume device operations */

+ 3 - 3
drivers/tty/hvc/hvc_xen.c

@@ -208,7 +208,7 @@ static int xen_hvm_console_init(void)
 
 
 	info = vtermno_to_xencons(HVC_COOKIE);
 	info = vtermno_to_xencons(HVC_COOKIE);
 	if (!info) {
 	if (!info) {
-		info = kzalloc(sizeof(struct xencons_info), GFP_KERNEL | __GFP_ZERO);
+		info = kzalloc(sizeof(struct xencons_info), GFP_KERNEL);
 		if (!info)
 		if (!info)
 			return -ENOMEM;
 			return -ENOMEM;
 	} else if (info->intf != NULL) {
 	} else if (info->intf != NULL) {
@@ -257,7 +257,7 @@ static int xen_pv_console_init(void)
 
 
 	info = vtermno_to_xencons(HVC_COOKIE);
 	info = vtermno_to_xencons(HVC_COOKIE);
 	if (!info) {
 	if (!info) {
-		info = kzalloc(sizeof(struct xencons_info), GFP_KERNEL | __GFP_ZERO);
+		info = kzalloc(sizeof(struct xencons_info), GFP_KERNEL);
 		if (!info)
 		if (!info)
 			return -ENOMEM;
 			return -ENOMEM;
 	} else if (info->intf != NULL) {
 	} else if (info->intf != NULL) {
@@ -284,7 +284,7 @@ static int xen_initial_domain_console_init(void)
 
 
 	info = vtermno_to_xencons(HVC_COOKIE);
 	info = vtermno_to_xencons(HVC_COOKIE);
 	if (!info) {
 	if (!info) {
-		info = kzalloc(sizeof(struct xencons_info), GFP_KERNEL | __GFP_ZERO);
+		info = kzalloc(sizeof(struct xencons_info), GFP_KERNEL);
 		if (!info)
 		if (!info)
 			return -ENOMEM;
 			return -ENOMEM;
 	}
 	}

+ 16 - 13
drivers/tty/n_gsm.c

@@ -807,7 +807,7 @@ static int gsm_dlci_data_output(struct gsm_mux *gsm, struct gsm_dlci *dlci)
 	int h = dlci->adaption - 1;
 	int h = dlci->adaption - 1;
 
 
 	total_size = 0;
 	total_size = 0;
-	while(1) {
+	while (1) {
 		len = kfifo_len(dlci->fifo);
 		len = kfifo_len(dlci->fifo);
 		if (len == 0)
 		if (len == 0)
 			return total_size;
 			return total_size;
@@ -827,8 +827,8 @@ static int gsm_dlci_data_output(struct gsm_mux *gsm, struct gsm_dlci *dlci)
 		switch (dlci->adaption) {
 		switch (dlci->adaption) {
 		case 1:	/* Unstructured */
 		case 1:	/* Unstructured */
 			break;
 			break;
-		case 2:	/* Unstructed with modem bits. Always one byte as we never
-			   send inline break data */
+		case 2:	/* Unstructed with modem bits.
+		Always one byte as we never send inline break data */
 			*dp++ = gsm_encode_modem(dlci);
 			*dp++ = gsm_encode_modem(dlci);
 			break;
 			break;
 		}
 		}
@@ -968,7 +968,7 @@ static void gsm_dlci_data_kick(struct gsm_dlci *dlci)
 	unsigned long flags;
 	unsigned long flags;
 	int sweep;
 	int sweep;
 
 
-	if (dlci->constipated) 
+	if (dlci->constipated)
 		return;
 		return;
 
 
 	spin_lock_irqsave(&dlci->gsm->tx_lock, flags);
 	spin_lock_irqsave(&dlci->gsm->tx_lock, flags);
@@ -981,7 +981,7 @@ static void gsm_dlci_data_kick(struct gsm_dlci *dlci)
 			gsm_dlci_data_output(dlci->gsm, dlci);
 			gsm_dlci_data_output(dlci->gsm, dlci);
 	}
 	}
 	if (sweep)
 	if (sweep)
- 		gsm_dlci_data_sweep(dlci->gsm);
+		gsm_dlci_data_sweep(dlci->gsm);
 	spin_unlock_irqrestore(&dlci->gsm->tx_lock, flags);
 	spin_unlock_irqrestore(&dlci->gsm->tx_lock, flags);
 }
 }
 
 
@@ -1138,7 +1138,7 @@ static void gsm_control_modem(struct gsm_mux *gsm, u8 *data, int clen)
 static void gsm_control_rls(struct gsm_mux *gsm, u8 *data, int clen)
 static void gsm_control_rls(struct gsm_mux *gsm, u8 *data, int clen)
 {
 {
 	struct tty_port *port;
 	struct tty_port *port;
-	unsigned int addr = 0 ;
+	unsigned int addr = 0;
 	u8 bits;
 	u8 bits;
 	int len = clen;
 	int len = clen;
 	u8 *dp = data;
 	u8 *dp = data;
@@ -1740,10 +1740,11 @@ static void gsm_queue(struct gsm_mux *gsm)
 
 
 	if ((gsm->control & ~PF) == UI)
 	if ((gsm->control & ~PF) == UI)
 		gsm->fcs = gsm_fcs_add_block(gsm->fcs, gsm->buf, gsm->len);
 		gsm->fcs = gsm_fcs_add_block(gsm->fcs, gsm->buf, gsm->len);
-	if (gsm->encoding == 0){
-		/* WARNING: gsm->received_fcs is used for gsm->encoding = 0 only.
-		            In this case it contain the last piece of data
-		            required to generate final CRC */
+	if (gsm->encoding == 0) {
+		/* WARNING: gsm->received_fcs is used for
+		gsm->encoding = 0 only.
+		In this case it contain the last piece of data
+		required to generate final CRC */
 		gsm->fcs = gsm_fcs_add(gsm->fcs, gsm->received_fcs);
 		gsm->fcs = gsm_fcs_add(gsm->fcs, gsm->received_fcs);
 	}
 	}
 	if (gsm->fcs != GOOD_FCS) {
 	if (gsm->fcs != GOOD_FCS) {
@@ -2904,9 +2905,11 @@ static int gsmtty_install(struct tty_driver *driver, struct tty_struct *tty)
 	gsm = gsm_mux[mux];
 	gsm = gsm_mux[mux];
 	if (gsm->dead)
 	if (gsm->dead)
 		return -EL2HLT;
 		return -EL2HLT;
-	/* If DLCI 0 is not yet fully open return an error. This is ok from a locking
-	   perspective as we don't have to worry about this if DLCI0 is lost */
-	if (gsm->dlci[0] && gsm->dlci[0]->state != DLCI_OPEN) 
+	/* If DLCI 0 is not yet fully open return an error.
+	This is ok from a locking
+	perspective as we don't have to worry about this
+	if DLCI0 is lost */
+	if (gsm->dlci[0] && gsm->dlci[0]->state != DLCI_OPEN)
 		return -EL2NSYNC;
 		return -EL2NSYNC;
 	dlci = gsm->dlci[line];
 	dlci = gsm->dlci[line];
 	if (dlci == NULL) {
 	if (dlci == NULL) {

+ 845 - 569
drivers/tty/n_tty.c

@@ -50,6 +50,7 @@
 #include <linux/uaccess.h>
 #include <linux/uaccess.h>
 #include <linux/module.h>
 #include <linux/module.h>
 #include <linux/ratelimit.h>
 #include <linux/ratelimit.h>
+#include <linux/vmalloc.h>
 
 
 
 
 /* number of characters left in xmit buffer before select has we have room */
 /* number of characters left in xmit buffer before select has we have room */
@@ -74,37 +75,81 @@
 #define ECHO_OP_SET_CANON_COL 0x81
 #define ECHO_OP_SET_CANON_COL 0x81
 #define ECHO_OP_ERASE_TAB 0x82
 #define ECHO_OP_ERASE_TAB 0x82
 
 
+#define ECHO_COMMIT_WATERMARK	256
+#define ECHO_BLOCK		256
+#define ECHO_DISCARD_WATERMARK	N_TTY_BUF_SIZE - (ECHO_BLOCK + 32)
+
+
+#undef N_TTY_TRACE
+#ifdef N_TTY_TRACE
+# define n_tty_trace(f, args...)	trace_printk(f, ##args)
+#else
+# define n_tty_trace(f, args...)
+#endif
+
 struct n_tty_data {
 struct n_tty_data {
-	unsigned int column;
+	/* producer-published */
+	size_t read_head;
+	size_t canon_head;
+	size_t echo_head;
+	size_t echo_commit;
+	DECLARE_BITMAP(char_map, 256);
+
+	/* private to n_tty_receive_overrun (single-threaded) */
 	unsigned long overrun_time;
 	unsigned long overrun_time;
 	int num_overrun;
 	int num_overrun;
 
 
+	/* non-atomic */
+	bool no_room;
+
+	/* must hold exclusive termios_rwsem to reset these */
 	unsigned char lnext:1, erasing:1, raw:1, real_raw:1, icanon:1;
 	unsigned char lnext:1, erasing:1, raw:1, real_raw:1, icanon:1;
-	unsigned char echo_overrun:1;
 
 
-	DECLARE_BITMAP(process_char_map, 256);
+	/* shared by producer and consumer */
+	char read_buf[N_TTY_BUF_SIZE];
 	DECLARE_BITMAP(read_flags, N_TTY_BUF_SIZE);
 	DECLARE_BITMAP(read_flags, N_TTY_BUF_SIZE);
+	unsigned char echo_buf[N_TTY_BUF_SIZE];
 
 
-	char *read_buf;
-	int read_head;
-	int read_tail;
-	int read_cnt;
 	int minimum_to_wake;
 	int minimum_to_wake;
 
 
-	unsigned char *echo_buf;
-	unsigned int echo_pos;
-	unsigned int echo_cnt;
+	/* consumer-published */
+	size_t read_tail;
+	size_t line_start;
 
 
-	int canon_data;
-	unsigned long canon_head;
+	/* protected by output lock */
+	unsigned int column;
 	unsigned int canon_column;
 	unsigned int canon_column;
+	size_t echo_tail;
 
 
 	struct mutex atomic_read_lock;
 	struct mutex atomic_read_lock;
 	struct mutex output_lock;
 	struct mutex output_lock;
-	struct mutex echo_lock;
-	raw_spinlock_t read_lock;
 };
 };
 
 
+static inline size_t read_cnt(struct n_tty_data *ldata)
+{
+	return ldata->read_head - ldata->read_tail;
+}
+
+static inline unsigned char read_buf(struct n_tty_data *ldata, size_t i)
+{
+	return ldata->read_buf[i & (N_TTY_BUF_SIZE - 1)];
+}
+
+static inline unsigned char *read_buf_addr(struct n_tty_data *ldata, size_t i)
+{
+	return &ldata->read_buf[i & (N_TTY_BUF_SIZE - 1)];
+}
+
+static inline unsigned char echo_buf(struct n_tty_data *ldata, size_t i)
+{
+	return ldata->echo_buf[i & (N_TTY_BUF_SIZE - 1)];
+}
+
+static inline unsigned char *echo_buf_addr(struct n_tty_data *ldata, size_t i)
+{
+	return &ldata->echo_buf[i & (N_TTY_BUF_SIZE - 1)];
+}
+
 static inline int tty_put_user(struct tty_struct *tty, unsigned char x,
 static inline int tty_put_user(struct tty_struct *tty, unsigned char x,
 			       unsigned char __user *ptr)
 			       unsigned char __user *ptr)
 {
 {
@@ -114,33 +159,18 @@ static inline int tty_put_user(struct tty_struct *tty, unsigned char x,
 	return put_user(x, ptr);
 	return put_user(x, ptr);
 }
 }
 
 
-/**
- *	n_tty_set_room	-	receive space
- *	@tty: terminal
- *
- *	Updates tty->receive_room to reflect the currently available space
- *	in the input buffer, and re-schedules the flip buffer work if space
- *	just became available.
- *
- *	Locks: Concurrent update is protected with read_lock
- */
-
-static int set_room(struct tty_struct *tty)
+static int receive_room(struct tty_struct *tty)
 {
 {
 	struct n_tty_data *ldata = tty->disc_data;
 	struct n_tty_data *ldata = tty->disc_data;
 	int left;
 	int left;
-	int old_left;
-	unsigned long flags;
-
-	raw_spin_lock_irqsave(&ldata->read_lock, flags);
 
 
 	if (I_PARMRK(tty)) {
 	if (I_PARMRK(tty)) {
 		/* Multiply read_cnt by 3, since each byte might take up to
 		/* Multiply read_cnt by 3, since each byte might take up to
 		 * three times as many spaces when PARMRK is set (depending on
 		 * three times as many spaces when PARMRK is set (depending on
 		 * its flags, e.g. parity error). */
 		 * its flags, e.g. parity error). */
-		left = N_TTY_BUF_SIZE - ldata->read_cnt * 3 - 1;
+		left = N_TTY_BUF_SIZE - read_cnt(ldata) * 3 - 1;
 	} else
 	} else
-		left = N_TTY_BUF_SIZE - ldata->read_cnt - 1;
+		left = N_TTY_BUF_SIZE - read_cnt(ldata) - 1;
 
 
 	/*
 	/*
 	 * If we are doing input canonicalization, and there are no
 	 * If we are doing input canonicalization, and there are no
@@ -149,19 +179,31 @@ static int set_room(struct tty_struct *tty)
 	 * characters will be beeped.
 	 * characters will be beeped.
 	 */
 	 */
 	if (left <= 0)
 	if (left <= 0)
-		left = ldata->icanon && !ldata->canon_data;
-	old_left = tty->receive_room;
-	tty->receive_room = left;
+		left = ldata->icanon && ldata->canon_head == ldata->read_tail;
 
 
-	raw_spin_unlock_irqrestore(&ldata->read_lock, flags);
-
-	return left && !old_left;
+	return left;
 }
 }
 
 
+/**
+ *	n_tty_set_room	-	receive space
+ *	@tty: terminal
+ *
+ *	Re-schedules the flip buffer work if space just became available.
+ *
+ *	Caller holds exclusive termios_rwsem
+ *	   or
+ *	n_tty_read()/consumer path:
+ *		holds non-exclusive termios_rwsem
+ */
+
 static void n_tty_set_room(struct tty_struct *tty)
 static void n_tty_set_room(struct tty_struct *tty)
 {
 {
+	struct n_tty_data *ldata = tty->disc_data;
+
 	/* Did this open up the receive buffer? We may need to flip */
 	/* Did this open up the receive buffer? We may need to flip */
-	if (set_room(tty)) {
+	if (unlikely(ldata->no_room) && receive_room(tty)) {
+		ldata->no_room = 0;
+
 		WARN_RATELIMIT(tty->port->itty == NULL,
 		WARN_RATELIMIT(tty->port->itty == NULL,
 				"scheduling with invalid itty\n");
 				"scheduling with invalid itty\n");
 		/* see if ldisc has been killed - if so, this means that
 		/* see if ldisc has been killed - if so, this means that
@@ -170,17 +212,93 @@ static void n_tty_set_room(struct tty_struct *tty)
 		 */
 		 */
 		WARN_RATELIMIT(test_bit(TTY_LDISC_HALTED, &tty->flags),
 		WARN_RATELIMIT(test_bit(TTY_LDISC_HALTED, &tty->flags),
 			       "scheduling buffer work for halted ldisc\n");
 			       "scheduling buffer work for halted ldisc\n");
-		schedule_work(&tty->port->buf.work);
+		queue_work(system_unbound_wq, &tty->port->buf.work);
 	}
 	}
 }
 }
 
 
-static void put_tty_queue_nolock(unsigned char c, struct n_tty_data *ldata)
+static ssize_t chars_in_buffer(struct tty_struct *tty)
+{
+	struct n_tty_data *ldata = tty->disc_data;
+	ssize_t n = 0;
+
+	if (!ldata->icanon)
+		n = read_cnt(ldata);
+	else
+		n = ldata->canon_head - ldata->read_tail;
+	return n;
+}
+
+/**
+ *	n_tty_write_wakeup	-	asynchronous I/O notifier
+ *	@tty: tty device
+ *
+ *	Required for the ptys, serial driver etc. since processes
+ *	that attach themselves to the master and rely on ASYNC
+ *	IO must be woken up
+ */
+
+static void n_tty_write_wakeup(struct tty_struct *tty)
 {
 {
-	if (ldata->read_cnt < N_TTY_BUF_SIZE) {
-		ldata->read_buf[ldata->read_head] = c;
-		ldata->read_head = (ldata->read_head + 1) & (N_TTY_BUF_SIZE-1);
-		ldata->read_cnt++;
+	if (tty->fasync && test_and_clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags))
+		kill_fasync(&tty->fasync, SIGIO, POLL_OUT);
+}
+
+static void n_tty_check_throttle(struct tty_struct *tty)
+{
+	if (tty->driver->type == TTY_DRIVER_TYPE_PTY)
+		return;
+	/*
+	 * Check the remaining room for the input canonicalization
+	 * mode.  We don't want to throttle the driver if we're in
+	 * canonical mode and don't have a newline yet!
+	 */
+	while (1) {
+		int throttled;
+		tty_set_flow_change(tty, TTY_THROTTLE_SAFE);
+		if (receive_room(tty) >= TTY_THRESHOLD_THROTTLE)
+			break;
+		throttled = tty_throttle_safe(tty);
+		if (!throttled)
+			break;
+	}
+	__tty_set_flow_change(tty, 0);
+}
+
+static void n_tty_check_unthrottle(struct tty_struct *tty)
+{
+	if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
+	    tty->link->ldisc->ops->write_wakeup == n_tty_write_wakeup) {
+		if (chars_in_buffer(tty) > TTY_THRESHOLD_UNTHROTTLE)
+			return;
+		if (!tty->count)
+			return;
+		n_tty_set_room(tty);
+		n_tty_write_wakeup(tty->link);
+		wake_up_interruptible_poll(&tty->link->write_wait, POLLOUT);
+		return;
+	}
+
+	/* If there is enough space in the read buffer now, let the
+	 * low-level driver know. We use chars_in_buffer() to
+	 * check the buffer, as it now knows about canonical mode.
+	 * Otherwise, if the driver is throttled and the line is
+	 * longer than TTY_THRESHOLD_UNTHROTTLE in canonical mode,
+	 * we won't get any more characters.
+	 */
+
+	while (1) {
+		int unthrottled;
+		tty_set_flow_change(tty, TTY_UNTHROTTLE_SAFE);
+		if (chars_in_buffer(tty) > TTY_THRESHOLD_UNTHROTTLE)
+			break;
+		if (!tty->count)
+			break;
+		n_tty_set_room(tty);
+		unthrottled = tty_unthrottle_safe(tty);
+		if (!unthrottled)
+			break;
 	}
 	}
+	__tty_set_flow_change(tty, 0);
 }
 }
 
 
 /**
 /**
@@ -188,21 +306,19 @@ static void put_tty_queue_nolock(unsigned char c, struct n_tty_data *ldata)
  *	@c: character
  *	@c: character
  *	@ldata: n_tty data
  *	@ldata: n_tty data
  *
  *
- *	Add a character to the tty read_buf queue. This is done under the
- *	read_lock to serialize character addition and also to protect us
- *	against parallel reads or flushes
+ *	Add a character to the tty read_buf queue.
+ *
+ *	n_tty_receive_buf()/producer path:
+ *		caller holds non-exclusive termios_rwsem
+ *		modifies read_head
+ *
+ *	read_head is only considered 'published' if canonical mode is
+ *	not active.
  */
  */
 
 
-static 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)
 {
 {
-	unsigned long flags;
-	/*
-	 *	The problem of stomping on the buffers ends here.
-	 *	Why didn't anyone see this one coming? --AJK
-	*/
-	raw_spin_lock_irqsave(&ldata->read_lock, flags);
-	put_tty_queue_nolock(c, ldata);
-	raw_spin_unlock_irqrestore(&ldata->read_lock, flags);
+	*read_buf_addr(ldata, ldata->read_head++) = c;
 }
 }
 
 
 /**
 /**
@@ -212,22 +328,17 @@ static void put_tty_queue(unsigned char c, struct n_tty_data *ldata)
  *	Reset the read buffer counters and clear the flags.
  *	Reset the read buffer counters and clear the flags.
  *	Called from n_tty_open() and n_tty_flush_buffer().
  *	Called from n_tty_open() and n_tty_flush_buffer().
  *
  *
- *	Locking: tty_read_lock for read fields.
+ *	Locking: caller holds exclusive termios_rwsem
+ *		 (or locking is not required)
  */
  */
 
 
 static void reset_buffer_flags(struct n_tty_data *ldata)
 static void reset_buffer_flags(struct n_tty_data *ldata)
 {
 {
-	unsigned long flags;
+	ldata->read_head = ldata->canon_head = ldata->read_tail = 0;
+	ldata->echo_head = ldata->echo_tail = ldata->echo_commit = 0;
+	ldata->line_start = 0;
 
 
-	raw_spin_lock_irqsave(&ldata->read_lock, flags);
-	ldata->read_head = ldata->read_tail = ldata->read_cnt = 0;
-	raw_spin_unlock_irqrestore(&ldata->read_lock, flags);
-
-	mutex_lock(&ldata->echo_lock);
-	ldata->echo_pos = ldata->echo_cnt = ldata->echo_overrun = 0;
-	mutex_unlock(&ldata->echo_lock);
-
-	ldata->canon_head = ldata->canon_data = ldata->erasing = 0;
+	ldata->erasing = 0;
 	bitmap_zero(ldata->read_flags, N_TTY_BUF_SIZE);
 	bitmap_zero(ldata->read_flags, N_TTY_BUF_SIZE);
 }
 }
 
 
@@ -251,16 +362,21 @@ static void n_tty_packet_mode_flush(struct tty_struct *tty)
  *	buffer flushed (eg at hangup) or when the N_TTY line discipline
  *	buffer flushed (eg at hangup) or when the N_TTY line discipline
  *	internally has to clean the pending queue (for example some signals).
  *	internally has to clean the pending queue (for example some signals).
  *
  *
- *	Locking: ctrl_lock, read_lock.
+ *	Holds termios_rwsem to exclude producer/consumer while
+ *	buffer indices are reset.
+ *
+ *	Locking: ctrl_lock, exclusive termios_rwsem
  */
  */
 
 
 static void n_tty_flush_buffer(struct tty_struct *tty)
 static void n_tty_flush_buffer(struct tty_struct *tty)
 {
 {
+	down_write(&tty->termios_rwsem);
 	reset_buffer_flags(tty->disc_data);
 	reset_buffer_flags(tty->disc_data);
 	n_tty_set_room(tty);
 	n_tty_set_room(tty);
 
 
 	if (tty->link)
 	if (tty->link)
 		n_tty_packet_mode_flush(tty);
 		n_tty_packet_mode_flush(tty);
+	up_write(&tty->termios_rwsem);
 }
 }
 
 
 /**
 /**
@@ -270,24 +386,18 @@ static void n_tty_flush_buffer(struct tty_struct *tty)
  *	Report the number of characters buffered to be delivered to user
  *	Report the number of characters buffered to be delivered to user
  *	at this instant in time.
  *	at this instant in time.
  *
  *
- *	Locking: read_lock
+ *	Locking: exclusive termios_rwsem
  */
  */
 
 
 static ssize_t n_tty_chars_in_buffer(struct tty_struct *tty)
 static ssize_t n_tty_chars_in_buffer(struct tty_struct *tty)
 {
 {
-	struct n_tty_data *ldata = tty->disc_data;
-	unsigned long flags;
-	ssize_t n = 0;
+	ssize_t n;
 
 
-	raw_spin_lock_irqsave(&ldata->read_lock, flags);
-	if (!ldata->icanon) {
-		n = ldata->read_cnt;
-	} else if (ldata->canon_data) {
-		n = (ldata->canon_head > ldata->read_tail) ?
-			ldata->canon_head - ldata->read_tail :
-			ldata->canon_head + (N_TTY_BUF_SIZE - ldata->read_tail);
-	}
-	raw_spin_unlock_irqrestore(&ldata->read_lock, flags);
+	WARN_ONCE(1, "%s is deprecated and scheduled for removal.", __func__);
+
+	down_write(&tty->termios_rwsem);
+	n = chars_in_buffer(tty);
+	up_write(&tty->termios_rwsem);
 	return n;
 	return n;
 }
 }
 
 
@@ -532,33 +642,23 @@ break_out:
  *	are prioritized.  Also, when control characters are echoed with a
  *	are prioritized.  Also, when control characters are echoed with a
  *	prefixed "^", the pair is treated atomically and thus not separated.
  *	prefixed "^", the pair is treated atomically and thus not separated.
  *
  *
- *	Locking: output_lock to protect column state and space left,
- *		 echo_lock to protect the echo buffer
+ *	Locking: callers must hold output_lock
  */
  */
 
 
-static void process_echoes(struct tty_struct *tty)
+static size_t __process_echoes(struct tty_struct *tty)
 {
 {
 	struct n_tty_data *ldata = tty->disc_data;
 	struct n_tty_data *ldata = tty->disc_data;
-	int	space, nr;
+	int	space, old_space;
+	size_t tail;
 	unsigned char c;
 	unsigned char c;
-	unsigned char *cp, *buf_end;
-
-	if (!ldata->echo_cnt)
-		return;
-
-	mutex_lock(&ldata->output_lock);
-	mutex_lock(&ldata->echo_lock);
 
 
-	space = tty_write_room(tty);
+	old_space = space = tty_write_room(tty);
 
 
-	buf_end = ldata->echo_buf + N_TTY_BUF_SIZE;
-	cp = ldata->echo_buf + ldata->echo_pos;
-	nr = ldata->echo_cnt;
-	while (nr > 0) {
-		c = *cp;
+	tail = ldata->echo_tail;
+	while (ldata->echo_commit != tail) {
+		c = echo_buf(ldata, tail);
 		if (c == ECHO_OP_START) {
 		if (c == ECHO_OP_START) {
 			unsigned char op;
 			unsigned char op;
-			unsigned char *opp;
 			int no_space_left = 0;
 			int no_space_left = 0;
 
 
 			/*
 			/*
@@ -566,18 +666,13 @@ static void process_echoes(struct tty_struct *tty)
 			 * operation, get the next byte, which is either the
 			 * operation, get the next byte, which is either the
 			 * op code or a control character value.
 			 * op code or a control character value.
 			 */
 			 */
-			opp = cp + 1;
-			if (opp == buf_end)
-				opp -= N_TTY_BUF_SIZE;
-			op = *opp;
+			op = echo_buf(ldata, tail + 1);
 
 
 			switch (op) {
 			switch (op) {
 				unsigned int num_chars, num_bs;
 				unsigned int num_chars, num_bs;
 
 
 			case ECHO_OP_ERASE_TAB:
 			case ECHO_OP_ERASE_TAB:
-				if (++opp == buf_end)
-					opp -= N_TTY_BUF_SIZE;
-				num_chars = *opp;
+				num_chars = echo_buf(ldata, tail + 2);
 
 
 				/*
 				/*
 				 * Determine how many columns to go back
 				 * Determine how many columns to go back
@@ -603,21 +698,18 @@ static void process_echoes(struct tty_struct *tty)
 					if (ldata->column > 0)
 					if (ldata->column > 0)
 						ldata->column--;
 						ldata->column--;
 				}
 				}
-				cp += 3;
-				nr -= 3;
+				tail += 3;
 				break;
 				break;
 
 
 			case ECHO_OP_SET_CANON_COL:
 			case ECHO_OP_SET_CANON_COL:
 				ldata->canon_column = ldata->column;
 				ldata->canon_column = ldata->column;
-				cp += 2;
-				nr -= 2;
+				tail += 2;
 				break;
 				break;
 
 
 			case ECHO_OP_MOVE_BACK_COL:
 			case ECHO_OP_MOVE_BACK_COL:
 				if (ldata->column > 0)
 				if (ldata->column > 0)
 					ldata->column--;
 					ldata->column--;
-				cp += 2;
-				nr -= 2;
+				tail += 2;
 				break;
 				break;
 
 
 			case ECHO_OP_START:
 			case ECHO_OP_START:
@@ -629,8 +721,7 @@ static void process_echoes(struct tty_struct *tty)
 				tty_put_char(tty, ECHO_OP_START);
 				tty_put_char(tty, ECHO_OP_START);
 				ldata->column++;
 				ldata->column++;
 				space--;
 				space--;
-				cp += 2;
-				nr -= 2;
+				tail += 2;
 				break;
 				break;
 
 
 			default:
 			default:
@@ -651,8 +742,7 @@ static void process_echoes(struct tty_struct *tty)
 				tty_put_char(tty, op ^ 0100);
 				tty_put_char(tty, op ^ 0100);
 				ldata->column += 2;
 				ldata->column += 2;
 				space -= 2;
 				space -= 2;
-				cp += 2;
-				nr -= 2;
+				tail += 2;
 			}
 			}
 
 
 			if (no_space_left)
 			if (no_space_left)
@@ -669,80 +759,92 @@ static void process_echoes(struct tty_struct *tty)
 				tty_put_char(tty, c);
 				tty_put_char(tty, c);
 				space -= 1;
 				space -= 1;
 			}
 			}
-			cp += 1;
-			nr -= 1;
+			tail += 1;
 		}
 		}
-
-		/* When end of circular buffer reached, wrap around */
-		if (cp >= buf_end)
-			cp -= N_TTY_BUF_SIZE;
 	}
 	}
 
 
-	if (nr == 0) {
-		ldata->echo_pos = 0;
-		ldata->echo_cnt = 0;
-		ldata->echo_overrun = 0;
-	} else {
-		int num_processed = ldata->echo_cnt - nr;
-		ldata->echo_pos += num_processed;
-		ldata->echo_pos &= N_TTY_BUF_SIZE - 1;
-		ldata->echo_cnt = nr;
-		if (num_processed > 0)
-			ldata->echo_overrun = 0;
+	/* If the echo buffer is nearly full (so that the possibility exists
+	 * of echo overrun before the next commit), then discard enough
+	 * data at the tail to prevent a subsequent overrun */
+	while (ldata->echo_commit - tail >= ECHO_DISCARD_WATERMARK) {
+		if (echo_buf(ldata, tail == ECHO_OP_START)) {
+			if (echo_buf(ldata, tail) == ECHO_OP_ERASE_TAB)
+				tail += 3;
+			else
+				tail += 2;
+		} else
+			tail++;
 	}
 	}
 
 
-	mutex_unlock(&ldata->echo_lock);
+	ldata->echo_tail = tail;
+	return old_space - space;
+}
+
+static void commit_echoes(struct tty_struct *tty)
+{
+	struct n_tty_data *ldata = tty->disc_data;
+	size_t nr, old, echoed;
+	size_t head;
+
+	head = ldata->echo_head;
+	old = ldata->echo_commit - ldata->echo_tail;
+
+	/* Process committed echoes if the accumulated # of bytes
+	 * is over the threshold (and try again each time another
+	 * block is accumulated) */
+	nr = head - ldata->echo_tail;
+	if (nr < ECHO_COMMIT_WATERMARK || (nr % ECHO_BLOCK > old % ECHO_BLOCK))
+		return;
+
+	mutex_lock(&ldata->output_lock);
+	ldata->echo_commit = head;
+	echoed = __process_echoes(tty);
+	mutex_unlock(&ldata->output_lock);
+
+	if (echoed && tty->ops->flush_chars)
+		tty->ops->flush_chars(tty);
+}
+
+static void process_echoes(struct tty_struct *tty)
+{
+	struct n_tty_data *ldata = tty->disc_data;
+	size_t echoed;
+
+	if (!L_ECHO(tty) || ldata->echo_commit == ldata->echo_tail)
+		return;
+
+	mutex_lock(&ldata->output_lock);
+	echoed = __process_echoes(tty);
 	mutex_unlock(&ldata->output_lock);
 	mutex_unlock(&ldata->output_lock);
 
 
-	if (tty->ops->flush_chars)
+	if (echoed && tty->ops->flush_chars)
 		tty->ops->flush_chars(tty);
 		tty->ops->flush_chars(tty);
 }
 }
 
 
+static void flush_echoes(struct tty_struct *tty)
+{
+	struct n_tty_data *ldata = tty->disc_data;
+
+	if (!L_ECHO(tty) || ldata->echo_commit == ldata->echo_head)
+		return;
+
+	mutex_lock(&ldata->output_lock);
+	ldata->echo_commit = ldata->echo_head;
+	__process_echoes(tty);
+	mutex_unlock(&ldata->output_lock);
+}
+
 /**
 /**
  *	add_echo_byte	-	add a byte to the echo buffer
  *	add_echo_byte	-	add a byte to the echo buffer
  *	@c: unicode byte to echo
  *	@c: unicode byte to echo
  *	@ldata: n_tty data
  *	@ldata: n_tty data
  *
  *
  *	Add a character or operation byte to the echo buffer.
  *	Add a character or operation byte to the echo buffer.
- *
- *	Should be called under the echo lock to protect the echo buffer.
  */
  */
 
 
-static void add_echo_byte(unsigned char c, struct n_tty_data *ldata)
+static inline void add_echo_byte(unsigned char c, struct n_tty_data *ldata)
 {
 {
-	int	new_byte_pos;
-
-	if (ldata->echo_cnt == N_TTY_BUF_SIZE) {
-		/* Circular buffer is already at capacity */
-		new_byte_pos = ldata->echo_pos;
-
-		/*
-		 * Since the buffer start position needs to be advanced,
-		 * be sure to step by a whole operation byte group.
-		 */
-		if (ldata->echo_buf[ldata->echo_pos] == ECHO_OP_START) {
-			if (ldata->echo_buf[(ldata->echo_pos + 1) &
-					  (N_TTY_BUF_SIZE - 1)] ==
-						ECHO_OP_ERASE_TAB) {
-				ldata->echo_pos += 3;
-				ldata->echo_cnt -= 2;
-			} else {
-				ldata->echo_pos += 2;
-				ldata->echo_cnt -= 1;
-			}
-		} else {
-			ldata->echo_pos++;
-		}
-		ldata->echo_pos &= N_TTY_BUF_SIZE - 1;
-
-		ldata->echo_overrun = 1;
-	} else {
-		new_byte_pos = ldata->echo_pos + ldata->echo_cnt;
-		new_byte_pos &= N_TTY_BUF_SIZE - 1;
-		ldata->echo_cnt++;
-	}
-
-	ldata->echo_buf[new_byte_pos] = c;
+	*echo_buf_addr(ldata, ldata->echo_head++) = c;
 }
 }
 
 
 /**
 /**
@@ -750,16 +852,12 @@ static void add_echo_byte(unsigned char c, struct n_tty_data *ldata)
  *	@ldata: n_tty data
  *	@ldata: n_tty data
  *
  *
  *	Add an operation to the echo buffer to move back one column.
  *	Add an operation to the echo buffer to move back one column.
- *
- *	Locking: echo_lock to protect the echo buffer
  */
  */
 
 
 static void echo_move_back_col(struct n_tty_data *ldata)
 static void echo_move_back_col(struct n_tty_data *ldata)
 {
 {
-	mutex_lock(&ldata->echo_lock);
 	add_echo_byte(ECHO_OP_START, ldata);
 	add_echo_byte(ECHO_OP_START, ldata);
 	add_echo_byte(ECHO_OP_MOVE_BACK_COL, ldata);
 	add_echo_byte(ECHO_OP_MOVE_BACK_COL, ldata);
-	mutex_unlock(&ldata->echo_lock);
 }
 }
 
 
 /**
 /**
@@ -768,16 +866,12 @@ static void echo_move_back_col(struct n_tty_data *ldata)
  *
  *
  *	Add an operation to the echo buffer to set the canon column
  *	Add an operation to the echo buffer to set the canon column
  *	to the current column.
  *	to the current column.
- *
- *	Locking: echo_lock to protect the echo buffer
  */
  */
 
 
 static void echo_set_canon_col(struct n_tty_data *ldata)
 static void echo_set_canon_col(struct n_tty_data *ldata)
 {
 {
-	mutex_lock(&ldata->echo_lock);
 	add_echo_byte(ECHO_OP_START, ldata);
 	add_echo_byte(ECHO_OP_START, ldata);
 	add_echo_byte(ECHO_OP_SET_CANON_COL, ldata);
 	add_echo_byte(ECHO_OP_SET_CANON_COL, ldata);
-	mutex_unlock(&ldata->echo_lock);
 }
 }
 
 
 /**
 /**
@@ -793,15 +887,11 @@ static void echo_set_canon_col(struct n_tty_data *ldata)
  *	of input.  This information will be used later, along with
  *	of input.  This information will be used later, along with
  *	canon column (if applicable), to go back the correct number
  *	canon column (if applicable), to go back the correct number
  *	of columns.
  *	of columns.
- *
- *	Locking: echo_lock to protect the echo buffer
  */
  */
 
 
 static void echo_erase_tab(unsigned int num_chars, int after_tab,
 static void echo_erase_tab(unsigned int num_chars, int after_tab,
 			   struct n_tty_data *ldata)
 			   struct n_tty_data *ldata)
 {
 {
-	mutex_lock(&ldata->echo_lock);
-
 	add_echo_byte(ECHO_OP_START, ldata);
 	add_echo_byte(ECHO_OP_START, ldata);
 	add_echo_byte(ECHO_OP_ERASE_TAB, ldata);
 	add_echo_byte(ECHO_OP_ERASE_TAB, ldata);
 
 
@@ -813,8 +903,6 @@ static void echo_erase_tab(unsigned int num_chars, int after_tab,
 		num_chars |= 0x80;
 		num_chars |= 0x80;
 
 
 	add_echo_byte(num_chars, ldata);
 	add_echo_byte(num_chars, ldata);
-
-	mutex_unlock(&ldata->echo_lock);
 }
 }
 
 
 /**
 /**
@@ -826,20 +914,16 @@ static void echo_erase_tab(unsigned int num_chars, int after_tab,
  *	L_ECHO(tty) is true. Called from the driver receive_buf path.
  *	L_ECHO(tty) is true. Called from the driver receive_buf path.
  *
  *
  *	This variant does not treat control characters specially.
  *	This variant does not treat control characters specially.
- *
- *	Locking: echo_lock to protect the echo buffer
  */
  */
 
 
 static void echo_char_raw(unsigned char c, struct n_tty_data *ldata)
 static void echo_char_raw(unsigned char c, struct n_tty_data *ldata)
 {
 {
-	mutex_lock(&ldata->echo_lock);
 	if (c == ECHO_OP_START) {
 	if (c == ECHO_OP_START) {
 		add_echo_byte(ECHO_OP_START, ldata);
 		add_echo_byte(ECHO_OP_START, ldata);
 		add_echo_byte(ECHO_OP_START, ldata);
 		add_echo_byte(ECHO_OP_START, ldata);
 	} else {
 	} else {
 		add_echo_byte(c, ldata);
 		add_echo_byte(c, ldata);
 	}
 	}
-	mutex_unlock(&ldata->echo_lock);
 }
 }
 
 
 /**
 /**
@@ -852,16 +936,12 @@ static void echo_char_raw(unsigned char c, struct n_tty_data *ldata)
  *
  *
  *	This variant tags control characters to be echoed as "^X"
  *	This variant tags control characters to be echoed as "^X"
  *	(where X is the letter representing the control char).
  *	(where X is the letter representing the control char).
- *
- *	Locking: echo_lock to protect the echo buffer
  */
  */
 
 
 static void echo_char(unsigned char c, struct tty_struct *tty)
 static void echo_char(unsigned char c, struct tty_struct *tty)
 {
 {
 	struct n_tty_data *ldata = tty->disc_data;
 	struct n_tty_data *ldata = tty->disc_data;
 
 
-	mutex_lock(&ldata->echo_lock);
-
 	if (c == ECHO_OP_START) {
 	if (c == ECHO_OP_START) {
 		add_echo_byte(ECHO_OP_START, ldata);
 		add_echo_byte(ECHO_OP_START, ldata);
 		add_echo_byte(ECHO_OP_START, ldata);
 		add_echo_byte(ECHO_OP_START, ldata);
@@ -870,8 +950,6 @@ static void echo_char(unsigned char c, struct tty_struct *tty)
 			add_echo_byte(ECHO_OP_START, ldata);
 			add_echo_byte(ECHO_OP_START, ldata);
 		add_echo_byte(c, ldata);
 		add_echo_byte(c, ldata);
 	}
 	}
-
-	mutex_unlock(&ldata->echo_lock);
 }
 }
 
 
 /**
 /**
@@ -896,17 +974,22 @@ static inline void finish_erasing(struct n_tty_data *ldata)
  *	present in the stream from the driver layer. Handles the complexities
  *	present in the stream from the driver layer. Handles the complexities
  *	of UTF-8 multibyte symbols.
  *	of UTF-8 multibyte symbols.
  *
  *
- *	Locking: read_lock for tty buffers
+ *	n_tty_receive_buf()/producer path:
+ *		caller holds non-exclusive termios_rwsem
+ *		modifies read_head
+ *
+ *	Modifying the read_head is not considered a publish in this context
+ *	because canonical mode is active -- only canon_head publishes
  */
  */
 
 
 static void eraser(unsigned char c, struct tty_struct *tty)
 static void eraser(unsigned char c, struct tty_struct *tty)
 {
 {
 	struct n_tty_data *ldata = tty->disc_data;
 	struct n_tty_data *ldata = tty->disc_data;
 	enum { ERASE, WERASE, KILL } kill_type;
 	enum { ERASE, WERASE, KILL } kill_type;
-	int head, seen_alnums, cnt;
-	unsigned long flags;
+	size_t head;
+	size_t cnt;
+	int seen_alnums;
 
 
-	/* FIXME: locking needed ? */
 	if (ldata->read_head == ldata->canon_head) {
 	if (ldata->read_head == ldata->canon_head) {
 		/* process_output('\a', tty); */ /* what do you think? */
 		/* process_output('\a', tty); */ /* what do you think? */
 		return;
 		return;
@@ -917,19 +1000,11 @@ static void eraser(unsigned char c, struct tty_struct *tty)
 		kill_type = WERASE;
 		kill_type = WERASE;
 	else {
 	else {
 		if (!L_ECHO(tty)) {
 		if (!L_ECHO(tty)) {
-			raw_spin_lock_irqsave(&ldata->read_lock, flags);
-			ldata->read_cnt -= ((ldata->read_head - ldata->canon_head) &
-					  (N_TTY_BUF_SIZE - 1));
 			ldata->read_head = ldata->canon_head;
 			ldata->read_head = ldata->canon_head;
-			raw_spin_unlock_irqrestore(&ldata->read_lock, flags);
 			return;
 			return;
 		}
 		}
 		if (!L_ECHOK(tty) || !L_ECHOKE(tty) || !L_ECHOE(tty)) {
 		if (!L_ECHOK(tty) || !L_ECHOKE(tty) || !L_ECHOE(tty)) {
-			raw_spin_lock_irqsave(&ldata->read_lock, flags);
-			ldata->read_cnt -= ((ldata->read_head - ldata->canon_head) &
-					  (N_TTY_BUF_SIZE - 1));
 			ldata->read_head = ldata->canon_head;
 			ldata->read_head = ldata->canon_head;
-			raw_spin_unlock_irqrestore(&ldata->read_lock, flags);
 			finish_erasing(ldata);
 			finish_erasing(ldata);
 			echo_char(KILL_CHAR(tty), tty);
 			echo_char(KILL_CHAR(tty), tty);
 			/* Add a newline if ECHOK is on and ECHOKE is off. */
 			/* Add a newline if ECHOK is on and ECHOKE is off. */
@@ -941,14 +1016,13 @@ static void eraser(unsigned char c, struct tty_struct *tty)
 	}
 	}
 
 
 	seen_alnums = 0;
 	seen_alnums = 0;
-	/* FIXME: Locking ?? */
 	while (ldata->read_head != ldata->canon_head) {
 	while (ldata->read_head != ldata->canon_head) {
 		head = ldata->read_head;
 		head = ldata->read_head;
 
 
 		/* erase a single possibly multibyte character */
 		/* erase a single possibly multibyte character */
 		do {
 		do {
-			head = (head - 1) & (N_TTY_BUF_SIZE-1);
-			c = ldata->read_buf[head];
+			head--;
+			c = read_buf(ldata, head);
 		} while (is_continuation(c, tty) && head != ldata->canon_head);
 		} while (is_continuation(c, tty) && head != ldata->canon_head);
 
 
 		/* do not partially erase */
 		/* do not partially erase */
@@ -962,11 +1036,8 @@ static void eraser(unsigned char c, struct tty_struct *tty)
 			else if (seen_alnums)
 			else if (seen_alnums)
 				break;
 				break;
 		}
 		}
-		cnt = (ldata->read_head - head) & (N_TTY_BUF_SIZE-1);
-		raw_spin_lock_irqsave(&ldata->read_lock, flags);
+		cnt = ldata->read_head - head;
 		ldata->read_head = head;
 		ldata->read_head = head;
-		ldata->read_cnt -= cnt;
-		raw_spin_unlock_irqrestore(&ldata->read_lock, flags);
 		if (L_ECHO(tty)) {
 		if (L_ECHO(tty)) {
 			if (L_ECHOPRT(tty)) {
 			if (L_ECHOPRT(tty)) {
 				if (!ldata->erasing) {
 				if (!ldata->erasing) {
@@ -976,9 +1047,8 @@ static void eraser(unsigned char c, struct tty_struct *tty)
 				/* if cnt > 1, output a multi-byte character */
 				/* if cnt > 1, output a multi-byte character */
 				echo_char(c, tty);
 				echo_char(c, tty);
 				while (--cnt > 0) {
 				while (--cnt > 0) {
-					head = (head+1) & (N_TTY_BUF_SIZE-1);
-					echo_char_raw(ldata->read_buf[head],
-							ldata);
+					head++;
+					echo_char_raw(read_buf(ldata, head), ldata);
 					echo_move_back_col(ldata);
 					echo_move_back_col(ldata);
 				}
 				}
 			} else if (kill_type == ERASE && !L_ECHOE(tty)) {
 			} else if (kill_type == ERASE && !L_ECHOE(tty)) {
@@ -986,7 +1056,7 @@ static void eraser(unsigned char c, struct tty_struct *tty)
 			} else if (c == '\t') {
 			} else if (c == '\t') {
 				unsigned int num_chars = 0;
 				unsigned int num_chars = 0;
 				int after_tab = 0;
 				int after_tab = 0;
-				unsigned long tail = ldata->read_head;
+				size_t tail = ldata->read_head;
 
 
 				/*
 				/*
 				 * Count the columns used for characters
 				 * Count the columns used for characters
@@ -996,8 +1066,8 @@ static void eraser(unsigned char c, struct tty_struct *tty)
 				 * number of columns.
 				 * number of columns.
 				 */
 				 */
 				while (tail != ldata->canon_head) {
 				while (tail != ldata->canon_head) {
-					tail = (tail-1) & (N_TTY_BUF_SIZE-1);
-					c = ldata->read_buf[tail];
+					tail--;
+					c = read_buf(ldata, tail);
 					if (c == '\t') {
 					if (c == '\t') {
 						after_tab = 1;
 						after_tab = 1;
 						break;
 						break;
@@ -1040,7 +1110,7 @@ static void eraser(unsigned char c, struct tty_struct *tty)
  *	Locking: ctrl_lock
  *	Locking: ctrl_lock
  */
  */
 
 
-static inline void isig(int sig, struct tty_struct *tty)
+static void isig(int sig, struct tty_struct *tty)
 {
 {
 	struct pid *tty_pgrp = tty_get_pgrp(tty);
 	struct pid *tty_pgrp = tty_get_pgrp(tty);
 	if (tty_pgrp) {
 	if (tty_pgrp) {
@@ -1056,10 +1126,14 @@ static inline void isig(int sig, struct tty_struct *tty)
  *	An RS232 break event has been hit in the incoming bitstream. This
  *	An RS232 break event has been hit in the incoming bitstream. This
  *	can cause a variety of events depending upon the termios settings.
  *	can cause a variety of events depending upon the termios settings.
  *
  *
- *	Called from the receive_buf path so single threaded.
+ *	n_tty_receive_buf()/producer path:
+ *		caller holds non-exclusive termios_rwsem
+ *		publishes read_head via put_tty_queue()
+ *
+ *	Note: may get exclusive termios_rwsem if flushing input buffer
  */
  */
 
 
-static inline void n_tty_receive_break(struct tty_struct *tty)
+static void n_tty_receive_break(struct tty_struct *tty)
 {
 {
 	struct n_tty_data *ldata = tty->disc_data;
 	struct n_tty_data *ldata = tty->disc_data;
 
 
@@ -1068,8 +1142,11 @@ static inline void n_tty_receive_break(struct tty_struct *tty)
 	if (I_BRKINT(tty)) {
 	if (I_BRKINT(tty)) {
 		isig(SIGINT, tty);
 		isig(SIGINT, tty);
 		if (!L_NOFLSH(tty)) {
 		if (!L_NOFLSH(tty)) {
+			/* flushing needs exclusive termios_rwsem */
+			up_read(&tty->termios_rwsem);
 			n_tty_flush_buffer(tty);
 			n_tty_flush_buffer(tty);
 			tty_driver_flush_buffer(tty);
 			tty_driver_flush_buffer(tty);
+			down_read(&tty->termios_rwsem);
 		}
 		}
 		return;
 		return;
 	}
 	}
@@ -1094,7 +1171,7 @@ static inline void n_tty_receive_break(struct tty_struct *tty)
  *	private.
  *	private.
  */
  */
 
 
-static inline void n_tty_receive_overrun(struct tty_struct *tty)
+static void n_tty_receive_overrun(struct tty_struct *tty)
 {
 {
 	struct n_tty_data *ldata = tty->disc_data;
 	struct n_tty_data *ldata = tty->disc_data;
 	char buf[64];
 	char buf[64];
@@ -1116,10 +1193,13 @@ static inline void n_tty_receive_overrun(struct tty_struct *tty)
  *	@c: character
  *	@c: character
  *
  *
  *	Process a parity error and queue the right data to indicate
  *	Process a parity error and queue the right data to indicate
- *	the error case if necessary. Locking as per n_tty_receive_buf.
+ *	the error case if necessary.
+ *
+ *	n_tty_receive_buf()/producer path:
+ *		caller holds non-exclusive termios_rwsem
+ *		publishes read_head via put_tty_queue()
  */
  */
-static inline 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)
 {
 {
 	struct n_tty_data *ldata = tty->disc_data;
 	struct n_tty_data *ldata = tty->disc_data;
 
 
@@ -1136,6 +1216,26 @@ static inline void n_tty_receive_parity_error(struct tty_struct *tty,
 	wake_up_interruptible(&tty->read_wait);
 	wake_up_interruptible(&tty->read_wait);
 }
 }
 
 
+static void
+n_tty_receive_signal_char(struct tty_struct *tty, int signal, unsigned char c)
+{
+	if (!L_NOFLSH(tty)) {
+		/* flushing needs exclusive termios_rwsem */
+		up_read(&tty->termios_rwsem);
+		n_tty_flush_buffer(tty);
+		tty_driver_flush_buffer(tty);
+		down_read(&tty->termios_rwsem);
+	}
+	if (I_IXON(tty))
+		start_tty(tty);
+	if (L_ECHO(tty)) {
+		echo_char(c, tty);
+		commit_echoes(tty);
+	}
+	isig(signal, tty);
+	return;
+}
+
 /**
 /**
  *	n_tty_receive_char	-	perform processing
  *	n_tty_receive_char	-	perform processing
  *	@tty: terminal device
  *	@tty: terminal device
@@ -1144,117 +1244,54 @@ static inline void n_tty_receive_parity_error(struct tty_struct *tty,
  *	Process an individual character of input received from the driver.
  *	Process an individual character of input received from the driver.
  *	This is serialized with respect to itself by the rules for the
  *	This is serialized with respect to itself by the rules for the
  *	driver above.
  *	driver above.
+ *
+ *	n_tty_receive_buf()/producer path:
+ *		caller holds non-exclusive termios_rwsem
+ *		publishes canon_head if canonical mode is active
+ *		otherwise, publishes read_head via put_tty_queue()
+ *
+ *	Returns 1 if LNEXT was received, else returns 0
  */
  */
 
 
-static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c)
+static int
+n_tty_receive_char_special(struct tty_struct *tty, unsigned char c)
 {
 {
 	struct n_tty_data *ldata = tty->disc_data;
 	struct n_tty_data *ldata = tty->disc_data;
-	unsigned long flags;
 	int parmrk;
 	int parmrk;
 
 
-	if (ldata->raw) {
-		put_tty_queue(c, ldata);
-		return;
-	}
-
-	if (I_ISTRIP(tty))
-		c &= 0x7f;
-	if (I_IUCLC(tty) && L_IEXTEN(tty))
-		c = tolower(c);
-
-	if (L_EXTPROC(tty)) {
-		put_tty_queue(c, ldata);
-		return;
-	}
-
-	if (tty->stopped && !tty->flow_stopped && I_IXON(tty) &&
-	    I_IXANY(tty) && c != START_CHAR(tty) && c != STOP_CHAR(tty) &&
-	    c != INTR_CHAR(tty) && c != QUIT_CHAR(tty) && c != SUSP_CHAR(tty)) {
-		start_tty(tty);
-		process_echoes(tty);
-	}
-
-	if (tty->closing) {
-		if (I_IXON(tty)) {
-			if (c == START_CHAR(tty)) {
-				start_tty(tty);
-				process_echoes(tty);
-			} else if (c == STOP_CHAR(tty))
-				stop_tty(tty);
-		}
-		return;
-	}
-
-	/*
-	 * If the previous character was LNEXT, or we know that this
-	 * character is not one of the characters that we'll have to
-	 * handle specially, do shortcut processing to speed things
-	 * up.
-	 */
-	if (!test_bit(c, ldata->process_char_map) || ldata->lnext) {
-		ldata->lnext = 0;
-		parmrk = (c == (unsigned char) '\377' && I_PARMRK(tty)) ? 1 : 0;
-		if (ldata->read_cnt >= (N_TTY_BUF_SIZE - parmrk - 1)) {
-			/* beep if no space */
-			if (L_ECHO(tty))
-				process_output('\a', tty);
-			return;
-		}
-		if (L_ECHO(tty)) {
-			finish_erasing(ldata);
-			/* Record the column of first canon char. */
-			if (ldata->canon_head == ldata->read_head)
-				echo_set_canon_col(ldata);
-			echo_char(c, tty);
-			process_echoes(tty);
-		}
-		if (parmrk)
-			put_tty_queue(c, ldata);
-		put_tty_queue(c, ldata);
-		return;
-	}
-
 	if (I_IXON(tty)) {
 	if (I_IXON(tty)) {
 		if (c == START_CHAR(tty)) {
 		if (c == START_CHAR(tty)) {
 			start_tty(tty);
 			start_tty(tty);
-			process_echoes(tty);
-			return;
+			commit_echoes(tty);
+			return 0;
 		}
 		}
 		if (c == STOP_CHAR(tty)) {
 		if (c == STOP_CHAR(tty)) {
 			stop_tty(tty);
 			stop_tty(tty);
-			return;
+			return 0;
 		}
 		}
 	}
 	}
 
 
 	if (L_ISIG(tty)) {
 	if (L_ISIG(tty)) {
-		int signal;
-		signal = SIGINT;
-		if (c == INTR_CHAR(tty))
-			goto send_signal;
-		signal = SIGQUIT;
-		if (c == QUIT_CHAR(tty))
-			goto send_signal;
-		signal = SIGTSTP;
-		if (c == SUSP_CHAR(tty)) {
-send_signal:
-			if (!L_NOFLSH(tty)) {
-				n_tty_flush_buffer(tty);
-				tty_driver_flush_buffer(tty);
-			}
-			if (I_IXON(tty))
-				start_tty(tty);
-			if (L_ECHO(tty)) {
-				echo_char(c, tty);
-				process_echoes(tty);
-			}
-			isig(signal, tty);
-			return;
+		if (c == INTR_CHAR(tty)) {
+			n_tty_receive_signal_char(tty, SIGINT, c);
+			return 0;
+		} else if (c == QUIT_CHAR(tty)) {
+			n_tty_receive_signal_char(tty, SIGQUIT, c);
+			return 0;
+		} else if (c == SUSP_CHAR(tty)) {
+			n_tty_receive_signal_char(tty, SIGTSTP, c);
+			return 0;
 		}
 		}
 	}
 	}
 
 
+	if (tty->stopped && !tty->flow_stopped && I_IXON(tty) && I_IXANY(tty)) {
+		start_tty(tty);
+		process_echoes(tty);
+	}
+
 	if (c == '\r') {
 	if (c == '\r') {
 		if (I_IGNCR(tty))
 		if (I_IGNCR(tty))
-			return;
+			return 0;
 		if (I_ICRNL(tty))
 		if (I_ICRNL(tty))
 			c = '\n';
 			c = '\n';
 	} else if (c == '\n' && I_INLCR(tty))
 	} else if (c == '\n' && I_INLCR(tty))
@@ -1264,8 +1301,8 @@ send_signal:
 		if (c == ERASE_CHAR(tty) || c == KILL_CHAR(tty) ||
 		if (c == ERASE_CHAR(tty) || c == KILL_CHAR(tty) ||
 		    (c == WERASE_CHAR(tty) && L_IEXTEN(tty))) {
 		    (c == WERASE_CHAR(tty) && L_IEXTEN(tty))) {
 			eraser(c, tty);
 			eraser(c, tty);
-			process_echoes(tty);
-			return;
+			commit_echoes(tty);
+			return 0;
 		}
 		}
 		if (c == LNEXT_CHAR(tty) && L_IEXTEN(tty)) {
 		if (c == LNEXT_CHAR(tty) && L_IEXTEN(tty)) {
 			ldata->lnext = 1;
 			ldata->lnext = 1;
@@ -1274,42 +1311,32 @@ send_signal:
 				if (L_ECHOCTL(tty)) {
 				if (L_ECHOCTL(tty)) {
 					echo_char_raw('^', ldata);
 					echo_char_raw('^', ldata);
 					echo_char_raw('\b', ldata);
 					echo_char_raw('\b', ldata);
-					process_echoes(tty);
+					commit_echoes(tty);
 				}
 				}
 			}
 			}
-			return;
+			return 1;
 		}
 		}
-		if (c == REPRINT_CHAR(tty) && L_ECHO(tty) &&
-		    L_IEXTEN(tty)) {
-			unsigned long tail = ldata->canon_head;
+		if (c == REPRINT_CHAR(tty) && L_ECHO(tty) && L_IEXTEN(tty)) {
+			size_t tail = ldata->canon_head;
 
 
 			finish_erasing(ldata);
 			finish_erasing(ldata);
 			echo_char(c, tty);
 			echo_char(c, tty);
 			echo_char_raw('\n', ldata);
 			echo_char_raw('\n', ldata);
 			while (tail != ldata->read_head) {
 			while (tail != ldata->read_head) {
-				echo_char(ldata->read_buf[tail], tty);
-				tail = (tail+1) & (N_TTY_BUF_SIZE-1);
+				echo_char(read_buf(ldata, tail), tty);
+				tail++;
 			}
 			}
-			process_echoes(tty);
-			return;
+			commit_echoes(tty);
+			return 0;
 		}
 		}
 		if (c == '\n') {
 		if (c == '\n') {
-			if (ldata->read_cnt >= N_TTY_BUF_SIZE) {
-				if (L_ECHO(tty))
-					process_output('\a', tty);
-				return;
-			}
 			if (L_ECHO(tty) || L_ECHONL(tty)) {
 			if (L_ECHO(tty) || L_ECHONL(tty)) {
 				echo_char_raw('\n', ldata);
 				echo_char_raw('\n', ldata);
-				process_echoes(tty);
+				commit_echoes(tty);
 			}
 			}
 			goto handle_newline;
 			goto handle_newline;
 		}
 		}
 		if (c == EOF_CHAR(tty)) {
 		if (c == EOF_CHAR(tty)) {
-			if (ldata->read_cnt >= N_TTY_BUF_SIZE)
-				return;
-			if (ldata->canon_head != ldata->read_head)
-				set_bit(TTY_PUSH, &tty->flags);
 			c = __DISABLED_CHAR;
 			c = __DISABLED_CHAR;
 			goto handle_newline;
 			goto handle_newline;
 		}
 		}
@@ -1317,11 +1344,6 @@ send_signal:
 		    (c == EOL2_CHAR(tty) && L_IEXTEN(tty))) {
 		    (c == EOL2_CHAR(tty) && L_IEXTEN(tty))) {
 			parmrk = (c == (unsigned char) '\377' && I_PARMRK(tty))
 			parmrk = (c == (unsigned char) '\377' && I_PARMRK(tty))
 				 ? 1 : 0;
 				 ? 1 : 0;
-			if (ldata->read_cnt >= (N_TTY_BUF_SIZE - parmrk)) {
-				if (L_ECHO(tty))
-					process_output('\a', tty);
-				return;
-			}
 			/*
 			/*
 			 * XXX are EOL_CHAR and EOL2_CHAR echoed?!?
 			 * XXX are EOL_CHAR and EOL2_CHAR echoed?!?
 			 */
 			 */
@@ -1330,7 +1352,7 @@ send_signal:
 				if (ldata->canon_head == ldata->read_head)
 				if (ldata->canon_head == ldata->read_head)
 					echo_set_canon_col(ldata);
 					echo_set_canon_col(ldata);
 				echo_char(c, tty);
 				echo_char(c, tty);
-				process_echoes(tty);
+				commit_echoes(tty);
 			}
 			}
 			/*
 			/*
 			 * XXX does PARMRK doubling happen for
 			 * XXX does PARMRK doubling happen for
@@ -1340,26 +1362,17 @@ send_signal:
 				put_tty_queue(c, ldata);
 				put_tty_queue(c, ldata);
 
 
 handle_newline:
 handle_newline:
-			raw_spin_lock_irqsave(&ldata->read_lock, flags);
-			set_bit(ldata->read_head, ldata->read_flags);
-			put_tty_queue_nolock(c, ldata);
+			set_bit(ldata->read_head & (N_TTY_BUF_SIZE - 1), ldata->read_flags);
+			put_tty_queue(c, ldata);
 			ldata->canon_head = ldata->read_head;
 			ldata->canon_head = ldata->read_head;
-			ldata->canon_data++;
-			raw_spin_unlock_irqrestore(&ldata->read_lock, flags);
 			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(&tty->read_wait);
 				wake_up_interruptible(&tty->read_wait);
-			return;
+			return 0;
 		}
 		}
 	}
 	}
 
 
 	parmrk = (c == (unsigned char) '\377' && I_PARMRK(tty)) ? 1 : 0;
 	parmrk = (c == (unsigned char) '\377' && I_PARMRK(tty)) ? 1 : 0;
-	if (ldata->read_cnt >= (N_TTY_BUF_SIZE - parmrk - 1)) {
-		/* beep if no space */
-		if (L_ECHO(tty))
-			process_output('\a', tty);
-		return;
-	}
 	if (L_ECHO(tty)) {
 	if (L_ECHO(tty)) {
 		finish_erasing(ldata);
 		finish_erasing(ldata);
 		if (c == '\n')
 		if (c == '\n')
@@ -1370,29 +1383,123 @@ handle_newline:
 				echo_set_canon_col(ldata);
 				echo_set_canon_col(ldata);
 			echo_char(c, tty);
 			echo_char(c, tty);
 		}
 		}
-		process_echoes(tty);
+		commit_echoes(tty);
 	}
 	}
 
 
 	if (parmrk)
 	if (parmrk)
 		put_tty_queue(c, ldata);
 		put_tty_queue(c, ldata);
 
 
 	put_tty_queue(c, ldata);
 	put_tty_queue(c, ldata);
+	return 0;
 }
 }
 
 
+static inline void
+n_tty_receive_char_inline(struct tty_struct *tty, unsigned char c)
+{
+	struct n_tty_data *ldata = tty->disc_data;
+	int parmrk;
 
 
-/**
- *	n_tty_write_wakeup	-	asynchronous I/O notifier
- *	@tty: tty device
- *
- *	Required for the ptys, serial driver etc. since processes
- *	that attach themselves to the master and rely on ASYNC
- *	IO must be woken up
- */
+	if (tty->stopped && !tty->flow_stopped && I_IXON(tty) && I_IXANY(tty)) {
+		start_tty(tty);
+		process_echoes(tty);
+	}
+	if (L_ECHO(tty)) {
+		finish_erasing(ldata);
+		/* Record the column of first canon char. */
+		if (ldata->canon_head == ldata->read_head)
+			echo_set_canon_col(ldata);
+		echo_char(c, tty);
+		commit_echoes(tty);
+	}
+	parmrk = (c == (unsigned char) '\377' && I_PARMRK(tty)) ? 1 : 0;
+	if (parmrk)
+		put_tty_queue(c, ldata);
+	put_tty_queue(c, ldata);
+}
 
 
-static void n_tty_write_wakeup(struct tty_struct *tty)
+static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c)
 {
 {
-	if (tty->fasync && test_and_clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags))
-		kill_fasync(&tty->fasync, SIGIO, POLL_OUT);
+	n_tty_receive_char_inline(tty, c);
+}
+
+static inline void
+n_tty_receive_char_fast(struct tty_struct *tty, unsigned char c)
+{
+	struct n_tty_data *ldata = tty->disc_data;
+
+	if (tty->stopped && !tty->flow_stopped && I_IXON(tty) && I_IXANY(tty)) {
+		start_tty(tty);
+		process_echoes(tty);
+	}
+	if (L_ECHO(tty)) {
+		finish_erasing(ldata);
+		/* Record the column of first canon char. */
+		if (ldata->canon_head == ldata->read_head)
+			echo_set_canon_col(ldata);
+		echo_char(c, tty);
+		commit_echoes(tty);
+	}
+	put_tty_queue(c, ldata);
+}
+
+static inline void
+n_tty_receive_char_closing(struct tty_struct *tty, unsigned char c)
+{
+	if (I_ISTRIP(tty))
+		c &= 0x7f;
+	if (I_IUCLC(tty) && L_IEXTEN(tty))
+		c = tolower(c);
+
+	if (I_IXON(tty)) {
+		if (c == STOP_CHAR(tty))
+			stop_tty(tty);
+		else if (c == START_CHAR(tty) ||
+			 (tty->stopped && !tty->flow_stopped && I_IXANY(tty) &&
+			  c != INTR_CHAR(tty) && c != QUIT_CHAR(tty) &&
+			  c != SUSP_CHAR(tty))) {
+			start_tty(tty);
+			process_echoes(tty);
+		}
+	}
+}
+
+static void
+n_tty_receive_char_flagged(struct tty_struct *tty, unsigned char c, char flag)
+{
+	char buf[64];
+
+	switch (flag) {
+	case TTY_BREAK:
+		n_tty_receive_break(tty);
+		break;
+	case TTY_PARITY:
+	case TTY_FRAME:
+		n_tty_receive_parity_error(tty, c);
+		break;
+	case TTY_OVERRUN:
+		n_tty_receive_overrun(tty);
+		break;
+	default:
+		printk(KERN_ERR "%s: unknown flag %d\n",
+		       tty_name(tty, buf), flag);
+		break;
+	}
+}
+
+static void
+n_tty_receive_char_lnext(struct tty_struct *tty, unsigned char c, char flag)
+{
+	struct n_tty_data *ldata = tty->disc_data;
+
+	ldata->lnext = 0;
+	if (likely(flag == TTY_NORMAL)) {
+		if (I_ISTRIP(tty))
+			c &= 0x7f;
+		if (I_IUCLC(tty) && L_IEXTEN(tty))
+			c = tolower(c);
+		n_tty_receive_char(tty, c);
+	} else
+		n_tty_receive_char_flagged(tty, c, flag);
 }
 }
 
 
 /**
 /**
@@ -1406,86 +1513,220 @@ static void n_tty_write_wakeup(struct tty_struct *tty)
  *	been received. This function must be called from soft contexts
  *	been received. This function must be called from soft contexts
  *	not from interrupt context. The driver is responsible for making
  *	not from interrupt context. The driver is responsible for making
  *	calls one at a time and in order (or using flush_to_ldisc)
  *	calls one at a time and in order (or using flush_to_ldisc)
+ *
+ *	n_tty_receive_buf()/producer path:
+ *		claims non-exclusive termios_rwsem
+ *		publishes read_head and canon_head
  */
  */
 
 
-static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp,
-			      char *fp, int count)
+static void
+n_tty_receive_buf_real_raw(struct tty_struct *tty, const unsigned char *cp,
+			   char *fp, int count)
 {
 {
 	struct n_tty_data *ldata = tty->disc_data;
 	struct n_tty_data *ldata = tty->disc_data;
-	const unsigned char *p;
-	char *f, flags = TTY_NORMAL;
-	int	i;
-	char	buf[64];
-	unsigned long cpuflags;
-
-	if (ldata->real_raw) {
-		raw_spin_lock_irqsave(&ldata->read_lock, cpuflags);
-		i = min(N_TTY_BUF_SIZE - ldata->read_cnt,
-			N_TTY_BUF_SIZE - ldata->read_head);
-		i = min(count, i);
-		memcpy(ldata->read_buf + ldata->read_head, cp, i);
-		ldata->read_head = (ldata->read_head + i) & (N_TTY_BUF_SIZE-1);
-		ldata->read_cnt += i;
-		cp += i;
-		count -= i;
-
-		i = min(N_TTY_BUF_SIZE - ldata->read_cnt,
-			N_TTY_BUF_SIZE - ldata->read_head);
-		i = min(count, i);
-		memcpy(ldata->read_buf + ldata->read_head, cp, i);
-		ldata->read_head = (ldata->read_head + i) & (N_TTY_BUF_SIZE-1);
-		ldata->read_cnt += i;
-		raw_spin_unlock_irqrestore(&ldata->read_lock, cpuflags);
-	} else {
-		for (i = count, p = cp, f = fp; i; i--, p++) {
-			if (f)
-				flags = *f++;
-			switch (flags) {
-			case TTY_NORMAL:
-				n_tty_receive_char(tty, *p);
-				break;
-			case TTY_BREAK:
-				n_tty_receive_break(tty);
-				break;
-			case TTY_PARITY:
-			case TTY_FRAME:
-				n_tty_receive_parity_error(tty, *p);
-				break;
-			case TTY_OVERRUN:
-				n_tty_receive_overrun(tty);
-				break;
-			default:
-				printk(KERN_ERR "%s: unknown flag %d\n",
-				       tty_name(tty, buf), flags);
-				break;
+	size_t n, head;
+
+	head = ldata->read_head & (N_TTY_BUF_SIZE - 1);
+	n = N_TTY_BUF_SIZE - max(read_cnt(ldata), head);
+	n = min_t(size_t, count, n);
+	memcpy(read_buf_addr(ldata, head), cp, n);
+	ldata->read_head += n;
+	cp += n;
+	count -= n;
+
+	head = ldata->read_head & (N_TTY_BUF_SIZE - 1);
+	n = N_TTY_BUF_SIZE - max(read_cnt(ldata), head);
+	n = min_t(size_t, count, n);
+	memcpy(read_buf_addr(ldata, head), cp, n);
+	ldata->read_head += n;
+}
+
+static void
+n_tty_receive_buf_raw(struct tty_struct *tty, const unsigned char *cp,
+		      char *fp, int count)
+{
+	struct n_tty_data *ldata = tty->disc_data;
+	char flag = TTY_NORMAL;
+
+	while (count--) {
+		if (fp)
+			flag = *fp++;
+		if (likely(flag == TTY_NORMAL))
+			put_tty_queue(*cp++, ldata);
+		else
+			n_tty_receive_char_flagged(tty, *cp++, flag);
+	}
+}
+
+static void
+n_tty_receive_buf_closing(struct tty_struct *tty, const unsigned char *cp,
+			  char *fp, int count)
+{
+	char flag = TTY_NORMAL;
+
+	while (count--) {
+		if (fp)
+			flag = *fp++;
+		if (likely(flag == TTY_NORMAL))
+			n_tty_receive_char_closing(tty, *cp++);
+		else
+			n_tty_receive_char_flagged(tty, *cp++, flag);
+	}
+}
+
+static void
+n_tty_receive_buf_standard(struct tty_struct *tty, const unsigned char *cp,
+			  char *fp, int count)
+{
+	struct n_tty_data *ldata = tty->disc_data;
+	char flag = TTY_NORMAL;
+
+	while (count--) {
+		if (fp)
+			flag = *fp++;
+		if (likely(flag == TTY_NORMAL)) {
+			unsigned char c = *cp++;
+
+			if (I_ISTRIP(tty))
+				c &= 0x7f;
+			if (I_IUCLC(tty) && L_IEXTEN(tty))
+				c = tolower(c);
+			if (L_EXTPROC(tty)) {
+				put_tty_queue(c, ldata);
+				continue;
 			}
 			}
+			if (!test_bit(c, ldata->char_map))
+				n_tty_receive_char_inline(tty, c);
+			else if (n_tty_receive_char_special(tty, c) && count) {
+				if (fp)
+					flag = *fp++;
+				n_tty_receive_char_lnext(tty, *cp++, flag);
+				count--;
+			}
+		} else
+			n_tty_receive_char_flagged(tty, *cp++, flag);
+	}
+}
+
+static void
+n_tty_receive_buf_fast(struct tty_struct *tty, const unsigned char *cp,
+		       char *fp, int count)
+{
+	struct n_tty_data *ldata = tty->disc_data;
+	char flag = TTY_NORMAL;
+
+	while (count--) {
+		if (fp)
+			flag = *fp++;
+		if (likely(flag == TTY_NORMAL)) {
+			unsigned char c = *cp++;
+
+			if (!test_bit(c, ldata->char_map))
+				n_tty_receive_char_fast(tty, c);
+			else if (n_tty_receive_char_special(tty, c) && count) {
+				if (fp)
+					flag = *fp++;
+				n_tty_receive_char_lnext(tty, *cp++, flag);
+				count--;
+			}
+		} else
+			n_tty_receive_char_flagged(tty, *cp++, flag);
+	}
+}
+
+static void __receive_buf(struct tty_struct *tty, const unsigned char *cp,
+			  char *fp, int count)
+{
+	struct n_tty_data *ldata = tty->disc_data;
+	bool preops = I_ISTRIP(tty) || (I_IUCLC(tty) && L_IEXTEN(tty));
+
+	if (ldata->real_raw)
+		n_tty_receive_buf_real_raw(tty, cp, fp, count);
+	else if (ldata->raw || (L_EXTPROC(tty) && !preops))
+		n_tty_receive_buf_raw(tty, cp, fp, count);
+	else if (tty->closing && !L_EXTPROC(tty))
+		n_tty_receive_buf_closing(tty, cp, fp, count);
+	else {
+		if (ldata->lnext) {
+			char flag = TTY_NORMAL;
+
+			if (fp)
+				flag = *fp++;
+			n_tty_receive_char_lnext(tty, *cp++, flag);
+			count--;
 		}
 		}
+
+		if (!preops && !I_PARMRK(tty))
+			n_tty_receive_buf_fast(tty, cp, fp, count);
+		else
+			n_tty_receive_buf_standard(tty, cp, fp, count);
+
+		flush_echoes(tty);
 		if (tty->ops->flush_chars)
 		if (tty->ops->flush_chars)
 			tty->ops->flush_chars(tty);
 			tty->ops->flush_chars(tty);
 	}
 	}
 
 
-	set_room(tty);
-
-	if ((!ldata->icanon && (ldata->read_cnt >= ldata->minimum_to_wake)) ||
+	if ((!ldata->icanon && (read_cnt(ldata) >= ldata->minimum_to_wake)) ||
 		L_EXTPROC(tty)) {
 		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(&tty->read_wait);
 			wake_up_interruptible(&tty->read_wait);
 	}
 	}
+}
+
+static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp,
+			      char *fp, int count)
+{
+	int room, n;
+
+	down_read(&tty->termios_rwsem);
 
 
-	/*
-	 * Check the remaining room for the input canonicalization
-	 * mode.  We don't want to throttle the driver if we're in
-	 * canonical mode and don't have a newline yet!
-	 */
 	while (1) {
 	while (1) {
-		tty_set_flow_change(tty, TTY_THROTTLE_SAFE);
-		if (tty->receive_room >= TTY_THRESHOLD_THROTTLE)
+		room = receive_room(tty);
+		n = min(count, room);
+		if (!n)
 			break;
 			break;
-		if (!tty_throttle_safe(tty))
+		__receive_buf(tty, cp, fp, n);
+		cp += n;
+		if (fp)
+			fp += n;
+		count -= n;
+	}
+
+	tty->receive_room = room;
+	n_tty_check_throttle(tty);
+	up_read(&tty->termios_rwsem);
+}
+
+static int n_tty_receive_buf2(struct tty_struct *tty, const unsigned char *cp,
+			      char *fp, int count)
+{
+	struct n_tty_data *ldata = tty->disc_data;
+	int room, n, rcvd = 0;
+
+	down_read(&tty->termios_rwsem);
+
+	while (1) {
+		room = receive_room(tty);
+		n = min(count, room);
+		if (!n) {
+			if (!room)
+				ldata->no_room = 1;
 			break;
 			break;
+		}
+		__receive_buf(tty, cp, fp, n);
+		cp += n;
+		if (fp)
+			fp += n;
+		count -= n;
+		rcvd += n;
 	}
 	}
-	__tty_set_flow_change(tty, 0);
+
+	tty->receive_room = room;
+	n_tty_check_throttle(tty);
+	up_read(&tty->termios_rwsem);
+
+	return rcvd;
 }
 }
 
 
 int is_ignored(int sig)
 int is_ignored(int sig)
@@ -1505,7 +1746,7 @@ int is_ignored(int sig)
  *	guaranteed that this function will not be re-entered or in progress
  *	guaranteed that this function will not be re-entered or in progress
  *	when the ldisc is closed.
  *	when the ldisc is closed.
  *
  *
- *	Locking: Caller holds tty->termios_mutex
+ *	Locking: Caller holds tty->termios_rwsem
  */
  */
 
 
 static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old)
 static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old)
@@ -1517,12 +1758,13 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old)
 		canon_change = (old->c_lflag ^ tty->termios.c_lflag) & ICANON;
 		canon_change = (old->c_lflag ^ tty->termios.c_lflag) & ICANON;
 	if (canon_change) {
 	if (canon_change) {
 		bitmap_zero(ldata->read_flags, N_TTY_BUF_SIZE);
 		bitmap_zero(ldata->read_flags, N_TTY_BUF_SIZE);
+		ldata->line_start = 0;
 		ldata->canon_head = ldata->read_tail;
 		ldata->canon_head = ldata->read_tail;
-		ldata->canon_data = 0;
 		ldata->erasing = 0;
 		ldata->erasing = 0;
+		ldata->lnext = 0;
 	}
 	}
 
 
-	if (canon_change && !L_ICANON(tty) && ldata->read_cnt)
+	if (canon_change && !L_ICANON(tty) && read_cnt(ldata))
 		wake_up_interruptible(&tty->read_wait);
 		wake_up_interruptible(&tty->read_wait);
 
 
 	ldata->icanon = (L_ICANON(tty) != 0);
 	ldata->icanon = (L_ICANON(tty) != 0);
@@ -1531,41 +1773,38 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old)
 	    I_ICRNL(tty) || I_INLCR(tty) || L_ICANON(tty) ||
 	    I_ICRNL(tty) || I_INLCR(tty) || L_ICANON(tty) ||
 	    I_IXON(tty) || L_ISIG(tty) || L_ECHO(tty) ||
 	    I_IXON(tty) || L_ISIG(tty) || L_ECHO(tty) ||
 	    I_PARMRK(tty)) {
 	    I_PARMRK(tty)) {
-		bitmap_zero(ldata->process_char_map, 256);
+		bitmap_zero(ldata->char_map, 256);
 
 
 		if (I_IGNCR(tty) || I_ICRNL(tty))
 		if (I_IGNCR(tty) || I_ICRNL(tty))
-			set_bit('\r', ldata->process_char_map);
+			set_bit('\r', ldata->char_map);
 		if (I_INLCR(tty))
 		if (I_INLCR(tty))
-			set_bit('\n', ldata->process_char_map);
+			set_bit('\n', ldata->char_map);
 
 
 		if (L_ICANON(tty)) {
 		if (L_ICANON(tty)) {
-			set_bit(ERASE_CHAR(tty), ldata->process_char_map);
-			set_bit(KILL_CHAR(tty), ldata->process_char_map);
-			set_bit(EOF_CHAR(tty), ldata->process_char_map);
-			set_bit('\n', ldata->process_char_map);
-			set_bit(EOL_CHAR(tty), ldata->process_char_map);
+			set_bit(ERASE_CHAR(tty), ldata->char_map);
+			set_bit(KILL_CHAR(tty), ldata->char_map);
+			set_bit(EOF_CHAR(tty), ldata->char_map);
+			set_bit('\n', ldata->char_map);
+			set_bit(EOL_CHAR(tty), ldata->char_map);
 			if (L_IEXTEN(tty)) {
 			if (L_IEXTEN(tty)) {
-				set_bit(WERASE_CHAR(tty),
-					ldata->process_char_map);
-				set_bit(LNEXT_CHAR(tty),
-					ldata->process_char_map);
-				set_bit(EOL2_CHAR(tty),
-					ldata->process_char_map);
+				set_bit(WERASE_CHAR(tty), ldata->char_map);
+				set_bit(LNEXT_CHAR(tty), ldata->char_map);
+				set_bit(EOL2_CHAR(tty), ldata->char_map);
 				if (L_ECHO(tty))
 				if (L_ECHO(tty))
 					set_bit(REPRINT_CHAR(tty),
 					set_bit(REPRINT_CHAR(tty),
-						ldata->process_char_map);
+						ldata->char_map);
 			}
 			}
 		}
 		}
 		if (I_IXON(tty)) {
 		if (I_IXON(tty)) {
-			set_bit(START_CHAR(tty), ldata->process_char_map);
-			set_bit(STOP_CHAR(tty), ldata->process_char_map);
+			set_bit(START_CHAR(tty), ldata->char_map);
+			set_bit(STOP_CHAR(tty), ldata->char_map);
 		}
 		}
 		if (L_ISIG(tty)) {
 		if (L_ISIG(tty)) {
-			set_bit(INTR_CHAR(tty), ldata->process_char_map);
-			set_bit(QUIT_CHAR(tty), ldata->process_char_map);
-			set_bit(SUSP_CHAR(tty), ldata->process_char_map);
+			set_bit(INTR_CHAR(tty), ldata->char_map);
+			set_bit(QUIT_CHAR(tty), ldata->char_map);
+			set_bit(SUSP_CHAR(tty), ldata->char_map);
 		}
 		}
-		clear_bit(__DISABLED_CHAR, ldata->process_char_map);
+		clear_bit(__DISABLED_CHAR, ldata->char_map);
 		ldata->raw = 0;
 		ldata->raw = 0;
 		ldata->real_raw = 0;
 		ldata->real_raw = 0;
 	} else {
 	} else {
@@ -1608,9 +1847,7 @@ static void n_tty_close(struct tty_struct *tty)
 	if (tty->link)
 	if (tty->link)
 		n_tty_packet_mode_flush(tty);
 		n_tty_packet_mode_flush(tty);
 
 
-	kfree(ldata->read_buf);
-	kfree(ldata->echo_buf);
-	kfree(ldata);
+	vfree(ldata);
 	tty->disc_data = NULL;
 	tty->disc_data = NULL;
 }
 }
 
 
@@ -1628,26 +1865,23 @@ static int n_tty_open(struct tty_struct *tty)
 {
 {
 	struct n_tty_data *ldata;
 	struct n_tty_data *ldata;
 
 
-	ldata = kzalloc(sizeof(*ldata), GFP_KERNEL);
+	/* Currently a malloc failure here can panic */
+	ldata = vmalloc(sizeof(*ldata));
 	if (!ldata)
 	if (!ldata)
 		goto err;
 		goto err;
 
 
 	ldata->overrun_time = jiffies;
 	ldata->overrun_time = jiffies;
 	mutex_init(&ldata->atomic_read_lock);
 	mutex_init(&ldata->atomic_read_lock);
 	mutex_init(&ldata->output_lock);
 	mutex_init(&ldata->output_lock);
-	mutex_init(&ldata->echo_lock);
-	raw_spin_lock_init(&ldata->read_lock);
-
-	/* These are ugly. Currently a malloc failure here can panic */
-	ldata->read_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL);
-	ldata->echo_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL);
-	if (!ldata->read_buf || !ldata->echo_buf)
-		goto err_free_bufs;
 
 
 	tty->disc_data = ldata;
 	tty->disc_data = ldata;
 	reset_buffer_flags(tty->disc_data);
 	reset_buffer_flags(tty->disc_data);
 	ldata->column = 0;
 	ldata->column = 0;
+	ldata->canon_column = 0;
 	ldata->minimum_to_wake = 1;
 	ldata->minimum_to_wake = 1;
+	ldata->num_overrun = 0;
+	ldata->no_room = 0;
+	ldata->lnext = 0;
 	tty->closing = 0;
 	tty->closing = 0;
 	/* indicate buffer work may resume */
 	/* indicate buffer work may resume */
 	clear_bit(TTY_LDISC_HALTED, &tty->flags);
 	clear_bit(TTY_LDISC_HALTED, &tty->flags);
@@ -1655,10 +1889,6 @@ static int n_tty_open(struct tty_struct *tty)
 	tty_unthrottle(tty);
 	tty_unthrottle(tty);
 
 
 	return 0;
 	return 0;
-err_free_bufs:
-	kfree(ldata->read_buf);
-	kfree(ldata->echo_buf);
-	kfree(ldata);
 err:
 err:
 	return -ENOMEM;
 	return -ENOMEM;
 }
 }
@@ -1667,11 +1897,10 @@ static inline int input_available_p(struct tty_struct *tty, int amt)
 {
 {
 	struct n_tty_data *ldata = tty->disc_data;
 	struct n_tty_data *ldata = tty->disc_data;
 
 
-	tty_flush_to_ldisc(tty);
 	if (ldata->icanon && !L_EXTPROC(tty)) {
 	if (ldata->icanon && !L_EXTPROC(tty)) {
-		if (ldata->canon_data)
+		if (ldata->canon_head != ldata->read_tail)
 			return 1;
 			return 1;
-	} else if (ldata->read_cnt >= (amt ? amt : 1))
+	} else if (read_cnt(ldata) >= (amt ? amt : 1))
 		return 1;
 		return 1;
 
 
 	return 0;
 	return 0;
@@ -1692,6 +1921,9 @@ static inline int input_available_p(struct tty_struct *tty, int amt)
  *
  *
  *	Called under the ldata->atomic_read_lock sem
  *	Called under the ldata->atomic_read_lock sem
  *
  *
+ *	n_tty_read()/consumer path:
+ *		caller holds non-exclusive termios_rwsem
+ *		read_tail published
  */
  */
 
 
 static int copy_from_read_buf(struct tty_struct *tty,
 static int copy_from_read_buf(struct tty_struct *tty,
@@ -1702,34 +1934,114 @@ static int copy_from_read_buf(struct tty_struct *tty,
 	struct n_tty_data *ldata = tty->disc_data;
 	struct n_tty_data *ldata = tty->disc_data;
 	int retval;
 	int retval;
 	size_t n;
 	size_t n;
-	unsigned long flags;
 	bool is_eof;
 	bool is_eof;
+	size_t tail = ldata->read_tail & (N_TTY_BUF_SIZE - 1);
 
 
 	retval = 0;
 	retval = 0;
-	raw_spin_lock_irqsave(&ldata->read_lock, flags);
-	n = min(ldata->read_cnt, N_TTY_BUF_SIZE - ldata->read_tail);
+	n = min(read_cnt(ldata), N_TTY_BUF_SIZE - tail);
 	n = min(*nr, n);
 	n = min(*nr, n);
-	raw_spin_unlock_irqrestore(&ldata->read_lock, flags);
 	if (n) {
 	if (n) {
-		retval = copy_to_user(*b, &ldata->read_buf[ldata->read_tail], n);
+		retval = copy_to_user(*b, read_buf_addr(ldata, tail), n);
 		n -= retval;
 		n -= retval;
-		is_eof = n == 1 &&
-			ldata->read_buf[ldata->read_tail] == EOF_CHAR(tty);
-		tty_audit_add_data(tty, &ldata->read_buf[ldata->read_tail], n,
+		is_eof = n == 1 && read_buf(ldata, tail) == EOF_CHAR(tty);
+		tty_audit_add_data(tty, read_buf_addr(ldata, tail), n,
 				ldata->icanon);
 				ldata->icanon);
-		raw_spin_lock_irqsave(&ldata->read_lock, flags);
-		ldata->read_tail = (ldata->read_tail + n) & (N_TTY_BUF_SIZE-1);
-		ldata->read_cnt -= n;
+		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 && !ldata->read_cnt)
+		if (L_EXTPROC(tty) && ldata->icanon && is_eof && !read_cnt(ldata))
 			n = 0;
 			n = 0;
-		raw_spin_unlock_irqrestore(&ldata->read_lock, flags);
 		*b += n;
 		*b += n;
 		*nr -= n;
 		*nr -= n;
 	}
 	}
 	return retval;
 	return retval;
 }
 }
 
 
+/**
+ *	canon_copy_from_read_buf	-	copy read data in canonical mode
+ *	@tty: terminal device
+ *	@b: user data
+ *	@nr: size of data
+ *
+ *	Helper function for n_tty_read.  It is only called when ICANON is on;
+ *	it copies one line of input up to and including the line-delimiting
+ *	character into the user-space buffer.
+ *
+ *	Called under the atomic_read_lock mutex
+ *
+ *	n_tty_read()/consumer path:
+ *		caller holds non-exclusive termios_rwsem
+ *		read_tail published
+ */
+
+static int canon_copy_from_read_buf(struct tty_struct *tty,
+				    unsigned char __user **b,
+				    size_t *nr)
+{
+	struct n_tty_data *ldata = tty->disc_data;
+	size_t n, size, more, c;
+	size_t eol;
+	size_t tail;
+	int ret, found = 0;
+	bool eof_push = 0;
+
+	/* N.B. avoid overrun if nr == 0 */
+	n = min(*nr, read_cnt(ldata));
+	if (!n)
+		return 0;
+
+	tail = ldata->read_tail & (N_TTY_BUF_SIZE - 1);
+	size = min_t(size_t, tail + n, N_TTY_BUF_SIZE);
+
+	n_tty_trace("%s: nr:%zu tail:%zu n:%zu size:%zu\n",
+		    __func__, *nr, tail, n, size);
+
+	eol = find_next_bit(ldata->read_flags, size, tail);
+	more = n - (size - tail);
+	if (eol == N_TTY_BUF_SIZE && more) {
+		/* scan wrapped without finding set bit */
+		eol = find_next_bit(ldata->read_flags, more, 0);
+		if (eol != more)
+			found = 1;
+	} else if (eol != size)
+		found = 1;
+
+	size = N_TTY_BUF_SIZE - tail;
+	n = (found + eol + size) & (N_TTY_BUF_SIZE - 1);
+	c = n;
+
+	if (found && read_buf(ldata, eol) == __DISABLED_CHAR) {
+		n--;
+		eof_push = !n && ldata->read_tail != ldata->line_start;
+	}
+
+	n_tty_trace("%s: eol:%zu found:%d n:%zu c:%zu size:%zu more:%zu\n",
+		    __func__, eol, found, n, c, size, more);
+
+	if (n > size) {
+		ret = copy_to_user(*b, read_buf_addr(ldata, tail), size);
+		if (ret)
+			return -EFAULT;
+		ret = copy_to_user(*b + size, ldata->read_buf, n - size);
+	} else
+		ret = copy_to_user(*b, read_buf_addr(ldata, tail), n);
+
+	if (ret)
+		return -EFAULT;
+	*b += n;
+	*nr -= n;
+
+	if (found)
+		clear_bit(eol, ldata->read_flags);
+	smp_mb__after_clear_bit();
+	ldata->read_tail += c;
+
+	if (found) {
+		ldata->line_start = ldata->read_tail;
+		tty_audit_push(tty);
+	}
+	return eof_push ? -EAGAIN : 0;
+}
+
 extern ssize_t redirected_tty_write(struct file *, const char __user *,
 extern ssize_t redirected_tty_write(struct file *, const char __user *,
 							size_t, loff_t *);
 							size_t, loff_t *);
 
 
@@ -1787,6 +2099,10 @@ static int job_control(struct tty_struct *tty, struct file *file)
  *	a hangup. Always called in user context, may sleep.
  *	a hangup. Always called in user context, may sleep.
  *
  *
  *	This code must be sure never to sleep through a hangup.
  *	This code must be sure never to sleep through a hangup.
+ *
+ *	n_tty_read()/consumer path:
+ *		claims non-exclusive termios_rwsem
+ *		publishes read_tail
  */
  */
 
 
 static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
 static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
@@ -1798,16 +2114,27 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
 	int c;
 	int c;
 	int minimum, time;
 	int minimum, time;
 	ssize_t retval = 0;
 	ssize_t retval = 0;
-	ssize_t size;
 	long timeout;
 	long timeout;
 	unsigned long flags;
 	unsigned long flags;
 	int packet;
 	int packet;
 
 
-do_it_again:
 	c = job_control(tty, file);
 	c = job_control(tty, file);
 	if (c < 0)
 	if (c < 0)
 		return c;
 		return c;
 
 
+	/*
+	 *	Internal serialization of reads.
+	 */
+	if (file->f_flags & O_NONBLOCK) {
+		if (!mutex_trylock(&ldata->atomic_read_lock))
+			return -EAGAIN;
+	} else {
+		if (mutex_lock_interruptible(&ldata->atomic_read_lock))
+			return -ERESTARTSYS;
+	}
+
+	down_read(&tty->termios_rwsem);
+
 	minimum = time = 0;
 	minimum = time = 0;
 	timeout = MAX_SCHEDULE_TIMEOUT;
 	timeout = MAX_SCHEDULE_TIMEOUT;
 	if (!ldata->icanon) {
 	if (!ldata->icanon) {
@@ -1825,16 +2152,6 @@ do_it_again:
 		}
 		}
 	}
 	}
 
 
-	/*
-	 *	Internal serialization of reads.
-	 */
-	if (file->f_flags & O_NONBLOCK) {
-		if (!mutex_trylock(&ldata->atomic_read_lock))
-			return -EAGAIN;
-	} else {
-		if (mutex_lock_interruptible(&ldata->atomic_read_lock))
-			return -ERESTARTSYS;
-	}
 	packet = tty->packet;
 	packet = tty->packet;
 
 
 	add_wait_queue(&tty->read_wait, &wait);
 	add_wait_queue(&tty->read_wait, &wait);
@@ -1883,7 +2200,11 @@ do_it_again:
 				break;
 				break;
 			}
 			}
 			n_tty_set_room(tty);
 			n_tty_set_room(tty);
+			up_read(&tty->termios_rwsem);
+
 			timeout = schedule_timeout(timeout);
 			timeout = schedule_timeout(timeout);
+
+			down_read(&tty->termios_rwsem);
 			continue;
 			continue;
 		}
 		}
 		__set_current_state(TASK_RUNNING);
 		__set_current_state(TASK_RUNNING);
@@ -1899,45 +2220,11 @@ do_it_again:
 		}
 		}
 
 
 		if (ldata->icanon && !L_EXTPROC(tty)) {
 		if (ldata->icanon && !L_EXTPROC(tty)) {
-			/* N.B. avoid overrun if nr == 0 */
-			raw_spin_lock_irqsave(&ldata->read_lock, flags);
-			while (nr && ldata->read_cnt) {
-				int eol;
-
-				eol = test_and_clear_bit(ldata->read_tail,
-						ldata->read_flags);
-				c = ldata->read_buf[ldata->read_tail];
-				ldata->read_tail = ((ldata->read_tail+1) &
-						  (N_TTY_BUF_SIZE-1));
-				ldata->read_cnt--;
-				if (eol) {
-					/* this test should be redundant:
-					 * we shouldn't be reading data if
-					 * canon_data is 0
-					 */
-					if (--ldata->canon_data < 0)
-						ldata->canon_data = 0;
-				}
-				raw_spin_unlock_irqrestore(&ldata->read_lock, flags);
-
-				if (!eol || (c != __DISABLED_CHAR)) {
-					if (tty_put_user(tty, c, b++)) {
-						retval = -EFAULT;
-						b--;
-						raw_spin_lock_irqsave(&ldata->read_lock, flags);
-						break;
-					}
-					nr--;
-				}
-				if (eol) {
-					tty_audit_push(tty);
-					raw_spin_lock_irqsave(&ldata->read_lock, flags);
-					break;
-				}
-				raw_spin_lock_irqsave(&ldata->read_lock, flags);
-			}
-			raw_spin_unlock_irqrestore(&ldata->read_lock, flags);
-			if (retval)
+			retval = canon_copy_from_read_buf(tty, &b, &nr);
+			if (retval == -EAGAIN) {
+				retval = 0;
+				continue;
+			} else if (retval)
 				break;
 				break;
 		} else {
 		} else {
 			int uncopied;
 			int uncopied;
@@ -1951,24 +2238,7 @@ do_it_again:
 			}
 			}
 		}
 		}
 
 
-		/* If there is enough space in the read buffer now, let the
-		 * low-level driver know. We use n_tty_chars_in_buffer() to
-		 * check the buffer, as it now knows about canonical mode.
-		 * Otherwise, if the driver is throttled and the line is
-		 * longer than TTY_THRESHOLD_UNTHROTTLE in canonical mode,
-		 * we won't get any more characters.
-		 */
-		while (1) {
-			tty_set_flow_change(tty, TTY_UNTHROTTLE_SAFE);
-			if (n_tty_chars_in_buffer(tty) > TTY_THRESHOLD_UNTHROTTLE)
-				break;
-			if (!tty->count)
-				break;
-			n_tty_set_room(tty);
-			if (!tty_unthrottle_safe(tty))
-				break;
-		}
-		__tty_set_flow_change(tty, 0);
+		n_tty_check_unthrottle(tty);
 
 
 		if (b - buf >= minimum)
 		if (b - buf >= minimum)
 			break;
 			break;
@@ -1982,15 +2252,11 @@ do_it_again:
 		ldata->minimum_to_wake = minimum;
 		ldata->minimum_to_wake = minimum;
 
 
 	__set_current_state(TASK_RUNNING);
 	__set_current_state(TASK_RUNNING);
-	size = b - buf;
-	if (size) {
-		retval = size;
-		if (nr)
-			clear_bit(TTY_PUSH, &tty->flags);
-	} else if (test_and_clear_bit(TTY_PUSH, &tty->flags))
-		goto do_it_again;
+	if (b - buf)
+		retval = b - buf;
 
 
 	n_tty_set_room(tty);
 	n_tty_set_room(tty);
+	up_read(&tty->termios_rwsem);
 	return retval;
 	return retval;
 }
 }
 
 
@@ -2031,6 +2297,8 @@ static ssize_t n_tty_write(struct tty_struct *tty, struct file *file,
 			return retval;
 			return retval;
 	}
 	}
 
 
+	down_read(&tty->termios_rwsem);
+
 	/* Write out any echoed characters that are still pending */
 	/* Write out any echoed characters that are still pending */
 	process_echoes(tty);
 	process_echoes(tty);
 
 
@@ -2084,13 +2352,18 @@ static ssize_t n_tty_write(struct tty_struct *tty, struct file *file,
 			retval = -EAGAIN;
 			retval = -EAGAIN;
 			break;
 			break;
 		}
 		}
+		up_read(&tty->termios_rwsem);
+
 		schedule();
 		schedule();
+
+		down_read(&tty->termios_rwsem);
 	}
 	}
 break_out:
 break_out:
 	__set_current_state(TASK_RUNNING);
 	__set_current_state(TASK_RUNNING);
 	remove_wait_queue(&tty->write_wait, &wait);
 	remove_wait_queue(&tty->write_wait, &wait);
 	if (b - buf != nr && tty->fasync)
 	if (b - buf != nr && tty->fasync)
 		set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
 		set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
+	up_read(&tty->termios_rwsem);
 	return (b - buf) ? b - buf : retval;
 	return (b - buf) ? b - buf : retval;
 }
 }
 
 
@@ -2139,19 +2412,19 @@ static unsigned int n_tty_poll(struct tty_struct *tty, struct file *file,
 
 
 static unsigned long inq_canon(struct n_tty_data *ldata)
 static unsigned long inq_canon(struct n_tty_data *ldata)
 {
 {
-	int nr, head, tail;
+	size_t nr, head, tail;
 
 
-	if (!ldata->canon_data)
+	if (ldata->canon_head == ldata->read_tail)
 		return 0;
 		return 0;
 	head = ldata->canon_head;
 	head = ldata->canon_head;
 	tail = ldata->read_tail;
 	tail = ldata->read_tail;
-	nr = (head - tail) & (N_TTY_BUF_SIZE-1);
+	nr = head - tail;
 	/* Skip EOF-chars.. */
 	/* Skip EOF-chars.. */
 	while (head != tail) {
 	while (head != tail) {
-		if (test_bit(tail, ldata->read_flags) &&
-		    ldata->read_buf[tail] == __DISABLED_CHAR)
+		if (test_bit(tail & (N_TTY_BUF_SIZE - 1), ldata->read_flags) &&
+		    read_buf(ldata, tail) == __DISABLED_CHAR)
 			nr--;
 			nr--;
-		tail = (tail+1) & (N_TTY_BUF_SIZE-1);
+		tail++;
 	}
 	}
 	return nr;
 	return nr;
 }
 }
@@ -2166,10 +2439,12 @@ static int n_tty_ioctl(struct tty_struct *tty, struct file *file,
 	case TIOCOUTQ:
 	case TIOCOUTQ:
 		return put_user(tty_chars_in_buffer(tty), (int __user *) arg);
 		return put_user(tty_chars_in_buffer(tty), (int __user *) arg);
 	case TIOCINQ:
 	case TIOCINQ:
-		/* FIXME: Locking */
-		retval = ldata->read_cnt;
+		down_write(&tty->termios_rwsem);
 		if (L_ICANON(tty))
 		if (L_ICANON(tty))
 			retval = inq_canon(ldata);
 			retval = inq_canon(ldata);
+		else
+			retval = read_cnt(ldata);
+		up_write(&tty->termios_rwsem);
 		return put_user(retval, (unsigned int __user *) arg);
 		return put_user(retval, (unsigned int __user *) arg);
 	default:
 	default:
 		return n_tty_ioctl_helper(tty, file, cmd, arg);
 		return n_tty_ioctl_helper(tty, file, cmd, arg);
@@ -2203,6 +2478,7 @@ struct tty_ldisc_ops tty_ldisc_N_TTY = {
 	.receive_buf     = n_tty_receive_buf,
 	.receive_buf     = n_tty_receive_buf,
 	.write_wakeup    = n_tty_write_wakeup,
 	.write_wakeup    = n_tty_write_wakeup,
 	.fasync		 = n_tty_fasync,
 	.fasync		 = n_tty_fasync,
+	.receive_buf2	 = n_tty_receive_buf2,
 };
 };
 
 
 /**
 /**

+ 6 - 12
drivers/tty/pty.c

@@ -89,17 +89,13 @@ static void pty_unthrottle(struct tty_struct *tty)
  *	pty_space	-	report space left for writing
  *	pty_space	-	report space left for writing
  *	@to: tty we are writing into
  *	@to: tty we are writing into
  *
  *
- *	The tty buffers allow 64K but we sneak a peak and clip at 8K this
- *	allows a lot of overspill room for echo and other fun messes to
- *	be handled properly
+ *	Limit the buffer space used by ptys to 8k.
  */
  */
 
 
 static int pty_space(struct tty_struct *to)
 static int pty_space(struct tty_struct *to)
 {
 {
-	int n = 8192 - to->port->buf.memory_used;
-	if (n < 0)
-		return 0;
-	return n;
+	int n = tty_buffer_space_avail(to->port);
+	return min(n, 8192);
 }
 }
 
 
 /**
 /**
@@ -125,10 +121,8 @@ static int pty_write(struct tty_struct *tty, const unsigned char *buf, int c)
 		/* Stuff the data into the input queue of the other end */
 		/* Stuff the data into the input queue of the other end */
 		c = tty_insert_flip_string(to->port, buf, c);
 		c = tty_insert_flip_string(to->port, buf, c);
 		/* And shovel */
 		/* And shovel */
-		if (c) {
+		if (c)
 			tty_flip_buffer_push(to->port);
 			tty_flip_buffer_push(to->port);
-			tty_wakeup(tty);
-		}
 	}
 	}
 	return c;
 	return c;
 }
 }
@@ -287,7 +281,7 @@ static int pty_resize(struct tty_struct *tty,  struct winsize *ws)
 	struct tty_struct *pty = tty->link;
 	struct tty_struct *pty = tty->link;
 
 
 	/* For a PTY we need to lock the tty side */
 	/* For a PTY we need to lock the tty side */
-	mutex_lock(&tty->termios_mutex);
+	mutex_lock(&tty->winsize_mutex);
 	if (!memcmp(ws, &tty->winsize, sizeof(*ws)))
 	if (!memcmp(ws, &tty->winsize, sizeof(*ws)))
 		goto done;
 		goto done;
 
 
@@ -314,7 +308,7 @@ static int pty_resize(struct tty_struct *tty,  struct winsize *ws)
 	tty->winsize = *ws;
 	tty->winsize = *ws;
 	pty->winsize = *ws;	/* Never used so will go away soon */
 	pty->winsize = *ws;	/* Never used so will go away soon */
 done:
 done:
-	mutex_unlock(&tty->termios_mutex);
+	mutex_unlock(&tty->winsize_mutex);
 	return 0;
 	return 0;
 }
 }
 
 

+ 1 - 1
drivers/tty/serial/8250/8250_core.c

@@ -3062,7 +3062,7 @@ void serial8250_resume_port(int line)
  */
  */
 static int serial8250_probe(struct platform_device *dev)
 static int serial8250_probe(struct platform_device *dev)
 {
 {
-	struct plat_serial8250_port *p = dev->dev.platform_data;
+	struct plat_serial8250_port *p = dev_get_platdata(&dev->dev);
 	struct uart_8250_port uart;
 	struct uart_8250_port uart;
 	int ret, i, irqflag = 0;
 	int ret, i, irqflag = 0;
 
 

+ 26 - 8
drivers/tty/serial/8250/8250_dw.c

@@ -57,11 +57,25 @@
 
 
 struct dw8250_data {
 struct dw8250_data {
 	int		last_lcr;
 	int		last_lcr;
+	int		last_mcr;
 	int		line;
 	int		line;
 	struct clk	*clk;
 	struct clk	*clk;
 	u8		usr_reg;
 	u8		usr_reg;
 };
 };
 
 
+static inline int dw8250_modify_msr(struct uart_port *p, int offset, int value)
+{
+	struct dw8250_data *d = p->private_data;
+
+	/* If reading MSR, report CTS asserted when auto-CTS/RTS enabled */
+	if (offset == UART_MSR && d->last_mcr & UART_MCR_AFE) {
+		value |= UART_MSR_CTS;
+		value &= ~UART_MSR_DCTS;
+	}
+
+	return value;
+}
+
 static void dw8250_serial_out(struct uart_port *p, int offset, int value)
 static void dw8250_serial_out(struct uart_port *p, int offset, int value)
 {
 {
 	struct dw8250_data *d = p->private_data;
 	struct dw8250_data *d = p->private_data;
@@ -69,15 +83,17 @@ static void dw8250_serial_out(struct uart_port *p, int offset, int value)
 	if (offset == UART_LCR)
 	if (offset == UART_LCR)
 		d->last_lcr = value;
 		d->last_lcr = value;
 
 
-	offset <<= p->regshift;
-	writeb(value, p->membase + offset);
+	if (offset == UART_MCR)
+		d->last_mcr = value;
+
+	writeb(value, p->membase + (offset << p->regshift));
 }
 }
 
 
 static unsigned int dw8250_serial_in(struct uart_port *p, int offset)
 static unsigned int dw8250_serial_in(struct uart_port *p, int offset)
 {
 {
-	offset <<= p->regshift;
+	unsigned int value = readb(p->membase + (offset << p->regshift));
 
 
-	return readb(p->membase + offset);
+	return dw8250_modify_msr(p, offset, value);
 }
 }
 
 
 /* Read Back (rb) version to ensure register access ording. */
 /* Read Back (rb) version to ensure register access ording. */
@@ -94,15 +110,17 @@ static void dw8250_serial_out32(struct uart_port *p, int offset, int value)
 	if (offset == UART_LCR)
 	if (offset == UART_LCR)
 		d->last_lcr = value;
 		d->last_lcr = value;
 
 
-	offset <<= p->regshift;
-	writel(value, p->membase + offset);
+	if (offset == UART_MCR)
+		d->last_mcr = value;
+
+	writel(value, p->membase + (offset << p->regshift));
 }
 }
 
 
 static unsigned int dw8250_serial_in32(struct uart_port *p, int offset)
 static unsigned int dw8250_serial_in32(struct uart_port *p, int offset)
 {
 {
-	offset <<= p->regshift;
+	unsigned int value = readl(p->membase + (offset << p->regshift));
 
 
-	return readl(p->membase + offset);
+	return dw8250_modify_msr(p, offset, value);
 }
 }
 
 
 static int dw8250_handle_irq(struct uart_port *p)
 static int dw8250_handle_irq(struct uart_port *p)

+ 1 - 1
drivers/tty/serial/8250/8250_early.c

@@ -194,7 +194,7 @@ static int __init parse_options(struct early_serial8250_device *device,
 		options++;
 		options++;
 		device->baud = simple_strtoul(options, NULL, 0);
 		device->baud = simple_strtoul(options, NULL, 0);
 		length = min(strcspn(options, " ") + 1,
 		length = min(strcspn(options, " ") + 1,
-			     sizeof(device->options));
+			     (size_t)(sizeof(device->options)));
 		strlcpy(device->options, options, length);
 		strlcpy(device->options, options, length);
 	} else {
 	} else {
 		device->baud = probe_baud(port);
 		device->baud = probe_baud(port);

+ 8 - 19
drivers/tty/serial/8250/8250_em.c

@@ -95,25 +95,23 @@ static int serial8250_em_probe(struct platform_device *pdev)
 	struct resource *irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
 	struct resource *irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
 	struct serial8250_em_priv *priv;
 	struct serial8250_em_priv *priv;
 	struct uart_8250_port up;
 	struct uart_8250_port up;
-	int ret = -EINVAL;
+	int ret;
 
 
 	if (!regs || !irq) {
 	if (!regs || !irq) {
 		dev_err(&pdev->dev, "missing registers or irq\n");
 		dev_err(&pdev->dev, "missing registers or irq\n");
-		goto err0;
+		return -EINVAL;
 	}
 	}
 
 
-	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
 	if (!priv) {
 	if (!priv) {
 		dev_err(&pdev->dev, "unable to allocate private data\n");
 		dev_err(&pdev->dev, "unable to allocate private data\n");
-		ret = -ENOMEM;
-		goto err0;
+		return -ENOMEM;
 	}
 	}
 
 
-	priv->sclk = clk_get(&pdev->dev, "sclk");
+	priv->sclk = devm_clk_get(&pdev->dev, "sclk");
 	if (IS_ERR(priv->sclk)) {
 	if (IS_ERR(priv->sclk)) {
 		dev_err(&pdev->dev, "unable to get clock\n");
 		dev_err(&pdev->dev, "unable to get clock\n");
-		ret = PTR_ERR(priv->sclk);
-		goto err1;
+		return PTR_ERR(priv->sclk);
 	}
 	}
 
 
 	memset(&up, 0, sizeof(up));
 	memset(&up, 0, sizeof(up));
@@ -136,20 +134,13 @@ static int serial8250_em_probe(struct platform_device *pdev)
 	ret = serial8250_register_8250_port(&up);
 	ret = serial8250_register_8250_port(&up);
 	if (ret < 0) {
 	if (ret < 0) {
 		dev_err(&pdev->dev, "unable to register 8250 port\n");
 		dev_err(&pdev->dev, "unable to register 8250 port\n");
-		goto err2;
+		clk_disable(priv->sclk);
+		return ret;
 	}
 	}
 
 
 	priv->line = ret;
 	priv->line = ret;
 	platform_set_drvdata(pdev, priv);
 	platform_set_drvdata(pdev, priv);
 	return 0;
 	return 0;
-
- err2:
-	clk_disable(priv->sclk);
-	clk_put(priv->sclk);
- err1:
-	kfree(priv);
- err0:
-	return ret;
 }
 }
 
 
 static int serial8250_em_remove(struct platform_device *pdev)
 static int serial8250_em_remove(struct platform_device *pdev)
@@ -158,8 +149,6 @@ static int serial8250_em_remove(struct platform_device *pdev)
 
 
 	serial8250_unregister_port(priv->line);
 	serial8250_unregister_port(priv->line);
 	clk_disable(priv->sclk);
 	clk_disable(priv->sclk);
-	clk_put(priv->sclk);
-	kfree(priv);
 	return 0;
 	return 0;
 }
 }
 
 

+ 11 - 4
drivers/tty/serial/8250/8250_pci.c

@@ -1565,6 +1565,7 @@ pci_wch_ch353_setup(struct serial_private *priv,
 #define PCI_DEVICE_ID_COMMTECH_4228PCIE	0x0021
 #define PCI_DEVICE_ID_COMMTECH_4228PCIE	0x0021
 #define PCI_DEVICE_ID_COMMTECH_4222PCIE	0x0022
 #define PCI_DEVICE_ID_COMMTECH_4222PCIE	0x0022
 #define PCI_DEVICE_ID_BROADCOM_TRUMANAGE 0x160a
 #define PCI_DEVICE_ID_BROADCOM_TRUMANAGE 0x160a
+#define PCI_DEVICE_ID_AMCC_ADDIDATA_APCI7800 0x818e
 
 
 #define PCI_VENDOR_ID_SUNIX		0x1fd4
 #define PCI_VENDOR_ID_SUNIX		0x1fd4
 #define PCI_DEVICE_ID_SUNIX_1999	0x1999
 #define PCI_DEVICE_ID_SUNIX_1999	0x1999
@@ -1587,8 +1588,8 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
 	* ADDI-DATA GmbH communication cards <info@addi-data.com>
 	* ADDI-DATA GmbH communication cards <info@addi-data.com>
 	*/
 	*/
 	{
 	{
-		.vendor         = PCI_VENDOR_ID_ADDIDATA_OLD,
-		.device         = PCI_DEVICE_ID_ADDIDATA_APCI7800,
+		.vendor         = PCI_VENDOR_ID_AMCC,
+		.device         = PCI_DEVICE_ID_AMCC_ADDIDATA_APCI7800,
 		.subvendor      = PCI_ANY_ID,
 		.subvendor      = PCI_ANY_ID,
 		.subdevice      = PCI_ANY_ID,
 		.subdevice      = PCI_ANY_ID,
 		.setup          = addidata_apci7800_setup,
 		.setup          = addidata_apci7800_setup,
@@ -4697,8 +4698,8 @@ static struct pci_device_id serial_pci_tbl[] = {
 		0,
 		0,
 		pbn_b0_1_115200 },
 		pbn_b0_1_115200 },
 
 
-	{	PCI_VENDOR_ID_ADDIDATA_OLD,
-		PCI_DEVICE_ID_ADDIDATA_APCI7800,
+	{	PCI_VENDOR_ID_AMCC,
+		PCI_DEVICE_ID_AMCC_ADDIDATA_APCI7800,
 		PCI_ANY_ID,
 		PCI_ANY_ID,
 		PCI_ANY_ID,
 		PCI_ANY_ID,
 		0,
 		0,
@@ -4797,6 +4798,12 @@ static struct pci_device_id serial_pci_tbl[] = {
 		PCI_VENDOR_ID_IBM, 0x0299,
 		PCI_VENDOR_ID_IBM, 0x0299,
 		0, 0, pbn_b0_bt_2_115200 },
 		0, 0, pbn_b0_bt_2_115200 },
 
 
+	/*
+	 * other NetMos 9835 devices are most likely handled by the
+	 * parport_serial driver, check drivers/parport/parport_serial.c
+	 * before adding them here.
+	 */
+
 	{	PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9901,
 	{	PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9901,
 		0xA000, 0x1000,
 		0xA000, 0x1000,
 		0, 0, pbn_b0_1_115200 },
 		0, 0, pbn_b0_1_115200 },

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

@@ -116,6 +116,8 @@ config SERIAL_8250_PCI
 	  This builds standard PCI serial support. You may be able to
 	  This builds standard PCI serial support. You may be able to
 	  disable this feature if you only need legacy serial support.
 	  disable this feature if you only need legacy serial support.
 	  Saves about 9K.
 	  Saves about 9K.
+	  Note that serial ports on NetMos 9835 Multi-I/O cards are handled
+	  by the parport_serial driver, enabled with CONFIG_PARPORT_SERIAL.
 
 
 config SERIAL_8250_HP300
 config SERIAL_8250_HP300
 	tristate
 	tristate

+ 26 - 7
drivers/tty/serial/Kconfig

@@ -291,13 +291,13 @@ config SERIAL_MAX3100
 
 
 config SERIAL_MAX310X
 config SERIAL_MAX310X
 	bool "MAX310X support"
 	bool "MAX310X support"
-	depends on SPI
+	depends on SPI_MASTER
 	select SERIAL_CORE
 	select SERIAL_CORE
-	select REGMAP_SPI if SPI
+	select REGMAP_SPI if SPI_MASTER
 	default n
 	default n
 	help
 	help
 	  This selects support for an advanced UART from Maxim (Dallas).
 	  This selects support for an advanced UART from Maxim (Dallas).
-	  Supported ICs are MAX3107, MAX3108.
+	  Supported ICs are MAX3107, MAX3108, MAX3109, MAX14830.
 	  Each IC contains 128 words each of receive and transmit FIFO
 	  Each IC contains 128 words each of receive and transmit FIFO
 	  that can be controlled through I2C or high-speed SPI.
 	  that can be controlled through I2C or high-speed SPI.
 
 
@@ -1401,13 +1401,16 @@ config SERIAL_XILINX_PS_UART_CONSOLE
 	  Enable a Xilinx PS UART port to be the system console.
 	  Enable a Xilinx PS UART port to be the system console.
 
 
 config SERIAL_AR933X
 config SERIAL_AR933X
-	bool "AR933X serial port support"
-	depends on SOC_AR933X
+	tristate "AR933X serial port support"
+	depends on HAVE_CLK && SOC_AR933X
 	select SERIAL_CORE
 	select SERIAL_CORE
 	help
 	help
 	  If you have an Atheros AR933X SOC based board and want to use the
 	  If you have an Atheros AR933X SOC based board and want to use the
 	  built-in UART of the SoC, say Y to this option.
 	  built-in UART of the SoC, say Y to this option.
 
 
+	  To compile this driver as a module, choose M here: the
+	  module will be called ar933x_uart.
+
 config SERIAL_AR933X_CONSOLE
 config SERIAL_AR933X_CONSOLE
 	bool "Console on AR933X serial port"
 	bool "Console on AR933X serial port"
 	depends on SERIAL_AR933X=y
 	depends on SERIAL_AR933X=y
@@ -1424,8 +1427,8 @@ config SERIAL_AR933X_NR_UARTS
 	  to support.
 	  to support.
 
 
 config SERIAL_EFM32_UART
 config SERIAL_EFM32_UART
-	tristate "EFM32 UART/USART port."
-	depends on ARCH_EFM32
+	tristate "EFM32 UART/USART port"
+	depends on ARM && (ARCH_EFM32 || COMPILE_TEST)
 	select SERIAL_CORE
 	select SERIAL_CORE
 	help
 	help
 	  This driver support the USART and UART ports on
 	  This driver support the USART and UART ports on
@@ -1497,6 +1500,22 @@ 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_ST_ASC
+	tristate "ST ASC serial port support"
+	select SERIAL_CORE
+	help
+	  This driver is for the on-chip Asychronous Serial Controller on
+	  STMicroelectronics STi SoCs.
+	  ASC is embedded in ST COMMS IP block. It supports Rx & Tx functionality.
+	  It support all industry standard baud rates.
+
+	  If unsure, say N.
+
+config SERIAL_ST_ASC_CONSOLE
+	bool "Support for console on ST ASC"
+	depends on SERIAL_ST_ASC=y
+	select SERIAL_CORE_CONSOLE
+
 endmenu
 endmenu
 
 
 endif # TTY
 endif # TTY

+ 1 - 0
drivers/tty/serial/Makefile

@@ -65,6 +65,7 @@ obj-$(CONFIG_SERIAL_KGDB_NMI) += kgdb_nmi.o
 obj-$(CONFIG_SERIAL_KS8695) += serial_ks8695.o
 obj-$(CONFIG_SERIAL_KS8695) += serial_ks8695.o
 obj-$(CONFIG_SERIAL_OMAP) += omap-serial.o
 obj-$(CONFIG_SERIAL_OMAP) += omap-serial.o
 obj-$(CONFIG_SERIAL_ALTERA_UART) += altera_uart.o
 obj-$(CONFIG_SERIAL_ALTERA_UART) += altera_uart.o
+obj-$(CONFIG_SERIAL_ST_ASC) += st-asc.o
 obj-$(CONFIG_KGDB_SERIAL_CONSOLE) += kgdboc.o
 obj-$(CONFIG_KGDB_SERIAL_CONSOLE) += kgdboc.o
 obj-$(CONFIG_SERIAL_QE) += ucc_uart.o
 obj-$(CONFIG_SERIAL_QE) += ucc_uart.o
 obj-$(CONFIG_SERIAL_TIMBERDALE)	+= timbuart.o
 obj-$(CONFIG_SERIAL_TIMBERDALE)	+= timbuart.o

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

@@ -139,7 +139,9 @@ static void altera_jtaguart_rx_chars(struct altera_jtaguart *pp)
 		uart_insert_char(port, 0, 0, ch, flag);
 		uart_insert_char(port, 0, 0, ch, flag);
 	}
 	}
 
 
+	spin_unlock(&port->lock);
 	tty_flip_buffer_push(&port->state->port);
 	tty_flip_buffer_push(&port->state->port);
+	spin_lock(&port->lock);
 }
 }
 
 
 static void altera_jtaguart_tx_chars(struct altera_jtaguart *pp)
 static void altera_jtaguart_tx_chars(struct altera_jtaguart *pp)
@@ -408,7 +410,8 @@ static struct uart_driver altera_jtaguart_driver = {
 
 
 static int altera_jtaguart_probe(struct platform_device *pdev)
 static int altera_jtaguart_probe(struct platform_device *pdev)
 {
 {
-	struct altera_jtaguart_platform_uart *platp = pdev->dev.platform_data;
+	struct altera_jtaguart_platform_uart *platp =
+			dev_get_platdata(&pdev->dev);
 	struct uart_port *port;
 	struct uart_port *port;
 	struct resource *res_irq, *res_mem;
 	struct resource *res_irq, *res_mem;
 	int i = pdev->id;
 	int i = pdev->id;

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

@@ -231,7 +231,9 @@ static void altera_uart_rx_chars(struct altera_uart *pp)
 				 flag);
 				 flag);
 	}
 	}
 
 
+	spin_unlock(&port->lock);
 	tty_flip_buffer_push(&port->state->port);
 	tty_flip_buffer_push(&port->state->port);
+	spin_lock(&port->lock);
 }
 }
 
 
 static void altera_uart_tx_chars(struct altera_uart *pp)
 static void altera_uart_tx_chars(struct altera_uart *pp)
@@ -534,7 +536,7 @@ static int altera_uart_get_of_uartclk(struct platform_device *pdev,
 
 
 static int altera_uart_probe(struct platform_device *pdev)
 static int altera_uart_probe(struct platform_device *pdev)
 {
 {
-	struct altera_uart_platform_uart *platp = pdev->dev.platform_data;
+	struct altera_uart_platform_uart *platp = dev_get_platdata(&pdev->dev);
 	struct uart_port *port;
 	struct uart_port *port;
 	struct resource *res_mem;
 	struct resource *res_mem;
 	struct resource *res_irq;
 	struct resource *res_irq;

+ 1 - 1
drivers/tty/serial/amba-pl010.c

@@ -721,7 +721,7 @@ static int pl010_probe(struct amba_device *dev, const struct amba_id *id)
 	uap->port.flags = UPF_BOOT_AUTOCONF;
 	uap->port.flags = UPF_BOOT_AUTOCONF;
 	uap->port.line = i;
 	uap->port.line = i;
 	uap->dev = dev;
 	uap->dev = dev;
-	uap->data = dev->dev.platform_data;
+	uap->data = dev_get_platdata(&dev->dev);
 
 
 	amba_ports[i] = uap;
 	amba_ports[i] = uap;
 
 

+ 11 - 7
drivers/tty/serial/amba-pl011.c

@@ -265,7 +265,7 @@ static void pl011_sgbuf_free(struct dma_chan *chan, struct pl011_sgbuf *sg,
 static void pl011_dma_probe_initcall(struct device *dev, struct uart_amba_port *uap)
 static void pl011_dma_probe_initcall(struct device *dev, struct uart_amba_port *uap)
 {
 {
 	/* DMA is the sole user of the platform data right now */
 	/* DMA is the sole user of the platform data right now */
-	struct amba_pl011_data *plat = uap->port.dev->platform_data;
+	struct amba_pl011_data *plat = dev_get_platdata(uap->port.dev);
 	struct dma_slave_config tx_conf = {
 	struct dma_slave_config tx_conf = {
 		.dst_addr = uap->port.mapbase + UART01x_DR,
 		.dst_addr = uap->port.mapbase + UART01x_DR,
 		.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE,
 		.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE,
@@ -677,6 +677,8 @@ static inline bool pl011_dma_tx_start(struct uart_amba_port *uap)
  * Locking: called with port lock held and IRQs disabled.
  * Locking: called with port lock held and IRQs disabled.
  */
  */
 static void pl011_dma_flush_buffer(struct uart_port *port)
 static void pl011_dma_flush_buffer(struct uart_port *port)
+__releases(&uap->port.lock)
+__acquires(&uap->port.lock)
 {
 {
 	struct uart_amba_port *uap = (struct uart_amba_port *)port;
 	struct uart_amba_port *uap = (struct uart_amba_port *)port;
 
 
@@ -1198,6 +1200,8 @@ static void pl011_enable_ms(struct uart_port *port)
 }
 }
 
 
 static void pl011_rx_chars(struct uart_amba_port *uap)
 static void pl011_rx_chars(struct uart_amba_port *uap)
+__releases(&uap->port.lock)
+__acquires(&uap->port.lock)
 {
 {
 	pl011_fifo_to_tty(uap);
 	pl011_fifo_to_tty(uap);
 
 
@@ -1497,10 +1501,10 @@ static int pl011_hwinit(struct uart_port *port)
 	uap->im = readw(uap->port.membase + UART011_IMSC);
 	uap->im = readw(uap->port.membase + UART011_IMSC);
 	writew(UART011_RTIM | UART011_RXIM, uap->port.membase + UART011_IMSC);
 	writew(UART011_RTIM | UART011_RXIM, uap->port.membase + UART011_IMSC);
 
 
-	if (uap->port.dev->platform_data) {
+	if (dev_get_platdata(uap->port.dev)) {
 		struct amba_pl011_data *plat;
 		struct amba_pl011_data *plat;
 
 
-		plat = uap->port.dev->platform_data;
+		plat = dev_get_platdata(uap->port.dev);
 		if (plat->init)
 		if (plat->init)
 			plat->init();
 			plat->init();
 	}
 	}
@@ -1645,10 +1649,10 @@ static void pl011_shutdown(struct uart_port *port)
 	/* Optionally let pins go into sleep states */
 	/* Optionally let pins go into sleep states */
 	pinctrl_pm_select_sleep_state(port->dev);
 	pinctrl_pm_select_sleep_state(port->dev);
 
 
-	if (uap->port.dev->platform_data) {
+	if (dev_get_platdata(uap->port.dev)) {
 		struct amba_pl011_data *plat;
 		struct amba_pl011_data *plat;
 
 
-		plat = uap->port.dev->platform_data;
+		plat = dev_get_platdata(uap->port.dev);
 		if (plat->exit)
 		if (plat->exit)
 			plat->exit();
 			plat->exit();
 	}
 	}
@@ -2002,10 +2006,10 @@ static int __init pl011_console_setup(struct console *co, char *options)
 	if (ret)
 	if (ret)
 		return ret;
 		return ret;
 
 
-	if (uap->port.dev->platform_data) {
+	if (dev_get_platdata(uap->port.dev)) {
 		struct amba_pl011_data *plat;
 		struct amba_pl011_data *plat;
 
 
-		plat = uap->port.dev->platform_data;
+		plat = dev_get_platdata(uap->port.dev);
 		if (plat->init)
 		if (plat->init)
 			plat->init();
 			plat->init();
 	}
 	}

+ 2 - 0
drivers/tty/serial/apbuart.c

@@ -125,7 +125,9 @@ static void apbuart_rx_chars(struct uart_port *port)
 		status = UART_GET_STATUS(port);
 		status = UART_GET_STATUS(port);
 	}
 	}
 
 
+	spin_unlock(&port->lock);
 	tty_flip_buffer_push(&port->state->port);
 	tty_flip_buffer_push(&port->state->port);
+	spin_lock(&port->lock);
 }
 }
 
 
 static void apbuart_tx_chars(struct uart_port *port)
 static void apbuart_tx_chars(struct uart_port *port)

+ 67 - 44
drivers/tty/serial/ar933x_uart.c

@@ -17,6 +17,8 @@
 #include <linux/sysrq.h>
 #include <linux/sysrq.h>
 #include <linux/delay.h>
 #include <linux/delay.h>
 #include <linux/platform_device.h>
 #include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
 #include <linux/tty.h>
 #include <linux/tty.h>
 #include <linux/tty_flip.h>
 #include <linux/tty_flip.h>
 #include <linux/serial_core.h>
 #include <linux/serial_core.h>
@@ -24,11 +26,11 @@
 #include <linux/slab.h>
 #include <linux/slab.h>
 #include <linux/io.h>
 #include <linux/io.h>
 #include <linux/irq.h>
 #include <linux/irq.h>
+#include <linux/clk.h>
 
 
 #include <asm/div64.h>
 #include <asm/div64.h>
 
 
 #include <asm/mach-ath79/ar933x_uart.h>
 #include <asm/mach-ath79/ar933x_uart.h>
-#include <asm/mach-ath79/ar933x_uart_platform.h>
 
 
 #define DRIVER_NAME "ar933x-uart"
 #define DRIVER_NAME "ar933x-uart"
 
 
@@ -47,8 +49,14 @@ struct ar933x_uart_port {
 	unsigned int		ier;	/* shadow Interrupt Enable Register */
 	unsigned int		ier;	/* shadow Interrupt Enable Register */
 	unsigned int		min_baud;
 	unsigned int		min_baud;
 	unsigned int		max_baud;
 	unsigned int		max_baud;
+	struct clk		*clk;
 };
 };
 
 
+static inline bool ar933x_uart_console_enabled(void)
+{
+	return config_enabled(CONFIG_SERIAL_AR933X_CONSOLE);
+}
+
 static inline unsigned int ar933x_uart_read(struct ar933x_uart_port *up,
 static inline unsigned int ar933x_uart_read(struct ar933x_uart_port *up,
 					    int offset)
 					    int offset)
 {
 {
@@ -322,7 +330,9 @@ static void ar933x_uart_rx_chars(struct ar933x_uart_port *up)
 			tty_insert_flip_char(port, ch, TTY_NORMAL);
 			tty_insert_flip_char(port, ch, TTY_NORMAL);
 	} while (max_count-- > 0);
 	} while (max_count-- > 0);
 
 
+	spin_unlock(&up->port.lock);
 	tty_flip_buffer_push(port);
 	tty_flip_buffer_push(port);
+	spin_lock(&up->port.lock);
 }
 }
 
 
 static void ar933x_uart_tx_chars(struct ar933x_uart_port *up)
 static void ar933x_uart_tx_chars(struct ar933x_uart_port *up)
@@ -497,8 +507,6 @@ static struct uart_ops ar933x_uart_ops = {
 	.verify_port	= ar933x_uart_verify_port,
 	.verify_port	= ar933x_uart_verify_port,
 };
 };
 
 
-#ifdef CONFIG_SERIAL_AR933X_CONSOLE
-
 static struct ar933x_uart_port *
 static struct ar933x_uart_port *
 ar933x_console_ports[CONFIG_SERIAL_AR933X_NR_UARTS];
 ar933x_console_ports[CONFIG_SERIAL_AR933X_NR_UARTS];
 
 
@@ -597,80 +605,88 @@ static struct console ar933x_uart_console = {
 
 
 static void ar933x_uart_add_console_port(struct ar933x_uart_port *up)
 static void ar933x_uart_add_console_port(struct ar933x_uart_port *up)
 {
 {
+	if (!ar933x_uart_console_enabled())
+		return;
+
 	ar933x_console_ports[up->port.line] = up;
 	ar933x_console_ports[up->port.line] = up;
 }
 }
 
 
-#define AR933X_SERIAL_CONSOLE	(&ar933x_uart_console)
-
-#else
-
-static inline void ar933x_uart_add_console_port(struct ar933x_uart_port *up) {}
-
-#define AR933X_SERIAL_CONSOLE	NULL
-
-#endif /* CONFIG_SERIAL_AR933X_CONSOLE */
-
 static struct uart_driver ar933x_uart_driver = {
 static struct uart_driver ar933x_uart_driver = {
 	.owner		= THIS_MODULE,
 	.owner		= THIS_MODULE,
 	.driver_name	= DRIVER_NAME,
 	.driver_name	= DRIVER_NAME,
 	.dev_name	= "ttyATH",
 	.dev_name	= "ttyATH",
 	.nr		= CONFIG_SERIAL_AR933X_NR_UARTS,
 	.nr		= CONFIG_SERIAL_AR933X_NR_UARTS,
-	.cons		= AR933X_SERIAL_CONSOLE,
+	.cons		= NULL, /* filled in runtime */
 };
 };
 
 
 static int ar933x_uart_probe(struct platform_device *pdev)
 static int ar933x_uart_probe(struct platform_device *pdev)
 {
 {
-	struct ar933x_uart_platform_data *pdata;
 	struct ar933x_uart_port *up;
 	struct ar933x_uart_port *up;
 	struct uart_port *port;
 	struct uart_port *port;
 	struct resource *mem_res;
 	struct resource *mem_res;
 	struct resource *irq_res;
 	struct resource *irq_res;
+	struct device_node *np;
 	unsigned int baud;
 	unsigned int baud;
 	int id;
 	int id;
 	int ret;
 	int ret;
 
 
-	pdata = pdev->dev.platform_data;
-	if (!pdata)
-		return -EINVAL;
-
-	id = pdev->id;
-	if (id == -1)
-		id = 0;
+	np = pdev->dev.of_node;
+	if (config_enabled(CONFIG_OF) && np) {
+		id = of_alias_get_id(np, "serial");
+		if (id < 0) {
+			dev_err(&pdev->dev, "unable to get alias id, err=%d\n",
+				id);
+			return id;
+		}
+	} else {
+		id = pdev->id;
+		if (id == -1)
+			id = 0;
+	}
 
 
 	if (id > CONFIG_SERIAL_AR933X_NR_UARTS)
 	if (id > CONFIG_SERIAL_AR933X_NR_UARTS)
 		return -EINVAL;
 		return -EINVAL;
 
 
-	mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	if (!mem_res) {
-		dev_err(&pdev->dev, "no MEM resource\n");
-		return -EINVAL;
-	}
-
 	irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
 	irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
 	if (!irq_res) {
 	if (!irq_res) {
 		dev_err(&pdev->dev, "no IRQ resource\n");
 		dev_err(&pdev->dev, "no IRQ resource\n");
 		return -EINVAL;
 		return -EINVAL;
 	}
 	}
 
 
-	up = kzalloc(sizeof(struct ar933x_uart_port), GFP_KERNEL);
+	up = devm_kzalloc(&pdev->dev, sizeof(struct ar933x_uart_port),
+			  GFP_KERNEL);
 	if (!up)
 	if (!up)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
+	up->clk = devm_clk_get(&pdev->dev, "uart");
+	if (IS_ERR(up->clk)) {
+		dev_err(&pdev->dev, "unable to get UART clock\n");
+		return PTR_ERR(up->clk);
+	}
+
 	port = &up->port;
 	port = &up->port;
-	port->mapbase = mem_res->start;
 
 
-	port->membase = ioremap(mem_res->start, AR933X_UART_REGS_SIZE);
-	if (!port->membase) {
-		ret = -ENOMEM;
-		goto err_free_up;
+	mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	port->membase = devm_ioremap_resource(&pdev->dev, mem_res);
+	if (IS_ERR(port->membase))
+		return PTR_ERR(port->membase);
+
+	ret = clk_prepare_enable(up->clk);
+	if (ret)
+		return ret;
+
+	port->uartclk = clk_get_rate(up->clk);
+	if (!port->uartclk) {
+		ret = -EINVAL;
+		goto err_disable_clk;
 	}
 	}
 
 
+	port->mapbase = mem_res->start;
 	port->line = id;
 	port->line = id;
 	port->irq = irq_res->start;
 	port->irq = irq_res->start;
 	port->dev = &pdev->dev;
 	port->dev = &pdev->dev;
 	port->type = PORT_AR933X;
 	port->type = PORT_AR933X;
 	port->iotype = UPIO_MEM32;
 	port->iotype = UPIO_MEM32;
-	port->uartclk = pdata->uartclk;
 
 
 	port->regshift = 2;
 	port->regshift = 2;
 	port->fifosize = AR933X_UART_FIFO_SIZE;
 	port->fifosize = AR933X_UART_FIFO_SIZE;
@@ -686,15 +702,13 @@ static int ar933x_uart_probe(struct platform_device *pdev)
 
 
 	ret = uart_add_one_port(&ar933x_uart_driver, &up->port);
 	ret = uart_add_one_port(&ar933x_uart_driver, &up->port);
 	if (ret)
 	if (ret)
-		goto err_unmap;
+		goto err_disable_clk;
 
 
 	platform_set_drvdata(pdev, up);
 	platform_set_drvdata(pdev, up);
 	return 0;
 	return 0;
 
 
-err_unmap:
-	iounmap(up->port.membase);
-err_free_up:
-	kfree(up);
+err_disable_clk:
+	clk_disable_unprepare(up->clk);
 	return ret;
 	return ret;
 }
 }
 
 
@@ -703,23 +717,30 @@ static int ar933x_uart_remove(struct platform_device *pdev)
 	struct ar933x_uart_port *up;
 	struct ar933x_uart_port *up;
 
 
 	up = platform_get_drvdata(pdev);
 	up = platform_get_drvdata(pdev);
-	platform_set_drvdata(pdev, NULL);
 
 
 	if (up) {
 	if (up) {
 		uart_remove_one_port(&ar933x_uart_driver, &up->port);
 		uart_remove_one_port(&ar933x_uart_driver, &up->port);
-		iounmap(up->port.membase);
-		kfree(up);
+		clk_disable_unprepare(up->clk);
 	}
 	}
 
 
 	return 0;
 	return 0;
 }
 }
 
 
+#ifdef CONFIG_OF
+static const struct of_device_id ar933x_uart_of_ids[] = {
+	{ .compatible = "qca,ar9330-uart" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, ar933x_uart_of_ids);
+#endif
+
 static struct platform_driver ar933x_uart_platform_driver = {
 static struct platform_driver ar933x_uart_platform_driver = {
 	.probe		= ar933x_uart_probe,
 	.probe		= ar933x_uart_probe,
 	.remove		= ar933x_uart_remove,
 	.remove		= ar933x_uart_remove,
 	.driver		= {
 	.driver		= {
 		.name		= DRIVER_NAME,
 		.name		= DRIVER_NAME,
 		.owner		= THIS_MODULE,
 		.owner		= THIS_MODULE,
+		.of_match_table = of_match_ptr(ar933x_uart_of_ids),
 	},
 	},
 };
 };
 
 
@@ -727,7 +748,9 @@ static int __init ar933x_uart_init(void)
 {
 {
 	int ret;
 	int ret;
 
 
-	ar933x_uart_driver.nr = CONFIG_SERIAL_AR933X_NR_UARTS;
+	if (ar933x_uart_console_enabled())
+		ar933x_uart_driver.cons = &ar933x_uart_console;
+
 	ret = uart_register_driver(&ar933x_uart_driver);
 	ret = uart_register_driver(&ar933x_uart_driver);
 	if (ret)
 	if (ret)
 		goto err_out;
 		goto err_out;

+ 20 - 15
drivers/tty/serial/arc_uart.c

@@ -209,9 +209,9 @@ static void arc_serial_start_tx(struct uart_port *port)
 	arc_serial_tx_chars(uart);
 	arc_serial_tx_chars(uart);
 }
 }
 
 
-static void arc_serial_rx_chars(struct arc_uart_port *uart)
+static void arc_serial_rx_chars(struct arc_uart_port *uart, unsigned int status)
 {
 {
-	unsigned int status, ch, flg = 0;
+	unsigned int ch, flg = 0;
 
 
 	/*
 	/*
 	 * UART has 4 deep RX-FIFO. Driver's recongnition of this fact
 	 * UART has 4 deep RX-FIFO. Driver's recongnition of this fact
@@ -222,11 +222,11 @@ static void arc_serial_rx_chars(struct arc_uart_port *uart)
 	 * before RX-EMPTY=0, implies some sort of buffering going on in the
 	 * before RX-EMPTY=0, implies some sort of buffering going on in the
 	 * controller, which is indeed the Rx-FIFO.
 	 * controller, which is indeed the Rx-FIFO.
 	 */
 	 */
-	while (!((status = UART_GET_STATUS(uart)) & RXEMPTY)) {
-
-		ch = UART_GET_DATA(uart);
-		uart->port.icount.rx++;
-
+	do {
+		/*
+		 * This could be an Rx Intr for err (no data),
+		 * so check err and clear that Intr first
+		 */
 		if (unlikely(status & (RXOERR | RXFERR))) {
 		if (unlikely(status & (RXOERR | RXFERR))) {
 			if (status & RXOERR) {
 			if (status & RXOERR) {
 				uart->port.icount.overrun++;
 				uart->port.icount.overrun++;
@@ -242,14 +242,19 @@ static void arc_serial_rx_chars(struct arc_uart_port *uart)
 		} else
 		} else
 			flg = TTY_NORMAL;
 			flg = TTY_NORMAL;
 
 
-		if (unlikely(uart_handle_sysrq_char(&uart->port, ch)))
-			goto done;
+		if (status & RXEMPTY)
+			continue;
 
 
-		uart_insert_char(&uart->port, status, RXOERR, ch, flg);
+		ch = UART_GET_DATA(uart);
+		uart->port.icount.rx++;
+
+		if (!(uart_handle_sysrq_char(&uart->port, ch)))
+			uart_insert_char(&uart->port, status, RXOERR, ch, flg);
 
 
-done:
+		spin_unlock(&uart->port.lock);
 		tty_flip_buffer_push(&uart->port.state->port);
 		tty_flip_buffer_push(&uart->port.state->port);
-	}
+		spin_lock(&uart->port.lock);
+	} while (!((status = UART_GET_STATUS(uart)) & RXEMPTY));
 }
 }
 
 
 /*
 /*
@@ -292,11 +297,11 @@ static irqreturn_t arc_serial_isr(int irq, void *dev_id)
 	 * notifications from the UART Controller.
 	 * notifications from the UART Controller.
 	 * To demultiplex between the two, we check the relevant bits
 	 * To demultiplex between the two, we check the relevant bits
 	 */
 	 */
-	if ((status & RXIENB) && !(status & RXEMPTY)) {
+	if (status & RXIENB) {
 
 
 		/* already in ISR, no need of xx_irqsave */
 		/* already in ISR, no need of xx_irqsave */
 		spin_lock(&uart->port.lock);
 		spin_lock(&uart->port.lock);
-		arc_serial_rx_chars(uart);
+		arc_serial_rx_chars(uart, status);
 		spin_unlock(&uart->port.lock);
 		spin_unlock(&uart->port.lock);
 	}
 	}
 
 
@@ -528,7 +533,7 @@ arc_uart_init_one(struct platform_device *pdev, int dev_id)
 	unsigned long *plat_data;
 	unsigned long *plat_data;
 	struct arc_uart_port *uart = &arc_uart_ports[dev_id];
 	struct arc_uart_port *uart = &arc_uart_ports[dev_id];
 
 
-	plat_data = ((unsigned long *)(pdev->dev.platform_data));
+	plat_data = (unsigned long *)dev_get_platdata(&pdev->dev);
 	if (!plat_data)
 	if (!plat_data)
 		return -ENODEV;
 		return -ENODEV;
 
 

+ 709 - 148
drivers/tty/serial/atmel_serial.c

@@ -39,8 +39,8 @@
 #include <linux/atmel_pdc.h>
 #include <linux/atmel_pdc.h>
 #include <linux/atmel_serial.h>
 #include <linux/atmel_serial.h>
 #include <linux/uaccess.h>
 #include <linux/uaccess.h>
-#include <linux/pinctrl/consumer.h>
 #include <linux/platform_data/atmel.h>
 #include <linux/platform_data/atmel.h>
+#include <linux/timer.h>
 
 
 #include <asm/io.h>
 #include <asm/io.h>
 #include <asm/ioctls.h>
 #include <asm/ioctls.h>
@@ -98,6 +98,7 @@ static void atmel_stop_rx(struct uart_port *port);
 #define UART_PUT_BRGR(port,v)	__raw_writel(v, (port)->membase + ATMEL_US_BRGR)
 #define UART_PUT_BRGR(port,v)	__raw_writel(v, (port)->membase + ATMEL_US_BRGR)
 #define UART_PUT_RTOR(port,v)	__raw_writel(v, (port)->membase + ATMEL_US_RTOR)
 #define UART_PUT_RTOR(port,v)	__raw_writel(v, (port)->membase + ATMEL_US_RTOR)
 #define UART_PUT_TTGR(port, v)	__raw_writel(v, (port)->membase + ATMEL_US_TTGR)
 #define UART_PUT_TTGR(port, v)	__raw_writel(v, (port)->membase + ATMEL_US_TTGR)
+#define UART_GET_IP_NAME(port)	__raw_readl((port)->membase + ATMEL_US_NAME)
 
 
  /* PDC registers */
  /* PDC registers */
 #define UART_PUT_PTCR(port,v)	__raw_writel(v, (port)->membase + ATMEL_PDC_PTCR)
 #define UART_PUT_PTCR(port,v)	__raw_writel(v, (port)->membase + ATMEL_PDC_PTCR)
@@ -140,13 +141,25 @@ struct atmel_uart_port {
 	u32			backup_imr;	/* IMR saved during suspend */
 	u32			backup_imr;	/* IMR saved during suspend */
 	int			break_active;	/* break being received */
 	int			break_active;	/* break being received */
 
 
-	short			use_dma_rx;	/* enable PDC receiver */
+	bool			use_dma_rx;	/* enable DMA receiver */
+	bool			use_pdc_rx;	/* enable PDC receiver */
 	short			pdc_rx_idx;	/* current PDC RX buffer */
 	short			pdc_rx_idx;	/* current PDC RX buffer */
 	struct atmel_dma_buffer	pdc_rx[2];	/* PDC receier */
 	struct atmel_dma_buffer	pdc_rx[2];	/* PDC receier */
 
 
-	short			use_dma_tx;	/* enable PDC transmitter */
+	bool			use_dma_tx;     /* enable DMA transmitter */
+	bool			use_pdc_tx;	/* enable PDC transmitter */
 	struct atmel_dma_buffer	pdc_tx;		/* PDC transmitter */
 	struct atmel_dma_buffer	pdc_tx;		/* PDC transmitter */
 
 
+	spinlock_t			lock_tx;	/* port lock */
+	spinlock_t			lock_rx;	/* port lock */
+	struct dma_chan			*chan_tx;
+	struct dma_chan			*chan_rx;
+	struct dma_async_tx_descriptor	*desc_tx;
+	struct dma_async_tx_descriptor	*desc_rx;
+	dma_cookie_t			cookie_tx;
+	dma_cookie_t			cookie_rx;
+	struct scatterlist		sg_tx;
+	struct scatterlist		sg_rx;
 	struct tasklet_struct	tasklet;
 	struct tasklet_struct	tasklet;
 	unsigned int		irq_status;
 	unsigned int		irq_status;
 	unsigned int		irq_status_prev;
 	unsigned int		irq_status_prev;
@@ -155,6 +168,14 @@ struct atmel_uart_port {
 
 
 	struct serial_rs485	rs485;		/* rs485 settings */
 	struct serial_rs485	rs485;		/* rs485 settings */
 	unsigned int		tx_done_mask;
 	unsigned int		tx_done_mask;
+	bool			is_usart;	/* usart or uart */
+	struct timer_list	uart_timer;	/* uart timer */
+	int (*prepare_rx)(struct uart_port *port);
+	int (*prepare_tx)(struct uart_port *port);
+	void (*schedule_rx)(struct uart_port *port);
+	void (*schedule_tx)(struct uart_port *port);
+	void (*release_rx)(struct uart_port *port);
+	void (*release_tx)(struct uart_port *port);
 };
 };
 
 
 static struct atmel_uart_port atmel_ports[ATMEL_MAX_UART];
 static struct atmel_uart_port atmel_ports[ATMEL_MAX_UART];
@@ -181,31 +202,45 @@ to_atmel_uart_port(struct uart_port *uart)
 }
 }
 
 
 #ifdef CONFIG_SERIAL_ATMEL_PDC
 #ifdef CONFIG_SERIAL_ATMEL_PDC
-static bool atmel_use_dma_rx(struct uart_port *port)
+static bool atmel_use_pdc_rx(struct uart_port *port)
 {
 {
 	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
 	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
 
 
-	return atmel_port->use_dma_rx;
+	return atmel_port->use_pdc_rx;
 }
 }
 
 
-static bool atmel_use_dma_tx(struct uart_port *port)
+static bool atmel_use_pdc_tx(struct uart_port *port)
 {
 {
 	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
 	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
 
 
-	return atmel_port->use_dma_tx;
+	return atmel_port->use_pdc_tx;
 }
 }
 #else
 #else
-static bool atmel_use_dma_rx(struct uart_port *port)
+static bool atmel_use_pdc_rx(struct uart_port *port)
 {
 {
 	return false;
 	return false;
 }
 }
 
 
-static bool atmel_use_dma_tx(struct uart_port *port)
+static bool atmel_use_pdc_tx(struct uart_port *port)
 {
 {
 	return false;
 	return false;
 }
 }
 #endif
 #endif
 
 
+static bool atmel_use_dma_tx(struct uart_port *port)
+{
+	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+
+	return atmel_port->use_dma_tx;
+}
+
+static bool atmel_use_dma_rx(struct uart_port *port)
+{
+	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+
+	return atmel_port->use_dma_rx;
+}
+
 /* Enable or disable the rs485 support */
 /* Enable or disable the rs485 support */
 void atmel_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf)
 void atmel_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf)
 {
 {
@@ -233,7 +268,7 @@ void atmel_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf)
 		mode |= ATMEL_US_USMODE_RS485;
 		mode |= ATMEL_US_USMODE_RS485;
 	} else {
 	} else {
 		dev_dbg(port->dev, "Setting UART to RS232\n");
 		dev_dbg(port->dev, "Setting UART to RS232\n");
-		if (atmel_use_dma_tx(port))
+		if (atmel_use_pdc_tx(port))
 			atmel_port->tx_done_mask = ATMEL_US_ENDTX |
 			atmel_port->tx_done_mask = ATMEL_US_ENDTX |
 				ATMEL_US_TXBUFE;
 				ATMEL_US_TXBUFE;
 		else
 		else
@@ -345,7 +380,7 @@ static void atmel_stop_tx(struct uart_port *port)
 {
 {
 	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
 	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
 
 
-	if (atmel_use_dma_tx(port)) {
+	if (atmel_use_pdc_tx(port)) {
 		/* disable PDC transmit */
 		/* disable PDC transmit */
 		UART_PUT_PTCR(port, ATMEL_PDC_TXTDIS);
 		UART_PUT_PTCR(port, ATMEL_PDC_TXTDIS);
 	}
 	}
@@ -364,7 +399,7 @@ static void atmel_start_tx(struct uart_port *port)
 {
 {
 	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
 	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
 
 
-	if (atmel_use_dma_tx(port)) {
+	if (atmel_use_pdc_tx(port)) {
 		if (UART_GET_PTSR(port) & ATMEL_PDC_TXTEN)
 		if (UART_GET_PTSR(port) & ATMEL_PDC_TXTEN)
 			/* The transmitter is already running.  Yes, we
 			/* The transmitter is already running.  Yes, we
 			   really need this.*/
 			   really need this.*/
@@ -390,7 +425,7 @@ static void atmel_start_rx(struct uart_port *port)
 
 
 	UART_PUT_CR(port, ATMEL_US_RXEN);
 	UART_PUT_CR(port, ATMEL_US_RXEN);
 
 
-	if (atmel_use_dma_rx(port)) {
+	if (atmel_use_pdc_rx(port)) {
 		/* enable PDC controller */
 		/* enable PDC controller */
 		UART_PUT_IER(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT |
 		UART_PUT_IER(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT |
 			port->read_status_mask);
 			port->read_status_mask);
@@ -407,7 +442,7 @@ static void atmel_stop_rx(struct uart_port *port)
 {
 {
 	UART_PUT_CR(port, ATMEL_US_RXDIS);
 	UART_PUT_CR(port, ATMEL_US_RXDIS);
 
 
-	if (atmel_use_dma_rx(port)) {
+	if (atmel_use_pdc_rx(port)) {
 		/* disable PDC receive */
 		/* disable PDC receive */
 		UART_PUT_PTCR(port, ATMEL_PDC_RXTDIS);
 		UART_PUT_PTCR(port, ATMEL_PDC_RXTDIS);
 		UART_PUT_IDR(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT |
 		UART_PUT_IDR(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT |
@@ -564,6 +599,372 @@ static void atmel_tx_chars(struct uart_port *port)
 		UART_PUT_IER(port, atmel_port->tx_done_mask);
 		UART_PUT_IER(port, atmel_port->tx_done_mask);
 }
 }
 
 
+static void atmel_complete_tx_dma(void *arg)
+{
+	struct atmel_uart_port *atmel_port = arg;
+	struct uart_port *port = &atmel_port->uart;
+	struct circ_buf *xmit = &port->state->xmit;
+	struct dma_chan *chan = atmel_port->chan_tx;
+	unsigned long flags;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	if (chan)
+		dmaengine_terminate_all(chan);
+	xmit->tail += sg_dma_len(&atmel_port->sg_tx);
+	xmit->tail &= UART_XMIT_SIZE - 1;
+
+	port->icount.tx += sg_dma_len(&atmel_port->sg_tx);
+
+	spin_lock_irq(&atmel_port->lock_tx);
+	async_tx_ack(atmel_port->desc_tx);
+	atmel_port->cookie_tx = -EINVAL;
+	atmel_port->desc_tx = NULL;
+	spin_unlock_irq(&atmel_port->lock_tx);
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(port);
+
+	/* Do we really need this? */
+	if (!uart_circ_empty(xmit))
+		tasklet_schedule(&atmel_port->tasklet);
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void atmel_release_tx_dma(struct uart_port *port)
+{
+	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+	struct dma_chan *chan = atmel_port->chan_tx;
+
+	if (chan) {
+		dmaengine_terminate_all(chan);
+		dma_release_channel(chan);
+		dma_unmap_sg(port->dev, &atmel_port->sg_tx, 1,
+				DMA_MEM_TO_DEV);
+	}
+
+	atmel_port->desc_tx = NULL;
+	atmel_port->chan_tx = NULL;
+	atmel_port->cookie_tx = -EINVAL;
+}
+
+/*
+ * Called from tasklet with TXRDY interrupt is disabled.
+ */
+static void atmel_tx_dma(struct uart_port *port)
+{
+	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+	struct circ_buf *xmit = &port->state->xmit;
+	struct dma_chan *chan = atmel_port->chan_tx;
+	struct dma_async_tx_descriptor *desc;
+	struct scatterlist *sg = &atmel_port->sg_tx;
+
+	/* Make sure we have an idle channel */
+	if (atmel_port->desc_tx != NULL)
+		return;
+
+	if (!uart_circ_empty(xmit) && !uart_tx_stopped(port)) {
+		/*
+		 * DMA is idle now.
+		 * Port xmit buffer is already mapped,
+		 * and it is one page... Just adjust
+		 * offsets and lengths. Since it is a circular buffer,
+		 * we have to transmit till the end, and then the rest.
+		 * Take the port lock to get a
+		 * consistent xmit buffer state.
+		 */
+		sg->offset = xmit->tail & (UART_XMIT_SIZE - 1);
+		sg_dma_address(sg) = (sg_dma_address(sg) &
+					~(UART_XMIT_SIZE - 1))
+					+ sg->offset;
+		sg_dma_len(sg) = CIRC_CNT_TO_END(xmit->head,
+						xmit->tail,
+						UART_XMIT_SIZE);
+		BUG_ON(!sg_dma_len(sg));
+
+		desc = dmaengine_prep_slave_sg(chan,
+						sg,
+						1,
+						DMA_MEM_TO_DEV,
+						DMA_PREP_INTERRUPT |
+						DMA_CTRL_ACK);
+		if (!desc) {
+			dev_err(port->dev, "Failed to send via dma!\n");
+			return;
+		}
+
+		dma_sync_sg_for_device(port->dev, sg, 1, DMA_MEM_TO_DEV);
+
+		atmel_port->desc_tx = desc;
+		desc->callback = atmel_complete_tx_dma;
+		desc->callback_param = atmel_port;
+		atmel_port->cookie_tx = dmaengine_submit(desc);
+
+	} else {
+		if (atmel_port->rs485.flags & SER_RS485_ENABLED) {
+			/* DMA done, stop TX, start RX for RS485 */
+			atmel_start_rx(port);
+		}
+	}
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(port);
+}
+
+static int atmel_prepare_tx_dma(struct uart_port *port)
+{
+	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+	dma_cap_mask_t		mask;
+	struct dma_slave_config config;
+	int ret, nent;
+
+	dma_cap_zero(mask);
+	dma_cap_set(DMA_SLAVE, mask);
+
+	atmel_port->chan_tx = dma_request_slave_channel(port->dev, "tx");
+	if (atmel_port->chan_tx == NULL)
+		goto chan_err;
+	dev_info(port->dev, "using %s for tx DMA transfers\n",
+		dma_chan_name(atmel_port->chan_tx));
+
+	spin_lock_init(&atmel_port->lock_tx);
+	sg_init_table(&atmel_port->sg_tx, 1);
+	/* UART circular tx buffer is an aligned page. */
+	BUG_ON((int)port->state->xmit.buf & ~PAGE_MASK);
+	sg_set_page(&atmel_port->sg_tx,
+			virt_to_page(port->state->xmit.buf),
+			UART_XMIT_SIZE,
+			(int)port->state->xmit.buf & ~PAGE_MASK);
+	nent = dma_map_sg(port->dev,
+				&atmel_port->sg_tx,
+				1,
+				DMA_MEM_TO_DEV);
+
+	if (!nent) {
+		dev_dbg(port->dev, "need to release resource of dma\n");
+		goto chan_err;
+	} else {
+		dev_dbg(port->dev, "%s: mapped %d@%p to %x\n", __func__,
+			sg_dma_len(&atmel_port->sg_tx),
+			port->state->xmit.buf,
+			sg_dma_address(&atmel_port->sg_tx));
+	}
+
+	/* Configure the slave DMA */
+	memset(&config, 0, sizeof(config));
+	config.direction = DMA_MEM_TO_DEV;
+	config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+	config.dst_addr = port->mapbase + ATMEL_US_THR;
+
+	ret = dmaengine_device_control(atmel_port->chan_tx,
+					DMA_SLAVE_CONFIG,
+					(unsigned long)&config);
+	if (ret) {
+		dev_err(port->dev, "DMA tx slave configuration failed\n");
+		goto chan_err;
+	}
+
+	return 0;
+
+chan_err:
+	dev_err(port->dev, "TX channel not available, switch to pio\n");
+	atmel_port->use_dma_tx = 0;
+	if (atmel_port->chan_tx)
+		atmel_release_tx_dma(port);
+	return -EINVAL;
+}
+
+static void atmel_flip_buffer_rx_dma(struct uart_port *port,
+					char *buf, size_t count)
+{
+	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+	struct tty_port *tport = &port->state->port;
+
+	dma_sync_sg_for_cpu(port->dev,
+				&atmel_port->sg_rx,
+				1,
+				DMA_DEV_TO_MEM);
+
+	tty_insert_flip_string(tport, buf, count);
+
+	dma_sync_sg_for_device(port->dev,
+				&atmel_port->sg_rx,
+				1,
+				DMA_DEV_TO_MEM);
+	/*
+	 * Drop the lock here since it might end up calling
+	 * uart_start(), which takes the lock.
+	 */
+	spin_unlock(&port->lock);
+	tty_flip_buffer_push(tport);
+	spin_lock(&port->lock);
+}
+
+static void atmel_complete_rx_dma(void *arg)
+{
+	struct uart_port *port = arg;
+	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+
+	tasklet_schedule(&atmel_port->tasklet);
+}
+
+static void atmel_release_rx_dma(struct uart_port *port)
+{
+	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+	struct dma_chan *chan = atmel_port->chan_rx;
+
+	if (chan) {
+		dmaengine_terminate_all(chan);
+		dma_release_channel(chan);
+		dma_unmap_sg(port->dev, &atmel_port->sg_rx, 1,
+				DMA_DEV_TO_MEM);
+	}
+
+	atmel_port->desc_rx = NULL;
+	atmel_port->chan_rx = NULL;
+	atmel_port->cookie_rx = -EINVAL;
+
+	if (!atmel_port->is_usart)
+		del_timer_sync(&atmel_port->uart_timer);
+}
+
+static void atmel_rx_from_dma(struct uart_port *port)
+{
+	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+	struct circ_buf *ring = &atmel_port->rx_ring;
+	struct dma_chan *chan = atmel_port->chan_rx;
+	struct dma_tx_state state;
+	enum dma_status dmastat;
+	size_t pending, count;
+
+
+	/* Reset the UART timeout early so that we don't miss one */
+	UART_PUT_CR(port, ATMEL_US_STTTO);
+	dmastat = dmaengine_tx_status(chan,
+				atmel_port->cookie_rx,
+				&state);
+	/* Restart a new tasklet if DMA status is error */
+	if (dmastat == DMA_ERROR) {
+		dev_dbg(port->dev, "Get residue error, restart tasklet\n");
+		UART_PUT_IER(port, ATMEL_US_TIMEOUT);
+		tasklet_schedule(&atmel_port->tasklet);
+		return;
+	}
+	/* current transfer size should no larger than dma buffer */
+	pending = sg_dma_len(&atmel_port->sg_rx) - state.residue;
+	BUG_ON(pending > sg_dma_len(&atmel_port->sg_rx));
+
+	/*
+	 * This will take the chars we have so far,
+	 * ring->head will record the transfer size, only new bytes come
+	 * will insert into the framework.
+	 */
+	if (pending > ring->head) {
+		count = pending - ring->head;
+
+		atmel_flip_buffer_rx_dma(port, ring->buf + ring->head, count);
+
+		ring->head += count;
+		if (ring->head == sg_dma_len(&atmel_port->sg_rx))
+			ring->head = 0;
+
+		port->icount.rx += count;
+	}
+
+	UART_PUT_IER(port, ATMEL_US_TIMEOUT);
+}
+
+static int atmel_prepare_rx_dma(struct uart_port *port)
+{
+	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+	struct dma_async_tx_descriptor *desc;
+	dma_cap_mask_t		mask;
+	struct dma_slave_config config;
+	struct circ_buf		*ring;
+	int ret, nent;
+
+	ring = &atmel_port->rx_ring;
+
+	dma_cap_zero(mask);
+	dma_cap_set(DMA_CYCLIC, mask);
+
+	atmel_port->chan_rx = dma_request_slave_channel(port->dev, "rx");
+	if (atmel_port->chan_rx == NULL)
+		goto chan_err;
+	dev_info(port->dev, "using %s for rx DMA transfers\n",
+		dma_chan_name(atmel_port->chan_rx));
+
+	spin_lock_init(&atmel_port->lock_rx);
+	sg_init_table(&atmel_port->sg_rx, 1);
+	/* UART circular rx buffer is an aligned page. */
+	BUG_ON((int)port->state->xmit.buf & ~PAGE_MASK);
+	sg_set_page(&atmel_port->sg_rx,
+			virt_to_page(ring->buf),
+			ATMEL_SERIAL_RINGSIZE,
+			(int)ring->buf & ~PAGE_MASK);
+			nent = dma_map_sg(port->dev,
+					&atmel_port->sg_rx,
+					1,
+					DMA_DEV_TO_MEM);
+
+	if (!nent) {
+		dev_dbg(port->dev, "need to release resource of dma\n");
+		goto chan_err;
+	} else {
+		dev_dbg(port->dev, "%s: mapped %d@%p to %x\n", __func__,
+			sg_dma_len(&atmel_port->sg_rx),
+			ring->buf,
+			sg_dma_address(&atmel_port->sg_rx));
+	}
+
+	/* Configure the slave DMA */
+	memset(&config, 0, sizeof(config));
+	config.direction = DMA_DEV_TO_MEM;
+	config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+	config.src_addr = port->mapbase + ATMEL_US_RHR;
+
+	ret = dmaengine_device_control(atmel_port->chan_rx,
+					DMA_SLAVE_CONFIG,
+					(unsigned long)&config);
+	if (ret) {
+		dev_err(port->dev, "DMA rx slave configuration failed\n");
+		goto chan_err;
+	}
+	/*
+	 * Prepare a cyclic dma transfer, assign 2 descriptors,
+	 * each one is half ring buffer size
+	 */
+	desc = dmaengine_prep_dma_cyclic(atmel_port->chan_rx,
+				sg_dma_address(&atmel_port->sg_rx),
+				sg_dma_len(&atmel_port->sg_rx),
+				sg_dma_len(&atmel_port->sg_rx)/2,
+				DMA_DEV_TO_MEM,
+				DMA_PREP_INTERRUPT);
+	desc->callback = atmel_complete_rx_dma;
+	desc->callback_param = port;
+	atmel_port->desc_rx = desc;
+	atmel_port->cookie_rx = dmaengine_submit(desc);
+
+	return 0;
+
+chan_err:
+	dev_err(port->dev, "RX channel not available, switch to pio\n");
+	atmel_port->use_dma_rx = 0;
+	if (atmel_port->chan_rx)
+		atmel_release_rx_dma(port);
+	return -EINVAL;
+}
+
+static void atmel_uart_timer_callback(unsigned long data)
+{
+	struct uart_port *port = (void *)data;
+	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+
+	tasklet_schedule(&atmel_port->tasklet);
+	mod_timer(&atmel_port->uart_timer, jiffies + uart_poll_timeout(port));
+}
+
 /*
 /*
  * receive interrupt handler.
  * receive interrupt handler.
  */
  */
@@ -572,7 +973,7 @@ atmel_handle_receive(struct uart_port *port, unsigned int pending)
 {
 {
 	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
 	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
 
 
-	if (atmel_use_dma_rx(port)) {
+	if (atmel_use_pdc_rx(port)) {
 		/*
 		/*
 		 * PDC receive. Just schedule the tasklet and let it
 		 * PDC receive. Just schedule the tasklet and let it
 		 * figure out the details.
 		 * figure out the details.
@@ -591,6 +992,13 @@ atmel_handle_receive(struct uart_port *port, unsigned int pending)
 			atmel_pdc_rxerr(port, pending);
 			atmel_pdc_rxerr(port, pending);
 	}
 	}
 
 
+	if (atmel_use_dma_rx(port)) {
+		if (pending & ATMEL_US_TIMEOUT) {
+			UART_PUT_IDR(port, ATMEL_US_TIMEOUT);
+			tasklet_schedule(&atmel_port->tasklet);
+		}
+	}
+
 	/* Interrupt receive */
 	/* Interrupt receive */
 	if (pending & ATMEL_US_RXRDY)
 	if (pending & ATMEL_US_RXRDY)
 		atmel_rx_chars(port);
 		atmel_rx_chars(port);
@@ -658,10 +1066,21 @@ static irqreturn_t atmel_interrupt(int irq, void *dev_id)
 	return pass_counter ? IRQ_HANDLED : IRQ_NONE;
 	return pass_counter ? IRQ_HANDLED : IRQ_NONE;
 }
 }
 
 
+static void atmel_release_tx_pdc(struct uart_port *port)
+{
+	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+	struct atmel_dma_buffer *pdc = &atmel_port->pdc_tx;
+
+	dma_unmap_single(port->dev,
+			 pdc->dma_addr,
+			 pdc->dma_size,
+			 DMA_TO_DEVICE);
+}
+
 /*
 /*
  * Called from tasklet with ENDTX and TXBUFE interrupts disabled.
  * Called from tasklet with ENDTX and TXBUFE interrupts disabled.
  */
  */
-static void atmel_tx_dma(struct uart_port *port)
+static void atmel_tx_pdc(struct uart_port *port)
 {
 {
 	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
 	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
 	struct circ_buf *xmit = &port->state->xmit;
 	struct circ_buf *xmit = &port->state->xmit;
@@ -710,6 +1129,23 @@ static void atmel_tx_dma(struct uart_port *port)
 		uart_write_wakeup(port);
 		uart_write_wakeup(port);
 }
 }
 
 
+static int atmel_prepare_tx_pdc(struct uart_port *port)
+{
+	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+	struct atmel_dma_buffer *pdc = &atmel_port->pdc_tx;
+	struct circ_buf *xmit = &port->state->xmit;
+
+	pdc->buf = xmit->buf;
+	pdc->dma_addr = dma_map_single(port->dev,
+					pdc->buf,
+					UART_XMIT_SIZE,
+					DMA_TO_DEVICE);
+	pdc->dma_size = UART_XMIT_SIZE;
+	pdc->ofs = 0;
+
+	return 0;
+}
+
 static void atmel_rx_from_ring(struct uart_port *port)
 static void atmel_rx_from_ring(struct uart_port *port)
 {
 {
 	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
 	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
@@ -778,7 +1214,26 @@ static void atmel_rx_from_ring(struct uart_port *port)
 	spin_lock(&port->lock);
 	spin_lock(&port->lock);
 }
 }
 
 
-static void atmel_rx_from_dma(struct uart_port *port)
+static void atmel_release_rx_pdc(struct uart_port *port)
+{
+	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+	int i;
+
+	for (i = 0; i < 2; i++) {
+		struct atmel_dma_buffer *pdc = &atmel_port->pdc_rx[i];
+
+		dma_unmap_single(port->dev,
+				 pdc->dma_addr,
+				 pdc->dma_size,
+				 DMA_FROM_DEVICE);
+		kfree(pdc->buf);
+	}
+
+	if (!atmel_port->is_usart)
+		del_timer_sync(&atmel_port->uart_timer);
+}
+
+static void atmel_rx_from_pdc(struct uart_port *port)
 {
 {
 	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
 	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
 	struct tty_port *tport = &port->state->port;
 	struct tty_port *tport = &port->state->port;
@@ -855,6 +1310,45 @@ static void atmel_rx_from_dma(struct uart_port *port)
 	UART_PUT_IER(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT);
 	UART_PUT_IER(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT);
 }
 }
 
 
+static int atmel_prepare_rx_pdc(struct uart_port *port)
+{
+	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+	int i;
+
+	for (i = 0; i < 2; i++) {
+		struct atmel_dma_buffer *pdc = &atmel_port->pdc_rx[i];
+
+		pdc->buf = kmalloc(PDC_BUFFER_SIZE, GFP_KERNEL);
+		if (pdc->buf == NULL) {
+			if (i != 0) {
+				dma_unmap_single(port->dev,
+					atmel_port->pdc_rx[0].dma_addr,
+					PDC_BUFFER_SIZE,
+					DMA_FROM_DEVICE);
+				kfree(atmel_port->pdc_rx[0].buf);
+			}
+			atmel_port->use_pdc_rx = 0;
+			return -ENOMEM;
+		}
+		pdc->dma_addr = dma_map_single(port->dev,
+						pdc->buf,
+						PDC_BUFFER_SIZE,
+						DMA_FROM_DEVICE);
+		pdc->dma_size = PDC_BUFFER_SIZE;
+		pdc->ofs = 0;
+	}
+
+	atmel_port->pdc_rx_idx = 0;
+
+	UART_PUT_RPR(port, atmel_port->pdc_rx[0].dma_addr);
+	UART_PUT_RCR(port, PDC_BUFFER_SIZE);
+
+	UART_PUT_RNPR(port, atmel_port->pdc_rx[1].dma_addr);
+	UART_PUT_RNCR(port, PDC_BUFFER_SIZE);
+
+	return 0;
+}
+
 /*
 /*
  * tasklet handling tty stuff outside the interrupt handler.
  * tasklet handling tty stuff outside the interrupt handler.
  */
  */
@@ -868,10 +1362,7 @@ static void atmel_tasklet_func(unsigned long data)
 	/* The interrupt handler does not take the lock */
 	/* The interrupt handler does not take the lock */
 	spin_lock(&port->lock);
 	spin_lock(&port->lock);
 
 
-	if (atmel_use_dma_tx(port))
-		atmel_tx_dma(port);
-	else
-		atmel_tx_chars(port);
+	atmel_port->schedule_tx(port);
 
 
 	status = atmel_port->irq_status;
 	status = atmel_port->irq_status;
 	status_change = status ^ atmel_port->irq_status_prev;
 	status_change = status ^ atmel_port->irq_status_prev;
@@ -893,19 +1384,152 @@ static void atmel_tasklet_func(unsigned long data)
 		atmel_port->irq_status_prev = status;
 		atmel_port->irq_status_prev = status;
 	}
 	}
 
 
-	if (atmel_use_dma_rx(port))
-		atmel_rx_from_dma(port);
-	else
-		atmel_rx_from_ring(port);
+	atmel_port->schedule_rx(port);
 
 
 	spin_unlock(&port->lock);
 	spin_unlock(&port->lock);
 }
 }
 
 
+static int atmel_init_property(struct atmel_uart_port *atmel_port,
+				struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct atmel_uart_data *pdata = dev_get_platdata(&pdev->dev);
+
+	if (np) {
+		/* DMA/PDC usage specification */
+		if (of_get_property(np, "atmel,use-dma-rx", NULL)) {
+			if (of_get_property(np, "dmas", NULL)) {
+				atmel_port->use_dma_rx  = true;
+				atmel_port->use_pdc_rx  = false;
+			} else {
+				atmel_port->use_dma_rx  = false;
+				atmel_port->use_pdc_rx  = true;
+			}
+		} else {
+			atmel_port->use_dma_rx  = false;
+			atmel_port->use_pdc_rx  = false;
+		}
+
+		if (of_get_property(np, "atmel,use-dma-tx", NULL)) {
+			if (of_get_property(np, "dmas", NULL)) {
+				atmel_port->use_dma_tx  = true;
+				atmel_port->use_pdc_tx  = false;
+			} else {
+				atmel_port->use_dma_tx  = false;
+				atmel_port->use_pdc_tx  = true;
+			}
+		} else {
+			atmel_port->use_dma_tx  = false;
+			atmel_port->use_pdc_tx  = false;
+		}
+
+	} else {
+		atmel_port->use_pdc_rx  = pdata->use_dma_rx;
+		atmel_port->use_pdc_tx  = pdata->use_dma_tx;
+		atmel_port->use_dma_rx  = false;
+		atmel_port->use_dma_tx  = false;
+	}
+
+	return 0;
+}
+
+static void atmel_init_rs485(struct atmel_uart_port *atmel_port,
+				struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct atmel_uart_data *pdata = dev_get_platdata(&pdev->dev);
+
+	if (np) {
+		u32 rs485_delay[2];
+		/* rs485 properties */
+		if (of_property_read_u32_array(np, "rs485-rts-delay",
+					rs485_delay, 2) == 0) {
+			struct serial_rs485 *rs485conf = &atmel_port->rs485;
+
+			rs485conf->delay_rts_before_send = rs485_delay[0];
+			rs485conf->delay_rts_after_send = rs485_delay[1];
+			rs485conf->flags = 0;
+
+		if (of_get_property(np, "rs485-rx-during-tx", NULL))
+			rs485conf->flags |= SER_RS485_RX_DURING_TX;
+
+		if (of_get_property(np, "linux,rs485-enabled-at-boot-time",
+								NULL))
+			rs485conf->flags |= SER_RS485_ENABLED;
+		}
+	} else {
+		atmel_port->rs485       = pdata->rs485;
+	}
+
+}
+
+static void atmel_set_ops(struct uart_port *port)
+{
+	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+
+	if (atmel_use_dma_rx(port)) {
+		atmel_port->prepare_rx = &atmel_prepare_rx_dma;
+		atmel_port->schedule_rx = &atmel_rx_from_dma;
+		atmel_port->release_rx = &atmel_release_rx_dma;
+	} else if (atmel_use_pdc_rx(port)) {
+		atmel_port->prepare_rx = &atmel_prepare_rx_pdc;
+		atmel_port->schedule_rx = &atmel_rx_from_pdc;
+		atmel_port->release_rx = &atmel_release_rx_pdc;
+	} else {
+		atmel_port->prepare_rx = NULL;
+		atmel_port->schedule_rx = &atmel_rx_from_ring;
+		atmel_port->release_rx = NULL;
+	}
+
+	if (atmel_use_dma_tx(port)) {
+		atmel_port->prepare_tx = &atmel_prepare_tx_dma;
+		atmel_port->schedule_tx = &atmel_tx_dma;
+		atmel_port->release_tx = &atmel_release_tx_dma;
+	} else if (atmel_use_pdc_tx(port)) {
+		atmel_port->prepare_tx = &atmel_prepare_tx_pdc;
+		atmel_port->schedule_tx = &atmel_tx_pdc;
+		atmel_port->release_tx = &atmel_release_tx_pdc;
+	} else {
+		atmel_port->prepare_tx = NULL;
+		atmel_port->schedule_tx = &atmel_tx_chars;
+		atmel_port->release_tx = NULL;
+	}
+}
+
+/*
+ * Get ip name usart or uart
+ */
+static int atmel_get_ip_name(struct uart_port *port)
+{
+	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+	int name = UART_GET_IP_NAME(port);
+	int usart, uart;
+	/* usart and uart ascii */
+	usart = 0x55534152;
+	uart = 0x44424755;
+
+	atmel_port->is_usart = false;
+
+	if (name == usart) {
+		dev_dbg(port->dev, "This is usart\n");
+		atmel_port->is_usart = true;
+	} else if (name == uart) {
+		dev_dbg(port->dev, "This is uart\n");
+		atmel_port->is_usart = false;
+	} else {
+		dev_err(port->dev, "Not supported ip name, set to uart\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
 /*
 /*
  * Perform initialization and enable port for reception
  * Perform initialization and enable port for reception
  */
  */
 static int atmel_startup(struct uart_port *port)
 static int atmel_startup(struct uart_port *port)
 {
 {
+	struct platform_device *pdev = to_platform_device(port->dev);
 	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
 	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
 	struct tty_struct *tty = port->state->port.tty;
 	struct tty_struct *tty = port->state->port.tty;
 	int retval;
 	int retval;
@@ -930,53 +1554,19 @@ static int atmel_startup(struct uart_port *port)
 	/*
 	/*
 	 * Initialize DMA (if necessary)
 	 * Initialize DMA (if necessary)
 	 */
 	 */
-	if (atmel_use_dma_rx(port)) {
-		int i;
-
-		for (i = 0; i < 2; i++) {
-			struct atmel_dma_buffer *pdc = &atmel_port->pdc_rx[i];
-
-			pdc->buf = kmalloc(PDC_BUFFER_SIZE, GFP_KERNEL);
-			if (pdc->buf == NULL) {
-				if (i != 0) {
-					dma_unmap_single(port->dev,
-						atmel_port->pdc_rx[0].dma_addr,
-						PDC_BUFFER_SIZE,
-						DMA_FROM_DEVICE);
-					kfree(atmel_port->pdc_rx[0].buf);
-				}
-				free_irq(port->irq, port);
-				return -ENOMEM;
-			}
-			pdc->dma_addr = dma_map_single(port->dev,
-						       pdc->buf,
-						       PDC_BUFFER_SIZE,
-						       DMA_FROM_DEVICE);
-			pdc->dma_size = PDC_BUFFER_SIZE;
-			pdc->ofs = 0;
-		}
+	atmel_init_property(atmel_port, pdev);
 
 
-		atmel_port->pdc_rx_idx = 0;
-
-		UART_PUT_RPR(port, atmel_port->pdc_rx[0].dma_addr);
-		UART_PUT_RCR(port, PDC_BUFFER_SIZE);
-
-		UART_PUT_RNPR(port, atmel_port->pdc_rx[1].dma_addr);
-		UART_PUT_RNCR(port, PDC_BUFFER_SIZE);
+	if (atmel_port->prepare_rx) {
+		retval = atmel_port->prepare_rx(port);
+		if (retval < 0)
+			atmel_set_ops(port);
 	}
 	}
-	if (atmel_use_dma_tx(port)) {
-		struct atmel_dma_buffer *pdc = &atmel_port->pdc_tx;
-		struct circ_buf *xmit = &port->state->xmit;
 
 
-		pdc->buf = xmit->buf;
-		pdc->dma_addr = dma_map_single(port->dev,
-					       pdc->buf,
-					       UART_XMIT_SIZE,
-					       DMA_TO_DEVICE);
-		pdc->dma_size = UART_XMIT_SIZE;
-		pdc->ofs = 0;
+	if (atmel_port->prepare_tx) {
+		retval = atmel_port->prepare_tx(port);
+		if (retval < 0)
+			atmel_set_ops(port);
 	}
 	}
-
 	/*
 	/*
 	 * If there is a specific "open" function (to register
 	 * If there is a specific "open" function (to register
 	 * control line interrupts)
 	 * control line interrupts)
@@ -1000,14 +1590,38 @@ static int atmel_startup(struct uart_port *port)
 	/* enable xmit & rcvr */
 	/* enable xmit & rcvr */
 	UART_PUT_CR(port, ATMEL_US_TXEN | ATMEL_US_RXEN);
 	UART_PUT_CR(port, ATMEL_US_TXEN | ATMEL_US_RXEN);
 
 
-	if (atmel_use_dma_rx(port)) {
+	if (atmel_use_pdc_rx(port)) {
 		/* set UART timeout */
 		/* set UART timeout */
-		UART_PUT_RTOR(port, PDC_RX_TIMEOUT);
-		UART_PUT_CR(port, ATMEL_US_STTTO);
-
-		UART_PUT_IER(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT);
+		if (!atmel_port->is_usart) {
+			setup_timer(&atmel_port->uart_timer,
+					atmel_uart_timer_callback,
+					(unsigned long)port);
+			mod_timer(&atmel_port->uart_timer,
+					jiffies + uart_poll_timeout(port));
+		/* set USART timeout */
+		} else {
+			UART_PUT_RTOR(port, PDC_RX_TIMEOUT);
+			UART_PUT_CR(port, ATMEL_US_STTTO);
+
+			UART_PUT_IER(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT);
+		}
 		/* enable PDC controller */
 		/* enable PDC controller */
 		UART_PUT_PTCR(port, ATMEL_PDC_RXTEN);
 		UART_PUT_PTCR(port, ATMEL_PDC_RXTEN);
+	} else if (atmel_use_dma_rx(port)) {
+		/* set UART timeout */
+		if (!atmel_port->is_usart) {
+			setup_timer(&atmel_port->uart_timer,
+					atmel_uart_timer_callback,
+					(unsigned long)port);
+			mod_timer(&atmel_port->uart_timer,
+					jiffies + uart_poll_timeout(port));
+		/* set USART timeout */
+		} else {
+			UART_PUT_RTOR(port, PDC_RX_TIMEOUT);
+			UART_PUT_CR(port, ATMEL_US_STTTO);
+
+			UART_PUT_IER(port, ATMEL_US_TIMEOUT);
+		}
 	} else {
 	} else {
 		/* enable receive only */
 		/* enable receive only */
 		UART_PUT_IER(port, ATMEL_US_RXRDY);
 		UART_PUT_IER(port, ATMEL_US_RXRDY);
@@ -1031,27 +1645,10 @@ static void atmel_shutdown(struct uart_port *port)
 	/*
 	/*
 	 * Shut-down the DMA.
 	 * Shut-down the DMA.
 	 */
 	 */
-	if (atmel_use_dma_rx(port)) {
-		int i;
-
-		for (i = 0; i < 2; i++) {
-			struct atmel_dma_buffer *pdc = &atmel_port->pdc_rx[i];
-
-			dma_unmap_single(port->dev,
-					 pdc->dma_addr,
-					 pdc->dma_size,
-					 DMA_FROM_DEVICE);
-			kfree(pdc->buf);
-		}
-	}
-	if (atmel_use_dma_tx(port)) {
-		struct atmel_dma_buffer *pdc = &atmel_port->pdc_tx;
-
-		dma_unmap_single(port->dev,
-				 pdc->dma_addr,
-				 pdc->dma_size,
-				 DMA_TO_DEVICE);
-	}
+	if (atmel_port->release_rx)
+		atmel_port->release_rx(port);
+	if (atmel_port->release_tx)
+		atmel_port->release_tx(port);
 
 
 	/*
 	/*
 	 * Disable all interrupts, port and break condition.
 	 * Disable all interrupts, port and break condition.
@@ -1080,7 +1677,7 @@ static void atmel_flush_buffer(struct uart_port *port)
 {
 {
 	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
 	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
 
 
-	if (atmel_use_dma_tx(port)) {
+	if (atmel_use_pdc_tx(port)) {
 		UART_PUT_TCR(port, 0);
 		UART_PUT_TCR(port, 0);
 		atmel_port->pdc_tx.ofs = 0;
 		atmel_port->pdc_tx.ofs = 0;
 	}
 	}
@@ -1193,7 +1790,7 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios,
 	if (termios->c_iflag & (BRKINT | PARMRK))
 	if (termios->c_iflag & (BRKINT | PARMRK))
 		port->read_status_mask |= ATMEL_US_RXBRK;
 		port->read_status_mask |= ATMEL_US_RXBRK;
 
 
-	if (atmel_use_dma_rx(port))
+	if (atmel_use_pdc_rx(port))
 		/* need to enable error interrupts */
 		/* need to enable error interrupts */
 		UART_PUT_IER(port, port->read_status_mask);
 		UART_PUT_IER(port, port->read_status_mask);
 
 
@@ -1423,38 +2020,6 @@ static struct uart_ops atmel_pops = {
 #endif
 #endif
 };
 };
 
 
-static void atmel_of_init_port(struct atmel_uart_port *atmel_port,
-					 struct device_node *np)
-{
-	u32 rs485_delay[2];
-
-	/* DMA/PDC usage specification */
-	if (of_get_property(np, "atmel,use-dma-rx", NULL))
-		atmel_port->use_dma_rx	= 1;
-	else
-		atmel_port->use_dma_rx	= 0;
-	if (of_get_property(np, "atmel,use-dma-tx", NULL))
-		atmel_port->use_dma_tx	= 1;
-	else
-		atmel_port->use_dma_tx	= 0;
-
-	/* rs485 properties */
-	if (of_property_read_u32_array(np, "rs485-rts-delay",
-					    rs485_delay, 2) == 0) {
-		struct serial_rs485 *rs485conf = &atmel_port->rs485;
-
-		rs485conf->delay_rts_before_send = rs485_delay[0];
-		rs485conf->delay_rts_after_send = rs485_delay[1];
-		rs485conf->flags = 0;
-
-		if (of_get_property(np, "rs485-rx-during-tx", NULL))
-			rs485conf->flags |= SER_RS485_RX_DURING_TX;
-
-		if (of_get_property(np, "linux,rs485-enabled-at-boot-time", NULL))
-			rs485conf->flags |= SER_RS485_ENABLED;
-	}
-}
-
 /*
 /*
  * Configure the port from the platform device resource info.
  * Configure the port from the platform device resource info.
  */
  */
@@ -1463,15 +2028,12 @@ static int atmel_init_port(struct atmel_uart_port *atmel_port,
 {
 {
 	int ret;
 	int ret;
 	struct uart_port *port = &atmel_port->uart;
 	struct uart_port *port = &atmel_port->uart;
-	struct atmel_uart_data *pdata = pdev->dev.platform_data;
+	struct atmel_uart_data *pdata = dev_get_platdata(&pdev->dev);
 
 
-	if (pdev->dev.of_node) {
-		atmel_of_init_port(atmel_port, pdev->dev.of_node);
-	} else {
-		atmel_port->use_dma_rx	= pdata->use_dma_rx;
-		atmel_port->use_dma_tx	= pdata->use_dma_tx;
-		atmel_port->rs485	= pdata->rs485;
-	}
+	if (!atmel_init_property(atmel_port, pdev))
+		atmel_set_ops(port);
+
+	atmel_init_rs485(atmel_port, pdev);
 
 
 	port->iotype		= UPIO_MEM;
 	port->iotype		= UPIO_MEM;
 	port->flags		= UPF_BOOT_AUTOCONF;
 	port->flags		= UPF_BOOT_AUTOCONF;
@@ -1516,7 +2078,7 @@ static int atmel_init_port(struct atmel_uart_port *atmel_port,
 	/* Use TXEMPTY for interrupt when rs485 else TXRDY or ENDTX|TXBUFE */
 	/* Use TXEMPTY for interrupt when rs485 else TXRDY or ENDTX|TXBUFE */
 	if (atmel_port->rs485.flags & SER_RS485_ENABLED)
 	if (atmel_port->rs485.flags & SER_RS485_ENABLED)
 		atmel_port->tx_done_mask = ATMEL_US_TXEMPTY;
 		atmel_port->tx_done_mask = ATMEL_US_TXEMPTY;
-	else if (atmel_use_dma_tx(port)) {
+	else if (atmel_use_pdc_tx(port)) {
 		port->fifosize = PDC_BUFFER_SIZE;
 		port->fifosize = PDC_BUFFER_SIZE;
 		atmel_port->tx_done_mask = ATMEL_US_ENDTX | ATMEL_US_TXBUFE;
 		atmel_port->tx_done_mask = ATMEL_US_ENDTX | ATMEL_US_TXBUFE;
 	} else {
 	} else {
@@ -1664,7 +2226,7 @@ static int __init atmel_console_init(void)
 	int ret;
 	int ret;
 	if (atmel_default_console_device) {
 	if (atmel_default_console_device) {
 		struct atmel_uart_data *pdata =
 		struct atmel_uart_data *pdata =
-			atmel_default_console_device->dev.platform_data;
+			dev_get_platdata(&atmel_default_console_device->dev);
 		int id = pdata->num;
 		int id = pdata->num;
 		struct atmel_uart_port *port = &atmel_ports[id];
 		struct atmel_uart_port *port = &atmel_ports[id];
 
 
@@ -1772,10 +2334,9 @@ static int atmel_serial_probe(struct platform_device *pdev)
 {
 {
 	struct atmel_uart_port *port;
 	struct atmel_uart_port *port;
 	struct device_node *np = pdev->dev.of_node;
 	struct device_node *np = pdev->dev.of_node;
-	struct atmel_uart_data *pdata = pdev->dev.platform_data;
+	struct atmel_uart_data *pdata = dev_get_platdata(&pdev->dev);
 	void *data;
 	void *data;
 	int ret = -ENODEV;
 	int ret = -ENODEV;
-	struct pinctrl *pinctrl;
 
 
 	BUILD_BUG_ON(ATMEL_SERIAL_RINGSIZE & (ATMEL_SERIAL_RINGSIZE - 1));
 	BUILD_BUG_ON(ATMEL_SERIAL_RINGSIZE & (ATMEL_SERIAL_RINGSIZE - 1));
 
 
@@ -1809,13 +2370,7 @@ static int atmel_serial_probe(struct platform_device *pdev)
 	if (ret)
 	if (ret)
 		goto err;
 		goto err;
 
 
-	pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
-	if (IS_ERR(pinctrl)) {
-		ret = PTR_ERR(pinctrl);
-		goto err;
-	}
-
-	if (!atmel_use_dma_rx(&port->uart)) {
+	if (!atmel_use_pdc_rx(&port->uart)) {
 		ret = -ENOMEM;
 		ret = -ENOMEM;
 		data = kmalloc(sizeof(struct atmel_uart_char)
 		data = kmalloc(sizeof(struct atmel_uart_char)
 				* ATMEL_SERIAL_RINGSIZE, GFP_KERNEL);
 				* ATMEL_SERIAL_RINGSIZE, GFP_KERNEL);
@@ -1847,6 +2402,13 @@ static int atmel_serial_probe(struct platform_device *pdev)
 		UART_PUT_CR(&port->uart, ATMEL_US_RTSEN);
 		UART_PUT_CR(&port->uart, ATMEL_US_RTSEN);
 	}
 	}
 
 
+	/*
+	 * Get port name of usart or uart
+	 */
+	ret = atmel_get_ip_name(&port->uart);
+	if (ret < 0)
+		goto err_add_port;
+
 	return 0;
 	return 0;
 
 
 err_add_port:
 err_add_port:
@@ -1868,7 +2430,6 @@ static int atmel_serial_remove(struct platform_device *pdev)
 	int ret = 0;
 	int ret = 0;
 
 
 	device_init_wakeup(&pdev->dev, 0);
 	device_init_wakeup(&pdev->dev, 0);
-	platform_set_drvdata(pdev, NULL);
 
 
 	ret = uart_remove_one_port(&atmel_uart, port);
 	ret = uart_remove_one_port(&atmel_uart, port);
 
 

+ 2 - 1
drivers/tty/serial/bcm63xx_uart.c

@@ -302,7 +302,9 @@ static void bcm_uart_do_rx(struct uart_port *port)
 
 
 	} while (--max_count);
 	} while (--max_count);
 
 
+	spin_unlock(&port->lock);
 	tty_flip_buffer_push(tty_port);
 	tty_flip_buffer_push(tty_port);
+	spin_lock(&port->lock);
 }
 }
 
 
 /*
 /*
@@ -852,7 +854,6 @@ static int bcm_uart_remove(struct platform_device *pdev)
 
 
 	port = platform_get_drvdata(pdev);
 	port = platform_get_drvdata(pdev);
 	uart_remove_one_port(&bcm_uart_driver, port);
 	uart_remove_one_port(&bcm_uart_driver, port);
-	platform_set_drvdata(pdev, NULL);
 	/* mark port as free */
 	/* mark port as free */
 	ports[pdev->id].membase = 0;
 	ports[pdev->id].membase = 0;
 	return 0;
 	return 0;

+ 8 - 6
drivers/tty/serial/bfin_sport_uart.c

@@ -161,11 +161,12 @@ static irqreturn_t sport_uart_rx_irq(int irq, void *dev_id)
 		if (!uart_handle_sysrq_char(&up->port, ch))
 		if (!uart_handle_sysrq_char(&up->port, ch))
 			tty_insert_flip_char(port, ch, TTY_NORMAL);
 			tty_insert_flip_char(port, ch, TTY_NORMAL);
 	}
 	}
-	/* XXX this won't deadlock with lowlat? */
-	tty_flip_buffer_push(port);
 
 
 	spin_unlock(&up->port.lock);
 	spin_unlock(&up->port.lock);
 
 
+	/* XXX this won't deadlock with lowlat? */
+	tty_flip_buffer_push(port);
+
 	return IRQ_HANDLED;
 	return IRQ_HANDLED;
 }
 }
 
 
@@ -766,7 +767,8 @@ static int sport_uart_probe(struct platform_device *pdev)
 		}
 		}
 
 
 		ret = peripheral_request_list(
 		ret = peripheral_request_list(
-			(unsigned short *)pdev->dev.platform_data, DRV_NAME);
+			(unsigned short *)dev_get_platdata(&pdev->dev),
+			DRV_NAME);
 		if (ret) {
 		if (ret) {
 			dev_err(&pdev->dev,
 			dev_err(&pdev->dev,
 				"Fail to request SPORT peripherals\n");
 				"Fail to request SPORT peripherals\n");
@@ -843,7 +845,7 @@ out_error_unmap:
 		iounmap(sport->port.membase);
 		iounmap(sport->port.membase);
 out_error_free_peripherals:
 out_error_free_peripherals:
 		peripheral_free_list(
 		peripheral_free_list(
-			(unsigned short *)pdev->dev.platform_data);
+			(unsigned short *)dev_get_platdata(&pdev->dev));
 out_error_free_mem:
 out_error_free_mem:
 		kfree(sport);
 		kfree(sport);
 		bfin_sport_uart_ports[pdev->id] = NULL;
 		bfin_sport_uart_ports[pdev->id] = NULL;
@@ -863,7 +865,7 @@ static int sport_uart_remove(struct platform_device *pdev)
 		uart_remove_one_port(&sport_uart_reg, &sport->port);
 		uart_remove_one_port(&sport_uart_reg, &sport->port);
 		iounmap(sport->port.membase);
 		iounmap(sport->port.membase);
 		peripheral_free_list(
 		peripheral_free_list(
-			(unsigned short *)pdev->dev.platform_data);
+			(unsigned short *)dev_get_platdata(&pdev->dev));
 		kfree(sport);
 		kfree(sport);
 		bfin_sport_uart_ports[pdev->id] = NULL;
 		bfin_sport_uart_ports[pdev->id] = NULL;
 	}
 	}
@@ -883,7 +885,7 @@ static struct platform_driver sport_uart_driver = {
 };
 };
 
 
 #ifdef CONFIG_SERIAL_BFIN_SPORT_CONSOLE
 #ifdef CONFIG_SERIAL_BFIN_SPORT_CONSOLE
-static __initdata struct early_platform_driver early_sport_uart_driver = {
+static struct early_platform_driver early_sport_uart_driver __initdata = {
 	.class_str = CLASS_BFIN_SPORT_CONSOLE,
 	.class_str = CLASS_BFIN_SPORT_CONSOLE,
 	.pdrv = &sport_uart_driver,
 	.pdrv = &sport_uart_driver,
 	.requested_id = EARLY_PLATFORM_ID_UNSET,
 	.requested_id = EARLY_PLATFORM_ID_UNSET,

+ 9 - 12
drivers/tty/serial/bfin_uart.c

@@ -41,10 +41,6 @@
 # undef CONFIG_EARLY_PRINTK
 # undef CONFIG_EARLY_PRINTK
 #endif
 #endif
 
 
-#ifdef CONFIG_SERIAL_BFIN_MODULE
-# undef CONFIG_EARLY_PRINTK
-#endif
-
 /* UART name and device definitions */
 /* UART name and device definitions */
 #define BFIN_SERIAL_DEV_NAME	"ttyBF"
 #define BFIN_SERIAL_DEV_NAME	"ttyBF"
 #define BFIN_SERIAL_MAJOR	204
 #define BFIN_SERIAL_MAJOR	204
@@ -1180,7 +1176,7 @@ bfin_earlyprintk_console_write(struct console *co, const char *s, unsigned int c
  * don't let the common infrastructure play with things. (see calls to setup
  * don't let the common infrastructure play with things. (see calls to setup
  * & earlysetup in ./kernel/printk.c:register_console()
  * & earlysetup in ./kernel/printk.c:register_console()
  */
  */
-static struct __initdata console bfin_early_serial_console = {
+static struct console bfin_early_serial_console __initdata = {
 	.name = "early_BFuart",
 	.name = "early_BFuart",
 	.write = bfin_earlyprintk_console_write,
 	.write = bfin_earlyprintk_console_write,
 	.device = uart_console_device,
 	.device = uart_console_device,
@@ -1244,7 +1240,8 @@ static int bfin_serial_probe(struct platform_device *pdev)
 			 */
 			 */
 #endif
 #endif
 		ret = peripheral_request_list(
 		ret = peripheral_request_list(
-			(unsigned short *)pdev->dev.platform_data, DRIVER_NAME);
+			(unsigned short *)dev_get_platdata(&pdev->dev),
+			DRIVER_NAME);
 		if (ret) {
 		if (ret) {
 			dev_err(&pdev->dev,
 			dev_err(&pdev->dev,
 				"fail to request bfin serial peripherals\n");
 				"fail to request bfin serial peripherals\n");
@@ -1362,7 +1359,7 @@ out_error_unmap:
 		iounmap(uart->port.membase);
 		iounmap(uart->port.membase);
 out_error_free_peripherals:
 out_error_free_peripherals:
 		peripheral_free_list(
 		peripheral_free_list(
-			(unsigned short *)pdev->dev.platform_data);
+			(unsigned short *)dev_get_platdata(&pdev->dev));
 out_error_free_mem:
 out_error_free_mem:
 		kfree(uart);
 		kfree(uart);
 		bfin_serial_ports[pdev->id] = NULL;
 		bfin_serial_ports[pdev->id] = NULL;
@@ -1381,7 +1378,7 @@ static int bfin_serial_remove(struct platform_device *pdev)
 		uart_remove_one_port(&bfin_serial_reg, &uart->port);
 		uart_remove_one_port(&bfin_serial_reg, &uart->port);
 		iounmap(uart->port.membase);
 		iounmap(uart->port.membase);
 		peripheral_free_list(
 		peripheral_free_list(
-			(unsigned short *)pdev->dev.platform_data);
+			(unsigned short *)dev_get_platdata(&pdev->dev));
 		kfree(uart);
 		kfree(uart);
 		bfin_serial_ports[pdev->id] = NULL;
 		bfin_serial_ports[pdev->id] = NULL;
 	}
 	}
@@ -1401,7 +1398,7 @@ static struct platform_driver bfin_serial_driver = {
 };
 };
 
 
 #if defined(CONFIG_SERIAL_BFIN_CONSOLE)
 #if defined(CONFIG_SERIAL_BFIN_CONSOLE)
-static __initdata struct early_platform_driver early_bfin_serial_driver = {
+static struct early_platform_driver early_bfin_serial_driver __initdata = {
 	.class_str = CLASS_BFIN_CONSOLE,
 	.class_str = CLASS_BFIN_CONSOLE,
 	.pdrv = &bfin_serial_driver,
 	.pdrv = &bfin_serial_driver,
 	.requested_id = EARLY_PLATFORM_ID_UNSET,
 	.requested_id = EARLY_PLATFORM_ID_UNSET,
@@ -1436,7 +1433,7 @@ static int bfin_earlyprintk_probe(struct platform_device *pdev)
 	}
 	}
 
 
 	ret = peripheral_request_list(
 	ret = peripheral_request_list(
-		(unsigned short *)pdev->dev.platform_data, DRIVER_NAME);
+		(unsigned short *)dev_get_platdata(&pdev->dev), DRIVER_NAME);
 	if (ret) {
 	if (ret) {
 		dev_err(&pdev->dev,
 		dev_err(&pdev->dev,
 				"fail to request bfin serial peripherals\n");
 				"fail to request bfin serial peripherals\n");
@@ -1467,7 +1464,7 @@ static int bfin_earlyprintk_probe(struct platform_device *pdev)
 
 
 out_error_free_peripherals:
 out_error_free_peripherals:
 	peripheral_free_list(
 	peripheral_free_list(
-		(unsigned short *)pdev->dev.platform_data);
+		(unsigned short *)dev_get_platdata(&pdev->dev));
 
 
 	return ret;
 	return ret;
 }
 }
@@ -1480,7 +1477,7 @@ static struct platform_driver bfin_earlyprintk_driver = {
 	},
 	},
 };
 };
 
 
-static __initdata struct early_platform_driver early_bfin_earlyprintk_driver = {
+static struct early_platform_driver early_bfin_earlyprintk_driver __initdata = {
 	.class_str = CLASS_BFIN_EARLYPRINTK,
 	.class_str = CLASS_BFIN_EARLYPRINTK,
 	.pdrv = &bfin_earlyprintk_driver,
 	.pdrv = &bfin_earlyprintk_driver,
 	.requested_id = EARLY_PLATFORM_ID_UNSET,
 	.requested_id = EARLY_PLATFORM_ID_UNSET,

+ 2 - 9
drivers/tty/serial/clps711x.c

@@ -438,8 +438,7 @@ static int uart_clps711x_probe(struct platform_device *pdev)
 	s->uart_clk = devm_clk_get(&pdev->dev, "uart");
 	s->uart_clk = devm_clk_get(&pdev->dev, "uart");
 	if (IS_ERR(s->uart_clk)) {
 	if (IS_ERR(s->uart_clk)) {
 		dev_err(&pdev->dev, "Can't get UART clocks\n");
 		dev_err(&pdev->dev, "Can't get UART clocks\n");
-		ret = PTR_ERR(s->uart_clk);
-		goto err_out;
+		return PTR_ERR(s->uart_clk);
 	}
 	}
 
 
 	s->uart.owner		= THIS_MODULE;
 	s->uart.owner		= THIS_MODULE;
@@ -461,7 +460,7 @@ static int uart_clps711x_probe(struct platform_device *pdev)
 	if (ret) {
 	if (ret) {
 		dev_err(&pdev->dev, "Registering UART driver failed\n");
 		dev_err(&pdev->dev, "Registering UART driver failed\n");
 		devm_clk_put(&pdev->dev, s->uart_clk);
 		devm_clk_put(&pdev->dev, s->uart_clk);
-		goto err_out;
+		return ret;
 	}
 	}
 
 
 	for (i = 0; i < UART_CLPS711X_NR; i++) {
 	for (i = 0; i < UART_CLPS711X_NR; i++) {
@@ -478,11 +477,6 @@ static int uart_clps711x_probe(struct platform_device *pdev)
 	}
 	}
 
 
 	return 0;
 	return 0;
-
-err_out:
-	platform_set_drvdata(pdev, NULL);
-
-	return ret;
 }
 }
 
 
 static int uart_clps711x_remove(struct platform_device *pdev)
 static int uart_clps711x_remove(struct platform_device *pdev)
@@ -495,7 +489,6 @@ static int uart_clps711x_remove(struct platform_device *pdev)
 
 
 	devm_clk_put(&pdev->dev, s->uart_clk);
 	devm_clk_put(&pdev->dev, s->uart_clk);
 	uart_unregister_driver(&s->uart);
 	uart_unregister_driver(&s->uart);
-	platform_set_drvdata(pdev, NULL);
 
 
 	return 0;
 	return 0;
 }
 }

+ 26 - 2
drivers/tty/serial/cpm_uart/cpm_uart_core.c

@@ -1213,8 +1213,32 @@ static int cpm_uart_init_port(struct device_node *np,
 		goto out_pram;
 		goto out_pram;
 	}
 	}
 
 
-	for (i = 0; i < NUM_GPIOS; i++)
-		pinfo->gpios[i] = of_get_gpio(np, i);
+	for (i = 0; i < NUM_GPIOS; i++) {
+		int gpio;
+
+		pinfo->gpios[i] = -1;
+
+		gpio = of_get_gpio(np, i);
+
+		if (gpio_is_valid(gpio)) {
+			ret = gpio_request(gpio, "cpm_uart");
+			if (ret) {
+				pr_err("can't request gpio #%d: %d\n", i, ret);
+				continue;
+			}
+			if (i == GPIO_RTS || i == GPIO_DTR)
+				ret = gpio_direction_output(gpio, 0);
+			else
+				ret = gpio_direction_input(gpio);
+			if (ret) {
+				pr_err("can't set direction for gpio #%d: %d\n",
+					i, ret);
+				gpio_free(gpio);
+				continue;
+			}
+			pinfo->gpios[i] = gpio;
+		}
+	}
 
 
 #ifdef CONFIG_PPC_EARLY_DEBUG_CPM
 #ifdef CONFIG_PPC_EARLY_DEBUG_CPM
 	udbg_putc = NULL;
 	udbg_putc = NULL;

+ 16 - 13
drivers/tty/serial/efm32-uart.c

@@ -268,10 +268,10 @@ static irqreturn_t efm32_uart_rxirq(int irq, void *data)
 		handled = IRQ_HANDLED;
 		handled = IRQ_HANDLED;
 	}
 	}
 
 
-	tty_flip_buffer_push(tport);
-
 	spin_unlock(&port->lock);
 	spin_unlock(&port->lock);
 
 
+	tty_flip_buffer_push(tport);
+
 	return handled;
 	return handled;
 }
 }
 
 
@@ -698,6 +698,7 @@ static int efm32_uart_probe(struct platform_device *pdev)
 {
 {
 	struct efm32_uart_port *efm_port;
 	struct efm32_uart_port *efm_port;
 	struct resource *res;
 	struct resource *res;
+	unsigned int line;
 	int ret;
 	int ret;
 
 
 	efm_port = kzalloc(sizeof(*efm_port), GFP_KERNEL);
 	efm_port = kzalloc(sizeof(*efm_port), GFP_KERNEL);
@@ -750,18 +751,21 @@ static int efm32_uart_probe(struct platform_device *pdev)
 
 
 		if (pdata)
 		if (pdata)
 			efm_port->pdata = *pdata;
 			efm_port->pdata = *pdata;
-	}
+	} else if (ret < 0)
+		goto err_probe_dt;
+
+	line = efm_port->port.line;
 
 
-	if (efm_port->port.line >= 0 &&
-			efm_port->port.line < ARRAY_SIZE(efm32_uart_ports))
-		efm32_uart_ports[efm_port->port.line] = efm_port;
+	if (line >= 0 && line < ARRAY_SIZE(efm32_uart_ports))
+		efm32_uart_ports[line] = efm_port;
 
 
 	ret = uart_add_one_port(&efm32_uart_reg, &efm_port->port);
 	ret = uart_add_one_port(&efm32_uart_reg, &efm_port->port);
 	if (ret) {
 	if (ret) {
 		dev_dbg(&pdev->dev, "failed to add port: %d\n", ret);
 		dev_dbg(&pdev->dev, "failed to add port: %d\n", ret);
 
 
-		if (pdev->id >= 0 && pdev->id < ARRAY_SIZE(efm32_uart_ports))
-			efm32_uart_ports[pdev->id] = NULL;
+		if (line >= 0 && line < ARRAY_SIZE(efm32_uart_ports))
+			efm32_uart_ports[line] = NULL;
+err_probe_dt:
 err_get_rxirq:
 err_get_rxirq:
 err_too_small:
 err_too_small:
 err_get_base:
 err_get_base:
@@ -777,20 +781,19 @@ err_get_base:
 static int efm32_uart_remove(struct platform_device *pdev)
 static int efm32_uart_remove(struct platform_device *pdev)
 {
 {
 	struct efm32_uart_port *efm_port = platform_get_drvdata(pdev);
 	struct efm32_uart_port *efm_port = platform_get_drvdata(pdev);
-
-	platform_set_drvdata(pdev, NULL);
+	unsigned int line = efm_port->port.line;
 
 
 	uart_remove_one_port(&efm32_uart_reg, &efm_port->port);
 	uart_remove_one_port(&efm32_uart_reg, &efm_port->port);
 
 
-	if (pdev->id >= 0 && pdev->id < ARRAY_SIZE(efm32_uart_ports))
-		efm32_uart_ports[pdev->id] = NULL;
+	if (line >= 0 && line < ARRAY_SIZE(efm32_uart_ports))
+		efm32_uart_ports[line] = NULL;
 
 
 	kfree(efm_port);
 	kfree(efm_port);
 
 
 	return 0;
 	return 0;
 }
 }
 
 
-static struct of_device_id efm32_uart_dt_ids[] = {
+static const struct of_device_id efm32_uart_dt_ids[] = {
 	{
 	{
 		.compatible = "efm32,uart",
 		.compatible = "efm32,uart",
 	}, {
 	}, {

+ 6 - 1
drivers/tty/serial/fsl_lpuart.c

@@ -342,8 +342,10 @@ static void lpuart_break_ctl(struct uart_port *port, int break_state)
 static void lpuart_setup_watermark(struct lpuart_port *sport)
 static void lpuart_setup_watermark(struct lpuart_port *sport)
 {
 {
 	unsigned char val, cr2;
 	unsigned char val, cr2;
+	unsigned char cr2_saved;
 
 
 	cr2 = readb(sport->port.membase + UARTCR2);
 	cr2 = readb(sport->port.membase + UARTCR2);
+	cr2_saved = cr2;
 	cr2 &= ~(UARTCR2_TIE | UARTCR2_TCIE | UARTCR2_TE |
 	cr2 &= ~(UARTCR2_TIE | UARTCR2_TCIE | UARTCR2_TE |
 			UARTCR2_RIE | UARTCR2_RE);
 			UARTCR2_RIE | UARTCR2_RE);
 	writeb(cr2, sport->port.membase + UARTCR2);
 	writeb(cr2, sport->port.membase + UARTCR2);
@@ -366,6 +368,9 @@ static void lpuart_setup_watermark(struct lpuart_port *sport)
 
 
 	writeb(2, sport->port.membase + UARTTWFIFO);
 	writeb(2, sport->port.membase + UARTTWFIFO);
 	writeb(1, sport->port.membase + UARTRWFIFO);
 	writeb(1, sport->port.membase + UARTRWFIFO);
+
+	/* Restore cr2 */
+	writeb(cr2_saved, sport->port.membase + UARTCR2);
 }
 }
 
 
 static int lpuart_startup(struct uart_port *port)
 static int lpuart_startup(struct uart_port *port)
@@ -858,7 +863,7 @@ static int __init lpuart_serial_init(void)
 	if (ret)
 	if (ret)
 		uart_unregister_driver(&lpuart_reg);
 		uart_unregister_driver(&lpuart_reg);
 
 
-	return 0;
+	return ret;
 }
 }
 
 
 static void __exit lpuart_serial_exit(void)
 static void __exit lpuart_serial_exit(void)

+ 56 - 47
drivers/tty/serial/icom.c

@@ -105,7 +105,7 @@ static const struct pci_device_id icom_pci_table[] = {
 	{}
 	{}
 };
 };
 
 
-struct lookup_proc_table start_proc[4] = {
+static struct lookup_proc_table start_proc[4] = {
 	{NULL, ICOM_CONTROL_START_A},
 	{NULL, ICOM_CONTROL_START_A},
 	{NULL, ICOM_CONTROL_START_B},
 	{NULL, ICOM_CONTROL_START_B},
 	{NULL, ICOM_CONTROL_START_C},
 	{NULL, ICOM_CONTROL_START_C},
@@ -113,14 +113,14 @@ struct lookup_proc_table start_proc[4] = {
 };
 };
 
 
 
 
-struct lookup_proc_table stop_proc[4] = {
+static struct lookup_proc_table stop_proc[4] = {
 	{NULL, ICOM_CONTROL_STOP_A},
 	{NULL, ICOM_CONTROL_STOP_A},
 	{NULL, ICOM_CONTROL_STOP_B},
 	{NULL, ICOM_CONTROL_STOP_B},
 	{NULL, ICOM_CONTROL_STOP_C},
 	{NULL, ICOM_CONTROL_STOP_C},
 	{NULL, ICOM_CONTROL_STOP_D}
 	{NULL, ICOM_CONTROL_STOP_D}
 };
 };
 
 
-struct lookup_int_table int_mask_tbl[4] = {
+static struct lookup_int_table int_mask_tbl[4] = {
 	{NULL, ICOM_INT_MASK_PRC_A},
 	{NULL, ICOM_INT_MASK_PRC_A},
 	{NULL, ICOM_INT_MASK_PRC_B},
 	{NULL, ICOM_INT_MASK_PRC_B},
 	{NULL, ICOM_INT_MASK_PRC_C},
 	{NULL, ICOM_INT_MASK_PRC_C},
@@ -297,25 +297,25 @@ static void stop_processor(struct icom_port *icom_port)
 	spin_lock_irqsave(&icom_lock, flags);
 	spin_lock_irqsave(&icom_lock, flags);
 
 
 	port = icom_port->port;
 	port = icom_port->port;
+	if (port >= ARRAY_SIZE(stop_proc)) {
+		dev_err(&icom_port->adapter->pci_dev->dev,
+			"Invalid port assignment\n");
+		goto unlock;
+	}
+
 	if (port == 0 || port == 1)
 	if (port == 0 || port == 1)
 		stop_proc[port].global_control_reg = &icom_port->global_reg->control;
 		stop_proc[port].global_control_reg = &icom_port->global_reg->control;
 	else
 	else
 		stop_proc[port].global_control_reg = &icom_port->global_reg->control_2;
 		stop_proc[port].global_control_reg = &icom_port->global_reg->control_2;
 
 
+	temp = readl(stop_proc[port].global_control_reg);
+	temp = (temp & ~start_proc[port].processor_id) | stop_proc[port].processor_id;
+	writel(temp, stop_proc[port].global_control_reg);
 
 
-	if (port < 4) {
-		temp = readl(stop_proc[port].global_control_reg);
-		temp =
-			(temp & ~start_proc[port].processor_id) | stop_proc[port].processor_id;
-		writel(temp, stop_proc[port].global_control_reg);
-
-		/* write flush */
-		readl(stop_proc[port].global_control_reg);
-	} else {
-		dev_err(&icom_port->adapter->pci_dev->dev,
-                        "Invalid port assignment\n");
-	}
+	/* write flush */
+	readl(stop_proc[port].global_control_reg);
 
 
+unlock:
 	spin_unlock_irqrestore(&icom_lock, flags);
 	spin_unlock_irqrestore(&icom_lock, flags);
 }
 }
 
 
@@ -328,23 +328,25 @@ static void start_processor(struct icom_port *icom_port)
 	spin_lock_irqsave(&icom_lock, flags);
 	spin_lock_irqsave(&icom_lock, flags);
 
 
 	port = icom_port->port;
 	port = icom_port->port;
+	if (port >= ARRAY_SIZE(start_proc)) {
+		dev_err(&icom_port->adapter->pci_dev->dev,
+			"Invalid port assignment\n");
+		goto unlock;
+	}
+
 	if (port == 0 || port == 1)
 	if (port == 0 || port == 1)
 		start_proc[port].global_control_reg = &icom_port->global_reg->control;
 		start_proc[port].global_control_reg = &icom_port->global_reg->control;
 	else
 	else
 		start_proc[port].global_control_reg = &icom_port->global_reg->control_2;
 		start_proc[port].global_control_reg = &icom_port->global_reg->control_2;
-	if (port < 4) {
-		temp = readl(start_proc[port].global_control_reg);
-		temp =
-			(temp & ~stop_proc[port].processor_id) | start_proc[port].processor_id;
-		writel(temp, start_proc[port].global_control_reg);
 
 
-		/* write flush */
-		readl(start_proc[port].global_control_reg);
-	} else {
-		dev_err(&icom_port->adapter->pci_dev->dev,
-                        "Invalid port assignment\n");
-	}
+	temp = readl(start_proc[port].global_control_reg);
+	temp = (temp & ~stop_proc[port].processor_id) | start_proc[port].processor_id;
+	writel(temp, start_proc[port].global_control_reg);
+
+	/* write flush */
+	readl(start_proc[port].global_control_reg);
 
 
+unlock:
 	spin_unlock_irqrestore(&icom_lock, flags);
 	spin_unlock_irqrestore(&icom_lock, flags);
 }
 }
 
 
@@ -557,6 +559,12 @@ static int startup(struct icom_port *icom_port)
 	 */
 	 */
 	spin_lock_irqsave(&icom_lock, flags);
 	spin_lock_irqsave(&icom_lock, flags);
 	port = icom_port->port;
 	port = icom_port->port;
+	if (port >= ARRAY_SIZE(int_mask_tbl)) {
+		dev_err(&icom_port->adapter->pci_dev->dev,
+			"Invalid port assignment\n");
+		goto unlock;
+	}
+
 	if (port == 0 || port == 1)
 	if (port == 0 || port == 1)
 		int_mask_tbl[port].global_int_mask = &icom_port->global_reg->int_mask;
 		int_mask_tbl[port].global_int_mask = &icom_port->global_reg->int_mask;
 	else
 	else
@@ -566,17 +574,14 @@ static int startup(struct icom_port *icom_port)
 		writew(0x00FF, icom_port->int_reg);
 		writew(0x00FF, icom_port->int_reg);
 	else
 	else
 		writew(0x3F00, icom_port->int_reg);
 		writew(0x3F00, icom_port->int_reg);
-	if (port < 4) {
-		temp = readl(int_mask_tbl[port].global_int_mask);
-		writel(temp & ~int_mask_tbl[port].processor_id, int_mask_tbl[port].global_int_mask);
 
 
-		/* write flush */
-		readl(int_mask_tbl[port].global_int_mask);
-	} else {
-		dev_err(&icom_port->adapter->pci_dev->dev,
-                        "Invalid port assignment\n");
-	}
+	temp = readl(int_mask_tbl[port].global_int_mask);
+	writel(temp & ~int_mask_tbl[port].processor_id, int_mask_tbl[port].global_int_mask);
 
 
+	/* write flush */
+	readl(int_mask_tbl[port].global_int_mask);
+
+unlock:
 	spin_unlock_irqrestore(&icom_lock, flags);
 	spin_unlock_irqrestore(&icom_lock, flags);
 	return 0;
 	return 0;
 }
 }
@@ -595,21 +600,23 @@ static void shutdown(struct icom_port *icom_port)
 	 * disable all interrupts
 	 * disable all interrupts
 	 */
 	 */
 	port = icom_port->port;
 	port = icom_port->port;
+	if (port >= ARRAY_SIZE(int_mask_tbl)) {
+		dev_err(&icom_port->adapter->pci_dev->dev,
+			"Invalid port assignment\n");
+		goto unlock;
+	}
 	if (port == 0 || port == 1)
 	if (port == 0 || port == 1)
 		int_mask_tbl[port].global_int_mask = &icom_port->global_reg->int_mask;
 		int_mask_tbl[port].global_int_mask = &icom_port->global_reg->int_mask;
 	else
 	else
 		int_mask_tbl[port].global_int_mask = &icom_port->global_reg->int_mask_2;
 		int_mask_tbl[port].global_int_mask = &icom_port->global_reg->int_mask_2;
 
 
-	if (port < 4) {
-		temp = readl(int_mask_tbl[port].global_int_mask);
-		writel(temp | int_mask_tbl[port].processor_id, int_mask_tbl[port].global_int_mask);
+	temp = readl(int_mask_tbl[port].global_int_mask);
+	writel(temp | int_mask_tbl[port].processor_id, int_mask_tbl[port].global_int_mask);
 
 
-		/* write flush */
-		readl(int_mask_tbl[port].global_int_mask);
-	} else {
-		dev_err(&icom_port->adapter->pci_dev->dev,
-                        "Invalid port assignment\n");
-	}
+	/* write flush */
+	readl(int_mask_tbl[port].global_int_mask);
+
+unlock:
 	spin_unlock_irqrestore(&icom_lock, flags);
 	spin_unlock_irqrestore(&icom_lock, flags);
 
 
 	/*
 	/*
@@ -834,7 +841,10 @@ ignore_char:
 		status = cpu_to_le16(icom_port->statStg->rcv[rcv_buff].flags);
 		status = cpu_to_le16(icom_port->statStg->rcv[rcv_buff].flags);
 	}
 	}
 	icom_port->next_rcv = rcv_buff;
 	icom_port->next_rcv = rcv_buff;
+
+	spin_unlock(&icom_port->uart_port.lock);
 	tty_flip_buffer_push(port);
 	tty_flip_buffer_push(port);
+	spin_lock(&icom_port->uart_port.lock);
 }
 }
 
 
 static void process_interrupt(u16 port_int_reg,
 static void process_interrupt(u16 port_int_reg,
@@ -1087,8 +1097,7 @@ static void icom_close(struct uart_port *port)
 
 
 	/* stop receiver */
 	/* stop receiver */
 	cmdReg = readb(&ICOM_PORT->dram->CmdReg);
 	cmdReg = readb(&ICOM_PORT->dram->CmdReg);
-	writeb(cmdReg & (unsigned char) ~CMD_RCV_ENABLE,
-	       &ICOM_PORT->dram->CmdReg);
+	writeb(cmdReg & ~CMD_RCV_ENABLE, &ICOM_PORT->dram->CmdReg);
 
 
 	shutdown(ICOM_PORT);
 	shutdown(ICOM_PORT);
 
 
@@ -1567,7 +1576,7 @@ static int icom_probe(struct pci_dev *dev,
 			icom_port->uart_port.type = PORT_ICOM;
 			icom_port->uart_port.type = PORT_ICOM;
 			icom_port->uart_port.iotype = UPIO_MEM;
 			icom_port->uart_port.iotype = UPIO_MEM;
 			icom_port->uart_port.membase =
 			icom_port->uart_port.membase =
-					       (char *) icom_adapter->base_addr_pci;
+				(unsigned char __iomem *)icom_adapter->base_addr_pci;
 			icom_port->uart_port.fifosize = 16;
 			icom_port->uart_port.fifosize = 16;
 			icom_port->uart_port.ops = &icom_ops;
 			icom_port->uart_port.ops = &icom_ops;
 			icom_port->uart_port.line =
 			icom_port->uart_port.line =

+ 1 - 1
drivers/tty/serial/ifx6x60.c

@@ -1008,7 +1008,7 @@ static int ifx_spi_spi_probe(struct spi_device *spi)
 		return -ENODEV;
 		return -ENODEV;
 	}
 	}
 
 
-	pl_data = (struct ifx_modem_platform_data *)spi->dev.platform_data;
+	pl_data = (struct ifx_modem_platform_data *)dev_get_platdata(&spi->dev);
 	if (!pl_data) {
 	if (!pl_data) {
 		dev_err(&spi->dev, "missing platform data!");
 		dev_err(&spi->dev, "missing platform data!");
 		return -ENODEV;
 		return -ENODEV;

+ 471 - 49
drivers/tty/serial/imx.c

@@ -47,11 +47,12 @@
 #include <linux/slab.h>
 #include <linux/slab.h>
 #include <linux/of.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
 #include <linux/of_device.h>
-#include <linux/pinctrl/consumer.h>
 #include <linux/io.h>
 #include <linux/io.h>
+#include <linux/dma-mapping.h>
 
 
 #include <asm/irq.h>
 #include <asm/irq.h>
 #include <linux/platform_data/serial-imx.h>
 #include <linux/platform_data/serial-imx.h>
+#include <linux/platform_data/dma-imx.h>
 
 
 /* Register definitions */
 /* Register definitions */
 #define URXD0 0x0  /* Receiver Register */
 #define URXD0 0x0  /* Receiver Register */
@@ -83,6 +84,7 @@
 #define UCR1_ADBR	(1<<14) /* Auto detect baud rate */
 #define UCR1_ADBR	(1<<14) /* Auto detect baud rate */
 #define UCR1_TRDYEN	(1<<13) /* Transmitter ready interrupt enable */
 #define UCR1_TRDYEN	(1<<13) /* Transmitter ready interrupt enable */
 #define UCR1_IDEN	(1<<12) /* Idle condition interrupt */
 #define UCR1_IDEN	(1<<12) /* Idle condition interrupt */
+#define UCR1_ICD_REG(x) (((x) & 3) << 10) /* idle condition detect */
 #define UCR1_RRDYEN	(1<<9)	/* Recv ready interrupt enable */
 #define UCR1_RRDYEN	(1<<9)	/* Recv ready interrupt enable */
 #define UCR1_RDMAEN	(1<<8)	/* Recv ready DMA enable */
 #define UCR1_RDMAEN	(1<<8)	/* Recv ready DMA enable */
 #define UCR1_IREN	(1<<7)	/* Infrared interface enable */
 #define UCR1_IREN	(1<<7)	/* Infrared interface enable */
@@ -91,6 +93,7 @@
 #define UCR1_SNDBRK	(1<<4)	/* Send break */
 #define UCR1_SNDBRK	(1<<4)	/* Send break */
 #define UCR1_TDMAEN	(1<<3)	/* Transmitter ready DMA enable */
 #define UCR1_TDMAEN	(1<<3)	/* Transmitter ready DMA enable */
 #define IMX1_UCR1_UARTCLKEN (1<<2) /* UART clock enabled, i.mx1 only */
 #define IMX1_UCR1_UARTCLKEN (1<<2) /* UART clock enabled, i.mx1 only */
+#define UCR1_ATDMAEN    (1<<2)  /* Aging DMA Timer Enable */
 #define UCR1_DOZE	(1<<1)	/* Doze */
 #define UCR1_DOZE	(1<<1)	/* Doze */
 #define UCR1_UARTEN	(1<<0)	/* UART enabled */
 #define UCR1_UARTEN	(1<<0)	/* UART enabled */
 #define UCR2_ESCI	(1<<15)	/* Escape seq interrupt enable */
 #define UCR2_ESCI	(1<<15)	/* Escape seq interrupt enable */
@@ -126,6 +129,7 @@
 #define UCR4_ENIRI	(1<<8)	/* Serial infrared interrupt enable */
 #define UCR4_ENIRI	(1<<8)	/* Serial infrared interrupt enable */
 #define UCR4_WKEN	(1<<7)	/* Wake interrupt enable */
 #define UCR4_WKEN	(1<<7)	/* Wake interrupt enable */
 #define UCR4_REF16	(1<<6)	/* Ref freq 16 MHz */
 #define UCR4_REF16	(1<<6)	/* Ref freq 16 MHz */
+#define UCR4_IDDMAEN    (1<<6)  /* DMA IDLE Condition Detected */
 #define UCR4_IRSC	(1<<5)	/* IR special case */
 #define UCR4_IRSC	(1<<5)	/* IR special case */
 #define UCR4_TCEN	(1<<3)	/* Transmit complete interrupt enable */
 #define UCR4_TCEN	(1<<3)	/* Transmit complete interrupt enable */
 #define UCR4_BKEN	(1<<2)	/* Break condition interrupt enable */
 #define UCR4_BKEN	(1<<2)	/* Break condition interrupt enable */
@@ -187,6 +191,7 @@
 enum imx_uart_type {
 enum imx_uart_type {
 	IMX1_UART,
 	IMX1_UART,
 	IMX21_UART,
 	IMX21_UART,
+	IMX6Q_UART,
 };
 };
 
 
 /* device type dependent stuff */
 /* device type dependent stuff */
@@ -209,6 +214,19 @@ struct imx_port {
 	struct clk		*clk_ipg;
 	struct clk		*clk_ipg;
 	struct clk		*clk_per;
 	struct clk		*clk_per;
 	const struct imx_uart_data *devdata;
 	const struct imx_uart_data *devdata;
+
+	/* DMA fields */
+	unsigned int		dma_is_inited:1;
+	unsigned int		dma_is_enabled:1;
+	unsigned int		dma_is_rxing:1;
+	unsigned int		dma_is_txing:1;
+	struct dma_chan		*dma_chan_rx, *dma_chan_tx;
+	struct scatterlist	rx_sgl, tx_sgl[2];
+	void			*rx_buf;
+	unsigned int		rx_bytes, tx_bytes;
+	struct work_struct	tsk_dma_rx, tsk_dma_tx;
+	unsigned int		dma_tx_nents;
+	wait_queue_head_t	dma_wait;
 };
 };
 
 
 struct imx_port_ucrs {
 struct imx_port_ucrs {
@@ -232,6 +250,10 @@ static struct imx_uart_data imx_uart_devdata[] = {
 		.uts_reg = IMX21_UTS,
 		.uts_reg = IMX21_UTS,
 		.devtype = IMX21_UART,
 		.devtype = IMX21_UART,
 	},
 	},
+	[IMX6Q_UART] = {
+		.uts_reg = IMX21_UTS,
+		.devtype = IMX6Q_UART,
+	},
 };
 };
 
 
 static struct platform_device_id imx_uart_devtype[] = {
 static struct platform_device_id imx_uart_devtype[] = {
@@ -241,6 +263,9 @@ static struct platform_device_id imx_uart_devtype[] = {
 	}, {
 	}, {
 		.name = "imx21-uart",
 		.name = "imx21-uart",
 		.driver_data = (kernel_ulong_t) &imx_uart_devdata[IMX21_UART],
 		.driver_data = (kernel_ulong_t) &imx_uart_devdata[IMX21_UART],
+	}, {
+		.name = "imx6q-uart",
+		.driver_data = (kernel_ulong_t) &imx_uart_devdata[IMX6Q_UART],
 	}, {
 	}, {
 		/* sentinel */
 		/* sentinel */
 	}
 	}
@@ -248,6 +273,7 @@ static struct platform_device_id imx_uart_devtype[] = {
 MODULE_DEVICE_TABLE(platform, imx_uart_devtype);
 MODULE_DEVICE_TABLE(platform, imx_uart_devtype);
 
 
 static struct of_device_id imx_uart_dt_ids[] = {
 static struct of_device_id imx_uart_dt_ids[] = {
+	{ .compatible = "fsl,imx6q-uart", .data = &imx_uart_devdata[IMX6Q_UART], },
 	{ .compatible = "fsl,imx1-uart", .data = &imx_uart_devdata[IMX1_UART], },
 	{ .compatible = "fsl,imx1-uart", .data = &imx_uart_devdata[IMX1_UART], },
 	{ .compatible = "fsl,imx21-uart", .data = &imx_uart_devdata[IMX21_UART], },
 	{ .compatible = "fsl,imx21-uart", .data = &imx_uart_devdata[IMX21_UART], },
 	{ /* sentinel */ }
 	{ /* sentinel */ }
@@ -269,6 +295,10 @@ static inline int is_imx21_uart(struct imx_port *sport)
 	return sport->devdata->devtype == IMX21_UART;
 	return sport->devdata->devtype == IMX21_UART;
 }
 }
 
 
+static inline int is_imx6q_uart(struct imx_port *sport)
+{
+	return sport->devdata->devtype == IMX6Q_UART;
+}
 /*
 /*
  * Save and restore functions for UCR1, UCR2 and UCR3 registers
  * Save and restore functions for UCR1, UCR2 and UCR3 registers
  */
  */
@@ -387,6 +417,13 @@ static void imx_stop_tx(struct uart_port *port)
 		return;
 		return;
 	}
 	}
 
 
+	/*
+	 * We are maybe in the SMP context, so if the DMA TX thread is running
+	 * on other cpu, we have to wait for it to finish.
+	 */
+	if (sport->dma_is_enabled && sport->dma_is_txing)
+		return;
+
 	temp = readl(sport->port.membase + UCR1);
 	temp = readl(sport->port.membase + UCR1);
 	writel(temp & ~UCR1_TXMPTYEN, sport->port.membase + UCR1);
 	writel(temp & ~UCR1_TXMPTYEN, sport->port.membase + UCR1);
 }
 }
@@ -399,6 +436,13 @@ static void imx_stop_rx(struct uart_port *port)
 	struct imx_port *sport = (struct imx_port *)port;
 	struct imx_port *sport = (struct imx_port *)port;
 	unsigned long temp;
 	unsigned long temp;
 
 
+	/*
+	 * We are maybe in the SMP context, so if the DMA TX thread is running
+	 * on other cpu, we have to wait for it to finish.
+	 */
+	if (sport->dma_is_enabled && sport->dma_is_rxing)
+		return;
+
 	temp = readl(sport->port.membase + UCR2);
 	temp = readl(sport->port.membase + UCR2);
 	writel(temp & ~UCR2_RXEN, sport->port.membase + UCR2);
 	writel(temp & ~UCR2_RXEN, sport->port.membase + UCR2);
 }
 }
@@ -434,6 +478,95 @@ static inline void imx_transmit_buffer(struct imx_port *sport)
 		imx_stop_tx(&sport->port);
 		imx_stop_tx(&sport->port);
 }
 }
 
 
+static void dma_tx_callback(void *data)
+{
+	struct imx_port *sport = data;
+	struct scatterlist *sgl = &sport->tx_sgl[0];
+	struct circ_buf *xmit = &sport->port.state->xmit;
+	unsigned long flags;
+
+	dma_unmap_sg(sport->port.dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE);
+
+	sport->dma_is_txing = 0;
+
+	/* update the stat */
+	spin_lock_irqsave(&sport->port.lock, flags);
+	xmit->tail = (xmit->tail + sport->tx_bytes) & (UART_XMIT_SIZE - 1);
+	sport->port.icount.tx += sport->tx_bytes;
+	spin_unlock_irqrestore(&sport->port.lock, flags);
+
+	dev_dbg(sport->port.dev, "we finish the TX DMA.\n");
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(&sport->port);
+
+	if (waitqueue_active(&sport->dma_wait)) {
+		wake_up(&sport->dma_wait);
+		dev_dbg(sport->port.dev, "exit in %s.\n", __func__);
+		return;
+	}
+
+	schedule_work(&sport->tsk_dma_tx);
+}
+
+static void dma_tx_work(struct work_struct *w)
+{
+	struct imx_port *sport = container_of(w, struct imx_port, tsk_dma_tx);
+	struct circ_buf *xmit = &sport->port.state->xmit;
+	struct scatterlist *sgl = sport->tx_sgl;
+	struct dma_async_tx_descriptor *desc;
+	struct dma_chan	*chan = sport->dma_chan_tx;
+	struct device *dev = sport->port.dev;
+	enum dma_status status;
+	unsigned long flags;
+	int ret;
+
+	status = chan->device->device_tx_status(chan, (dma_cookie_t)0, NULL);
+	if (DMA_IN_PROGRESS == status)
+		return;
+
+	spin_lock_irqsave(&sport->port.lock, flags);
+	sport->tx_bytes = uart_circ_chars_pending(xmit);
+	if (sport->tx_bytes == 0) {
+		spin_unlock_irqrestore(&sport->port.lock, flags);
+		return;
+	}
+
+	if (xmit->tail > xmit->head) {
+		sport->dma_tx_nents = 2;
+		sg_init_table(sgl, 2);
+		sg_set_buf(sgl, xmit->buf + xmit->tail,
+				UART_XMIT_SIZE - xmit->tail);
+		sg_set_buf(sgl + 1, xmit->buf, xmit->head);
+	} else {
+		sport->dma_tx_nents = 1;
+		sg_init_one(sgl, xmit->buf + xmit->tail, sport->tx_bytes);
+	}
+	spin_unlock_irqrestore(&sport->port.lock, flags);
+
+	ret = dma_map_sg(dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE);
+	if (ret == 0) {
+		dev_err(dev, "DMA mapping error for TX.\n");
+		return;
+	}
+	desc = dmaengine_prep_slave_sg(chan, sgl, sport->dma_tx_nents,
+					DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT);
+	if (!desc) {
+		dev_err(dev, "We cannot prepare for the TX slave dma!\n");
+		return;
+	}
+	desc->callback = dma_tx_callback;
+	desc->callback_param = sport;
+
+	dev_dbg(dev, "TX: prepare to send %lu bytes by DMA.\n",
+			uart_circ_chars_pending(xmit));
+	/* fire it */
+	sport->dma_is_txing = 1;
+	dmaengine_submit(desc);
+	dma_async_issue_pending(chan);
+	return;
+}
+
 /*
 /*
  * interrupts disabled on entry
  * interrupts disabled on entry
  */
  */
@@ -460,8 +593,10 @@ static void imx_start_tx(struct uart_port *port)
 	temp |= UCR4_OREN;
 	temp |= UCR4_OREN;
 	writel(temp, sport->port.membase + UCR4);
 	writel(temp, sport->port.membase + UCR4);
 
 
-	temp = readl(sport->port.membase + UCR1);
-	writel(temp | UCR1_TXMPTYEN, sport->port.membase + UCR1);
+	if (!sport->dma_is_enabled) {
+		temp = readl(sport->port.membase + UCR1);
+		writel(temp | UCR1_TXMPTYEN, sport->port.membase + UCR1);
+	}
 
 
 	if (USE_IRDA(sport)) {
 	if (USE_IRDA(sport)) {
 		temp = readl(sport->port.membase + UCR1);
 		temp = readl(sport->port.membase + UCR1);
@@ -473,6 +608,15 @@ static void imx_start_tx(struct uart_port *port)
 		writel(temp, sport->port.membase + UCR4);
 		writel(temp, sport->port.membase + UCR4);
 	}
 	}
 
 
+	if (sport->dma_is_enabled) {
+		/*
+		 * We may in the interrupt context, so arise a work_struct to
+		 * do the real job.
+		 */
+		schedule_work(&sport->tsk_dma_tx);
+		return;
+	}
+
 	if (readl(sport->port.membase + uts_reg(sport)) & UTS_TXEMPTY)
 	if (readl(sport->port.membase + uts_reg(sport)) & UTS_TXEMPTY)
 		imx_transmit_buffer(sport);
 		imx_transmit_buffer(sport);
 }
 }
@@ -588,6 +732,28 @@ out:
 	return IRQ_HANDLED;
 	return IRQ_HANDLED;
 }
 }
 
 
+/*
+ * If the RXFIFO is filled with some data, and then we
+ * arise a DMA operation to receive them.
+ */
+static void imx_dma_rxint(struct imx_port *sport)
+{
+	unsigned long temp;
+
+	temp = readl(sport->port.membase + USR2);
+	if ((temp & USR2_RDR) && !sport->dma_is_rxing) {
+		sport->dma_is_rxing = 1;
+
+		/* disable the `Recerver Ready Interrrupt` */
+		temp = readl(sport->port.membase + UCR1);
+		temp &= ~(UCR1_RRDYEN);
+		writel(temp, sport->port.membase + UCR1);
+
+		/* tell the DMA to receive the data. */
+		schedule_work(&sport->tsk_dma_rx);
+	}
+}
+
 static irqreturn_t imx_int(int irq, void *dev_id)
 static irqreturn_t imx_int(int irq, void *dev_id)
 {
 {
 	struct imx_port *sport = dev_id;
 	struct imx_port *sport = dev_id;
@@ -596,8 +762,12 @@ static irqreturn_t imx_int(int irq, void *dev_id)
 
 
 	sts = readl(sport->port.membase + USR1);
 	sts = readl(sport->port.membase + USR1);
 
 
-	if (sts & USR1_RRDY)
-		imx_rxint(irq, dev_id);
+	if (sts & USR1_RRDY) {
+		if (sport->dma_is_enabled)
+			imx_dma_rxint(sport);
+		else
+			imx_rxint(irq, dev_id);
+	}
 
 
 	if (sts & USR1_TRDY &&
 	if (sts & USR1_TRDY &&
 			readl(sport->port.membase + UCR1) & UCR1_TXMPTYEN)
 			readl(sport->port.membase + UCR1) & UCR1_TXMPTYEN)
@@ -654,7 +824,8 @@ static void imx_set_mctrl(struct uart_port *port, unsigned int mctrl)
 	temp = readl(sport->port.membase + UCR2) & ~UCR2_CTS;
 	temp = readl(sport->port.membase + UCR2) & ~UCR2_CTS;
 
 
 	if (mctrl & TIOCM_RTS)
 	if (mctrl & TIOCM_RTS)
-		temp |= UCR2_CTS;
+		if (!sport->dma_is_enabled)
+			temp |= UCR2_CTS;
 
 
 	writel(temp, sport->port.membase + UCR2);
 	writel(temp, sport->port.membase + UCR2);
 }
 }
@@ -693,6 +864,226 @@ static int imx_setup_ufcr(struct imx_port *sport, unsigned int mode)
 	return 0;
 	return 0;
 }
 }
 
 
+#define RX_BUF_SIZE	(PAGE_SIZE)
+static int start_rx_dma(struct imx_port *sport);
+static void dma_rx_work(struct work_struct *w)
+{
+	struct imx_port *sport = container_of(w, struct imx_port, tsk_dma_rx);
+	struct tty_port *port = &sport->port.state->port;
+
+	if (sport->rx_bytes) {
+		tty_insert_flip_string(port, sport->rx_buf, sport->rx_bytes);
+		tty_flip_buffer_push(port);
+		sport->rx_bytes = 0;
+	}
+
+	if (sport->dma_is_rxing)
+		start_rx_dma(sport);
+}
+
+static void imx_rx_dma_done(struct imx_port *sport)
+{
+	unsigned long temp;
+
+	/* Enable this interrupt when the RXFIFO is empty. */
+	temp = readl(sport->port.membase + UCR1);
+	temp |= UCR1_RRDYEN;
+	writel(temp, sport->port.membase + UCR1);
+
+	sport->dma_is_rxing = 0;
+
+	/* Is the shutdown waiting for us? */
+	if (waitqueue_active(&sport->dma_wait))
+		wake_up(&sport->dma_wait);
+}
+
+/*
+ * There are three kinds of RX DMA interrupts(such as in the MX6Q):
+ *   [1] the RX DMA buffer is full.
+ *   [2] the Aging timer expires(wait for 8 bytes long)
+ *   [3] the Idle Condition Detect(enabled the UCR4_IDDMAEN).
+ *
+ * The [2] is trigger when a character was been sitting in the FIFO
+ * meanwhile [3] can wait for 32 bytes long when the RX line is
+ * on IDLE state and RxFIFO is empty.
+ */
+static void dma_rx_callback(void *data)
+{
+	struct imx_port *sport = data;
+	struct dma_chan	*chan = sport->dma_chan_rx;
+	struct scatterlist *sgl = &sport->rx_sgl;
+	struct dma_tx_state state;
+	enum dma_status status;
+	unsigned int count;
+
+	/* unmap it first */
+	dma_unmap_sg(sport->port.dev, sgl, 1, DMA_FROM_DEVICE);
+
+	status = chan->device->device_tx_status(chan, (dma_cookie_t)0, &state);
+	count = RX_BUF_SIZE - state.residue;
+	dev_dbg(sport->port.dev, "We get %d bytes.\n", count);
+
+	if (count) {
+		sport->rx_bytes = count;
+		schedule_work(&sport->tsk_dma_rx);
+	} else
+		imx_rx_dma_done(sport);
+}
+
+static int start_rx_dma(struct imx_port *sport)
+{
+	struct scatterlist *sgl = &sport->rx_sgl;
+	struct dma_chan	*chan = sport->dma_chan_rx;
+	struct device *dev = sport->port.dev;
+	struct dma_async_tx_descriptor *desc;
+	int ret;
+
+	sg_init_one(sgl, sport->rx_buf, RX_BUF_SIZE);
+	ret = dma_map_sg(dev, sgl, 1, DMA_FROM_DEVICE);
+	if (ret == 0) {
+		dev_err(dev, "DMA mapping error for RX.\n");
+		return -EINVAL;
+	}
+	desc = dmaengine_prep_slave_sg(chan, sgl, 1, DMA_DEV_TO_MEM,
+					DMA_PREP_INTERRUPT);
+	if (!desc) {
+		dev_err(dev, "We cannot prepare for the RX slave dma!\n");
+		return -EINVAL;
+	}
+	desc->callback = dma_rx_callback;
+	desc->callback_param = sport;
+
+	dev_dbg(dev, "RX: prepare for the DMA.\n");
+	dmaengine_submit(desc);
+	dma_async_issue_pending(chan);
+	return 0;
+}
+
+static void imx_uart_dma_exit(struct imx_port *sport)
+{
+	if (sport->dma_chan_rx) {
+		dma_release_channel(sport->dma_chan_rx);
+		sport->dma_chan_rx = NULL;
+
+		kfree(sport->rx_buf);
+		sport->rx_buf = NULL;
+	}
+
+	if (sport->dma_chan_tx) {
+		dma_release_channel(sport->dma_chan_tx);
+		sport->dma_chan_tx = NULL;
+	}
+
+	sport->dma_is_inited = 0;
+}
+
+static int imx_uart_dma_init(struct imx_port *sport)
+{
+	struct dma_slave_config slave_config = {};
+	struct device *dev = sport->port.dev;
+	int ret;
+
+	/* Prepare for RX : */
+	sport->dma_chan_rx = dma_request_slave_channel(dev, "rx");
+	if (!sport->dma_chan_rx) {
+		dev_dbg(dev, "cannot get the DMA channel.\n");
+		ret = -EINVAL;
+		goto err;
+	}
+
+	slave_config.direction = DMA_DEV_TO_MEM;
+	slave_config.src_addr = sport->port.mapbase + URXD0;
+	slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+	slave_config.src_maxburst = RXTL;
+	ret = dmaengine_slave_config(sport->dma_chan_rx, &slave_config);
+	if (ret) {
+		dev_err(dev, "error in RX dma configuration.\n");
+		goto err;
+	}
+
+	sport->rx_buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
+	if (!sport->rx_buf) {
+		dev_err(dev, "cannot alloc DMA buffer.\n");
+		ret = -ENOMEM;
+		goto err;
+	}
+	sport->rx_bytes = 0;
+
+	/* Prepare for TX : */
+	sport->dma_chan_tx = dma_request_slave_channel(dev, "tx");
+	if (!sport->dma_chan_tx) {
+		dev_err(dev, "cannot get the TX DMA channel!\n");
+		ret = -EINVAL;
+		goto err;
+	}
+
+	slave_config.direction = DMA_MEM_TO_DEV;
+	slave_config.dst_addr = sport->port.mapbase + URTX0;
+	slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+	slave_config.dst_maxburst = TXTL;
+	ret = dmaengine_slave_config(sport->dma_chan_tx, &slave_config);
+	if (ret) {
+		dev_err(dev, "error in TX dma configuration.");
+		goto err;
+	}
+
+	sport->dma_is_inited = 1;
+
+	return 0;
+err:
+	imx_uart_dma_exit(sport);
+	return ret;
+}
+
+static void imx_enable_dma(struct imx_port *sport)
+{
+	unsigned long temp;
+	struct tty_port *port = &sport->port.state->port;
+
+	port->low_latency = 1;
+	INIT_WORK(&sport->tsk_dma_tx, dma_tx_work);
+	INIT_WORK(&sport->tsk_dma_rx, dma_rx_work);
+	init_waitqueue_head(&sport->dma_wait);
+
+	/* set UCR1 */
+	temp = readl(sport->port.membase + UCR1);
+	temp |= UCR1_RDMAEN | UCR1_TDMAEN | UCR1_ATDMAEN |
+		/* wait for 32 idle frames for IDDMA interrupt */
+		UCR1_ICD_REG(3);
+	writel(temp, sport->port.membase + UCR1);
+
+	/* set UCR4 */
+	temp = readl(sport->port.membase + UCR4);
+	temp |= UCR4_IDDMAEN;
+	writel(temp, sport->port.membase + UCR4);
+
+	sport->dma_is_enabled = 1;
+}
+
+static void imx_disable_dma(struct imx_port *sport)
+{
+	unsigned long temp;
+	struct tty_port *port = &sport->port.state->port;
+
+	/* clear UCR1 */
+	temp = readl(sport->port.membase + UCR1);
+	temp &= ~(UCR1_RDMAEN | UCR1_TDMAEN | UCR1_ATDMAEN);
+	writel(temp, sport->port.membase + UCR1);
+
+	/* clear UCR2 */
+	temp = readl(sport->port.membase + UCR2);
+	temp &= ~(UCR2_CTSC | UCR2_CTS);
+	writel(temp, sport->port.membase + UCR2);
+
+	/* clear UCR4 */
+	temp = readl(sport->port.membase + UCR4);
+	temp &= ~UCR4_IDDMAEN;
+	writel(temp, sport->port.membase + UCR4);
+
+	sport->dma_is_enabled = 0;
+	port->low_latency = 0;
+}
+
 /* half the RX buffer size */
 /* half the RX buffer size */
 #define CTSTL 16
 #define CTSTL 16
 
 
@@ -702,15 +1093,13 @@ static int imx_startup(struct uart_port *port)
 	int retval;
 	int retval;
 	unsigned long flags, temp;
 	unsigned long flags, temp;
 
 
-	if (!uart_console(port)) {
-		retval = clk_prepare_enable(sport->clk_per);
-		if (retval)
-			goto error_out1;
-		retval = clk_prepare_enable(sport->clk_ipg);
-		if (retval) {
-			clk_disable_unprepare(sport->clk_per);
-			goto error_out1;
-		}
+	retval = clk_prepare_enable(sport->clk_per);
+	if (retval)
+		goto error_out1;
+	retval = clk_prepare_enable(sport->clk_ipg);
+	if (retval) {
+		clk_disable_unprepare(sport->clk_per);
+		goto error_out1;
 	}
 	}
 
 
 	imx_setup_ufcr(sport, 0);
 	imx_setup_ufcr(sport, 0);
@@ -803,7 +1192,7 @@ static int imx_startup(struct uart_port *port)
 		}
 		}
 	}
 	}
 
 
-	if (is_imx21_uart(sport)) {
+	if (!is_imx1_uart(sport)) {
 		temp = readl(sport->port.membase + UCR3);
 		temp = readl(sport->port.membase + UCR3);
 		temp |= IMX21_UCR3_RXDMUXSEL;
 		temp |= IMX21_UCR3_RXDMUXSEL;
 		writel(temp, sport->port.membase + UCR3);
 		writel(temp, sport->port.membase + UCR3);
@@ -833,7 +1222,7 @@ static int imx_startup(struct uart_port *port)
 
 
 	if (USE_IRDA(sport)) {
 	if (USE_IRDA(sport)) {
 		struct imxuart_platform_data *pdata;
 		struct imxuart_platform_data *pdata;
-		pdata = sport->port.dev->platform_data;
+		pdata = dev_get_platdata(sport->port.dev);
 		sport->irda_inv_rx = pdata->irda_inv_rx;
 		sport->irda_inv_rx = pdata->irda_inv_rx;
 		sport->irda_inv_tx = pdata->irda_inv_tx;
 		sport->irda_inv_tx = pdata->irda_inv_tx;
 		sport->trcv_delay = pdata->transceiver_delay;
 		sport->trcv_delay = pdata->transceiver_delay;
@@ -859,6 +1248,15 @@ static void imx_shutdown(struct uart_port *port)
 	unsigned long temp;
 	unsigned long temp;
 	unsigned long flags;
 	unsigned long flags;
 
 
+	if (sport->dma_is_enabled) {
+		/* We have to wait for the DMA to finish. */
+		wait_event(sport->dma_wait,
+			!sport->dma_is_rxing && !sport->dma_is_txing);
+		imx_stop_rx(port);
+		imx_disable_dma(sport);
+		imx_uart_dma_exit(sport);
+	}
+
 	spin_lock_irqsave(&sport->port.lock, flags);
 	spin_lock_irqsave(&sport->port.lock, flags);
 	temp = readl(sport->port.membase + UCR2);
 	temp = readl(sport->port.membase + UCR2);
 	temp &= ~(UCR2_TXEN);
 	temp &= ~(UCR2_TXEN);
@@ -867,7 +1265,7 @@ static void imx_shutdown(struct uart_port *port)
 
 
 	if (USE_IRDA(sport)) {
 	if (USE_IRDA(sport)) {
 		struct imxuart_platform_data *pdata;
 		struct imxuart_platform_data *pdata;
-		pdata = sport->port.dev->platform_data;
+		pdata = dev_get_platdata(sport->port.dev);
 		if (pdata->irda_enable)
 		if (pdata->irda_enable)
 			pdata->irda_enable(0);
 			pdata->irda_enable(0);
 	}
 	}
@@ -901,10 +1299,8 @@ static void imx_shutdown(struct uart_port *port)
 	writel(temp, sport->port.membase + UCR1);
 	writel(temp, sport->port.membase + UCR1);
 	spin_unlock_irqrestore(&sport->port.lock, flags);
 	spin_unlock_irqrestore(&sport->port.lock, flags);
 
 
-	if (!uart_console(&sport->port)) {
-		clk_disable_unprepare(sport->clk_per);
-		clk_disable_unprepare(sport->clk_ipg);
-	}
+	clk_disable_unprepare(sport->clk_per);
+	clk_disable_unprepare(sport->clk_ipg);
 }
 }
 
 
 static void
 static void
@@ -947,6 +1343,11 @@ 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;
 		}
 		}
@@ -1020,6 +1421,11 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
 		 */
 		 */
 		div = 1;
 		div = 1;
 	} else {
 	} else {
+		/* custom-baudrate handling */
+		div = sport->port.uartclk / (baud * 16);
+		if (baud == 38400 && quot != div)
+			baud = sport->port.uartclk / (quot * 16);
+
 		div = sport->port.uartclk / (baud * 16);
 		div = sport->port.uartclk / (baud * 16);
 		if (div > 7)
 		if (div > 7)
 			div = 7;
 			div = 7;
@@ -1048,7 +1454,7 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
 	writel(num, sport->port.membase + UBIR);
 	writel(num, sport->port.membase + UBIR);
 	writel(denom, sport->port.membase + UBMR);
 	writel(denom, sport->port.membase + UBMR);
 
 
-	if (is_imx21_uart(sport))
+	if (!is_imx1_uart(sport))
 		writel(sport->port.uartclk / div / 1000,
 		writel(sport->port.uartclk / div / 1000,
 				sport->port.membase + IMX21_ONEMS);
 				sport->port.membase + IMX21_ONEMS);
 
 
@@ -1060,6 +1466,8 @@ 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);
 }
 }
 
 
@@ -1251,6 +1659,16 @@ imx_console_write(struct console *co, const char *s, unsigned int count)
 	unsigned int ucr1;
 	unsigned int ucr1;
 	unsigned long flags = 0;
 	unsigned long flags = 0;
 	int locked = 1;
 	int locked = 1;
+	int retval;
+
+	retval = clk_enable(sport->clk_per);
+	if (retval)
+		return;
+	retval = clk_enable(sport->clk_ipg);
+	if (retval) {
+		clk_disable(sport->clk_per);
+		return;
+	}
 
 
 	if (sport->port.sysrq)
 	if (sport->port.sysrq)
 		locked = 0;
 		locked = 0;
@@ -1286,6 +1704,9 @@ imx_console_write(struct console *co, const char *s, unsigned int count)
 
 
 	if (locked)
 	if (locked)
 		spin_unlock_irqrestore(&sport->port.lock, flags);
 		spin_unlock_irqrestore(&sport->port.lock, flags);
+
+	clk_disable(sport->clk_ipg);
+	clk_disable(sport->clk_per);
 }
 }
 
 
 /*
 /*
@@ -1359,6 +1780,7 @@ imx_console_setup(struct console *co, char *options)
 	int bits = 8;
 	int bits = 8;
 	int parity = 'n';
 	int parity = 'n';
 	int flow = 'n';
 	int flow = 'n';
+	int retval;
 
 
 	/*
 	/*
 	 * Check whether an invalid uart number has been specified, and
 	 * Check whether an invalid uart number has been specified, and
@@ -1371,6 +1793,11 @@ imx_console_setup(struct console *co, char *options)
 	if (sport == NULL)
 	if (sport == NULL)
 		return -ENODEV;
 		return -ENODEV;
 
 
+	/* For setting the registers, we only need to enable the ipg clock. */
+	retval = clk_prepare_enable(sport->clk_ipg);
+	if (retval)
+		goto error_console;
+
 	if (options)
 	if (options)
 		uart_parse_options(options, &baud, &parity, &bits, &flow);
 		uart_parse_options(options, &baud, &parity, &bits, &flow);
 	else
 	else
@@ -1378,7 +1805,20 @@ imx_console_setup(struct console *co, char *options)
 
 
 	imx_setup_ufcr(sport, 0);
 	imx_setup_ufcr(sport, 0);
 
 
-	return uart_set_options(&sport->port, co, baud, parity, bits, flow);
+	retval = uart_set_options(&sport->port, co, baud, parity, bits, flow);
+
+	clk_disable(sport->clk_ipg);
+	if (retval) {
+		clk_unprepare(sport->clk_ipg);
+		goto error_console;
+	}
+
+	retval = clk_prepare(sport->clk_per);
+	if (retval)
+		clk_disable_unprepare(sport->clk_ipg);
+
+error_console:
+	return retval;
 }
 }
 
 
 static struct uart_driver imx_reg;
 static struct uart_driver imx_reg;
@@ -1472,6 +1912,9 @@ static int serial_imx_probe_dt(struct imx_port *sport,
 
 
 	sport->devdata = of_id->data;
 	sport->devdata = of_id->data;
 
 
+	if (of_device_is_stdout_path(np))
+		add_preferred_console(imx_reg.cons->name, sport->port.line, 0);
+
 	return 0;
 	return 0;
 }
 }
 #else
 #else
@@ -1485,7 +1928,7 @@ static inline int serial_imx_probe_dt(struct imx_port *sport,
 static void serial_imx_probe_pdata(struct imx_port *sport,
 static void serial_imx_probe_pdata(struct imx_port *sport,
 		struct platform_device *pdev)
 		struct platform_device *pdev)
 {
 {
-	struct imxuart_platform_data *pdata = pdev->dev.platform_data;
+	struct imxuart_platform_data *pdata = dev_get_platdata(&pdev->dev);
 
 
 	sport->port.line = pdev->id;
 	sport->port.line = pdev->id;
 	sport->devdata = (struct imx_uart_data	*) pdev->id_entry->driver_data;
 	sport->devdata = (struct imx_uart_data	*) pdev->id_entry->driver_data;
@@ -1507,7 +1950,6 @@ static int serial_imx_probe(struct platform_device *pdev)
 	void __iomem *base;
 	void __iomem *base;
 	int ret = 0;
 	int ret = 0;
 	struct resource *res;
 	struct resource *res;
-	struct pinctrl *pinctrl;
 
 
 	sport = devm_kzalloc(&pdev->dev, sizeof(*sport), GFP_KERNEL);
 	sport = devm_kzalloc(&pdev->dev, sizeof(*sport), GFP_KERNEL);
 	if (!sport)
 	if (!sport)
@@ -1543,13 +1985,6 @@ static int serial_imx_probe(struct platform_device *pdev)
 	sport->timer.function = imx_timeout;
 	sport->timer.function = imx_timeout;
 	sport->timer.data     = (unsigned long)sport;
 	sport->timer.data     = (unsigned long)sport;
 
 
-	pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
-	if (IS_ERR(pinctrl)) {
-		ret = PTR_ERR(pinctrl);
-		dev_err(&pdev->dev, "failed to get default pinctrl: %d\n", ret);
-		return ret;
-	}
-
 	sport->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
 	sport->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
 	if (IS_ERR(sport->clk_ipg)) {
 	if (IS_ERR(sport->clk_ipg)) {
 		ret = PTR_ERR(sport->clk_ipg);
 		ret = PTR_ERR(sport->clk_ipg);
@@ -1564,18 +1999,15 @@ static int serial_imx_probe(struct platform_device *pdev)
 		return ret;
 		return ret;
 	}
 	}
 
 
-	clk_prepare_enable(sport->clk_per);
-	clk_prepare_enable(sport->clk_ipg);
-
 	sport->port.uartclk = clk_get_rate(sport->clk_per);
 	sport->port.uartclk = clk_get_rate(sport->clk_per);
 
 
 	imx_ports[sport->port.line] = sport;
 	imx_ports[sport->port.line] = sport;
 
 
-	pdata = pdev->dev.platform_data;
+	pdata = dev_get_platdata(&pdev->dev);
 	if (pdata && pdata->init) {
 	if (pdata && pdata->init) {
 		ret = pdata->init(pdev);
 		ret = pdata->init(pdev);
 		if (ret)
 		if (ret)
-			goto clkput;
+			return ret;
 	}
 	}
 
 
 	ret = uart_add_one_port(&imx_reg, &sport->port);
 	ret = uart_add_one_port(&imx_reg, &sport->port);
@@ -1583,18 +2015,10 @@ static int serial_imx_probe(struct platform_device *pdev)
 		goto deinit;
 		goto deinit;
 	platform_set_drvdata(pdev, sport);
 	platform_set_drvdata(pdev, sport);
 
 
-	if (!uart_console(&sport->port)) {
-		clk_disable_unprepare(sport->clk_per);
-		clk_disable_unprepare(sport->clk_ipg);
-	}
-
 	return 0;
 	return 0;
 deinit:
 deinit:
 	if (pdata && pdata->exit)
 	if (pdata && pdata->exit)
 		pdata->exit(pdev);
 		pdata->exit(pdev);
-clkput:
-	clk_disable_unprepare(sport->clk_per);
-	clk_disable_unprepare(sport->clk_ipg);
 	return ret;
 	return ret;
 }
 }
 
 
@@ -1603,9 +2027,7 @@ static int serial_imx_remove(struct platform_device *pdev)
 	struct imxuart_platform_data *pdata;
 	struct imxuart_platform_data *pdata;
 	struct imx_port *sport = platform_get_drvdata(pdev);
 	struct imx_port *sport = platform_get_drvdata(pdev);
 
 
-	pdata = pdev->dev.platform_data;
-
-	platform_set_drvdata(pdev, NULL);
+	pdata = dev_get_platdata(&pdev->dev);
 
 
 	uart_remove_one_port(&imx_reg, &sport->port);
 	uart_remove_one_port(&imx_reg, &sport->port);
 
 

+ 2 - 2
drivers/tty/serial/ioc4_serial.c

@@ -297,7 +297,7 @@ struct ioc4_serial {
 	struct ioc4_uartregs uart_1;
 	struct ioc4_uartregs uart_1;
 	struct ioc4_uartregs uart_2;
 	struct ioc4_uartregs uart_2;
 	struct ioc4_uartregs uart_3;
 	struct ioc4_uartregs uart_3;
-} ioc4_serial;
+};
 
 
 /* UART clock speed */
 /* UART clock speed */
 #define IOC4_SER_XIN_CLK_66     66666667
 #define IOC4_SER_XIN_CLK_66     66666667
@@ -2767,7 +2767,7 @@ ioc4_serial_core_attach(struct pci_dev *pdev, int port_type)
  *		called per card found from IOC4 master module.
  *		called per card found from IOC4 master module.
  * @idd: Master module data for this IOC4
  * @idd: Master module data for this IOC4
  */
  */
-int
+static int
 ioc4_serial_attach_one(struct ioc4_driver_data *idd)
 ioc4_serial_attach_one(struct ioc4_driver_data *idd)
 {
 {
 	unsigned long tmp_addr1;
 	unsigned long tmp_addr1;

+ 5 - 2
drivers/tty/serial/lantiq.c

@@ -318,7 +318,7 @@ lqasc_startup(struct uart_port *port)
 	struct ltq_uart_port *ltq_port = to_ltq_uart_port(port);
 	struct ltq_uart_port *ltq_port = to_ltq_uart_port(port);
 	int retval;
 	int retval;
 
 
-	if (ltq_port->clk)
+	if (!IS_ERR(ltq_port->clk))
 		clk_enable(ltq_port->clk);
 		clk_enable(ltq_port->clk);
 	port->uartclk = clk_get_rate(ltq_port->fpiclk);
 	port->uartclk = clk_get_rate(ltq_port->fpiclk);
 
 
@@ -386,7 +386,7 @@ lqasc_shutdown(struct uart_port *port)
 		port->membase + LTQ_ASC_RXFCON);
 		port->membase + LTQ_ASC_RXFCON);
 	ltq_w32_mask(ASCTXFCON_TXFEN, ASCTXFCON_TXFFLU,
 	ltq_w32_mask(ASCTXFCON_TXFEN, ASCTXFCON_TXFFLU,
 		port->membase + LTQ_ASC_TXFCON);
 		port->membase + LTQ_ASC_TXFCON);
-	if (ltq_port->clk)
+	if (!IS_ERR(ltq_port->clk))
 		clk_disable(ltq_port->clk);
 		clk_disable(ltq_port->clk);
 }
 }
 
 
@@ -636,6 +636,9 @@ lqasc_console_setup(struct console *co, char *options)
 
 
 	port = &ltq_port->port;
 	port = &ltq_port->port;
 
 
+	if (!IS_ERR(ltq_port->clk))
+		clk_enable(ltq_port->clk);
+
 	port->uartclk = clk_get_rate(ltq_port->fpiclk);
 	port->uartclk = clk_get_rate(ltq_port->fpiclk);
 
 
 	if (options)
 	if (options)

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

@@ -279,7 +279,10 @@ static void __serial_lpc32xx_rx(struct uart_port *port)
 
 
 		tmp = readl(LPC32XX_HSUART_FIFO(port->membase));
 		tmp = readl(LPC32XX_HSUART_FIFO(port->membase));
 	}
 	}
+
+	spin_unlock(&port->lock);
 	tty_flip_buffer_push(tport);
 	tty_flip_buffer_push(tport);
+	spin_lock(&port->lock);
 }
 }
 
 
 static void __serial_lpc32xx_tx(struct uart_port *port)
 static void __serial_lpc32xx_tx(struct uart_port *port)
@@ -351,10 +354,8 @@ static irqreturn_t serial_lpc32xx_interrupt(int irq, void *dev_id)
 	}
 	}
 
 
 	/* Data received? */
 	/* Data received? */
-	if (status & (LPC32XX_HSU_RX_TIMEOUT_INT | LPC32XX_HSU_RX_TRIG_INT)) {
+	if (status & (LPC32XX_HSU_RX_TIMEOUT_INT | LPC32XX_HSU_RX_TRIG_INT))
 		__serial_lpc32xx_rx(port);
 		__serial_lpc32xx_rx(port);
-		tty_flip_buffer_push(tport);
-	}
 
 
 	/* Transmit data request? */
 	/* Transmit data request? */
 	if ((status & LPC32XX_HSU_TX_INT) && (!uart_tx_stopped(port))) {
 	if ((status & LPC32XX_HSU_TX_INT) && (!uart_tx_stopped(port))) {

+ 3 - 0
drivers/tty/serial/m32r_sio.c

@@ -368,7 +368,10 @@ static void receive_chars(struct uart_sio_port *up, int *status)
 	ignore_char:
 	ignore_char:
 		*status = serial_in(up, UART_LSR);
 		*status = serial_in(up, UART_LSR);
 	} while ((*status & UART_LSR_DR) && (max_count-- > 0));
 	} while ((*status & UART_LSR_DR) && (max_count-- > 0));
+
+	spin_unlock(&up->port.lock);
 	tty_flip_buffer_push(port);
 	tty_flip_buffer_push(port);
+	spin_lock(&up->port.lock);
 }
 }
 
 
 static void transmit_chars(struct uart_sio_port *up)
 static void transmit_chars(struct uart_sio_port *up)

+ 1 - 1
drivers/tty/serial/max3100.c

@@ -779,7 +779,7 @@ static int max3100_probe(struct spi_device *spi)
 	max3100s[i]->irq = spi->irq;
 	max3100s[i]->irq = spi->irq;
 	spin_lock_init(&max3100s[i]->conf_lock);
 	spin_lock_init(&max3100s[i]->conf_lock);
 	spi_set_drvdata(spi, max3100s[i]);
 	spi_set_drvdata(spi, max3100s[i]);
-	pdata = spi->dev.platform_data;
+	pdata = dev_get_platdata(&spi->dev);
 	max3100s[i]->crystal = pdata->crystal;
 	max3100s[i]->crystal = pdata->crystal;
 	max3100s[i]->loopback = pdata->loopback;
 	max3100s[i]->loopback = pdata->loopback;
 	max3100s[i]->poll_time = pdata->poll_time * HZ / 1000;
 	max3100s[i]->poll_time = pdata->poll_time * HZ / 1000;

+ 522 - 469
drivers/tty/serial/max310x.c

@@ -1,7 +1,7 @@
 /*
 /*
- *  Maxim (Dallas) MAX3107/8 serial driver
+ *  Maxim (Dallas) MAX3107/8/9, MAX14830 serial driver
  *
  *
- *  Copyright (C) 2012 Alexander Shiyan <shc_work@mail.ru>
+ *  Copyright (C) 2012-2013 Alexander Shiyan <shc_work@mail.ru>
  *
  *
  *  Based on max3100.c, by Christian Pellegrin <chripell@evolware.org>
  *  Based on max3100.c, by Christian Pellegrin <chripell@evolware.org>
  *  Based on max3110.c, by Feng Tang <feng.tang@intel.com>
  *  Based on max3110.c, by Feng Tang <feng.tang@intel.com>
@@ -13,11 +13,10 @@
  *  (at your option) any later version.
  *  (at your option) any later version.
  */
  */
 
 
-/* TODO: MAX3109 support (Dual) */
-/* TODO: MAX14830 support (Quad) */
-
 #include <linux/module.h>
 #include <linux/module.h>
+#include <linux/delay.h>
 #include <linux/device.h>
 #include <linux/device.h>
+#include <linux/bitops.h>
 #include <linux/serial_core.h>
 #include <linux/serial_core.h>
 #include <linux/serial.h>
 #include <linux/serial.h>
 #include <linux/tty.h>
 #include <linux/tty.h>
@@ -25,8 +24,10 @@
 #include <linux/regmap.h>
 #include <linux/regmap.h>
 #include <linux/gpio.h>
 #include <linux/gpio.h>
 #include <linux/spi/spi.h>
 #include <linux/spi/spi.h>
+
 #include <linux/platform_data/max310x.h>
 #include <linux/platform_data/max310x.h>
 
 
+#define MAX310X_NAME			"max310x"
 #define MAX310X_MAJOR			204
 #define MAX310X_MAJOR			204
 #define MAX310X_MINOR			209
 #define MAX310X_MINOR			209
 
 
@@ -37,7 +38,8 @@
 #define MAX310X_IRQSTS_REG		(0x02) /* IRQ status */
 #define MAX310X_IRQSTS_REG		(0x02) /* IRQ status */
 #define MAX310X_LSR_IRQEN_REG		(0x03) /* LSR IRQ enable */
 #define MAX310X_LSR_IRQEN_REG		(0x03) /* LSR IRQ enable */
 #define MAX310X_LSR_IRQSTS_REG		(0x04) /* LSR IRQ status */
 #define MAX310X_LSR_IRQSTS_REG		(0x04) /* LSR IRQ status */
-#define MAX310X_SPCHR_IRQEN_REG		(0x05) /* Special char IRQ enable */
+#define MAX310X_REG_05			(0x05)
+#define MAX310X_SPCHR_IRQEN_REG		MAX310X_REG_05 /* Special char IRQ en */
 #define MAX310X_SPCHR_IRQSTS_REG	(0x06) /* Special char IRQ status */
 #define MAX310X_SPCHR_IRQSTS_REG	(0x06) /* Special char IRQ status */
 #define MAX310X_STS_IRQEN_REG		(0x07) /* Status IRQ enable */
 #define MAX310X_STS_IRQEN_REG		(0x07) /* Status IRQ enable */
 #define MAX310X_STS_IRQSTS_REG		(0x08) /* Status IRQ status */
 #define MAX310X_STS_IRQSTS_REG		(0x08) /* Status IRQ status */
@@ -63,8 +65,15 @@
 #define MAX310X_BRGDIVLSB_REG		(0x1c) /* Baud rate divisor LSB */
 #define MAX310X_BRGDIVLSB_REG		(0x1c) /* Baud rate divisor LSB */
 #define MAX310X_BRGDIVMSB_REG		(0x1d) /* Baud rate divisor MSB */
 #define MAX310X_BRGDIVMSB_REG		(0x1d) /* Baud rate divisor MSB */
 #define MAX310X_CLKSRC_REG		(0x1e) /* Clock source */
 #define MAX310X_CLKSRC_REG		(0x1e) /* Clock source */
-/* Only present in MAX3107 */
-#define MAX3107_REVID_REG		(0x1f) /* Revision identification */
+#define MAX310X_REG_1F			(0x1f)
+
+#define MAX310X_REVID_REG		MAX310X_REG_1F /* Revision ID */
+
+#define MAX310X_GLOBALIRQ_REG		MAX310X_REG_1F /* Global IRQ (RO) */
+#define MAX310X_GLOBALCMD_REG		MAX310X_REG_1F /* Global Command (WO) */
+
+/* Extended registers */
+#define MAX310X_REVID_EXTREG		MAX310X_REG_05 /* Revision ID */
 
 
 /* IRQ register bits */
 /* IRQ register bits */
 #define MAX310X_IRQ_LSR_BIT		(1 << 0) /* LSR interrupt */
 #define MAX310X_IRQ_LSR_BIT		(1 << 0) /* LSR interrupt */
@@ -246,58 +255,210 @@
 #define MAX310X_CLKSRC_EXTCLK_BIT	(1 << 4) /* External clock enable */
 #define MAX310X_CLKSRC_EXTCLK_BIT	(1 << 4) /* External clock enable */
 #define MAX310X_CLKSRC_CLK2RTS_BIT	(1 << 7) /* Baud clk to RTS pin */
 #define MAX310X_CLKSRC_CLK2RTS_BIT	(1 << 7) /* Baud clk to RTS pin */
 
 
+/* Global commands */
+#define MAX310X_EXTREG_ENBL		(0xce)
+#define MAX310X_EXTREG_DSBL		(0xcd)
+
 /* Misc definitions */
 /* Misc definitions */
 #define MAX310X_FIFO_SIZE		(128)
 #define MAX310X_FIFO_SIZE		(128)
+#define MAX310x_REV_MASK		(0xfc)
 
 
 /* MAX3107 specific */
 /* MAX3107 specific */
 #define MAX3107_REV_ID			(0xa0)
 #define MAX3107_REV_ID			(0xa0)
-#define MAX3107_REV_MASK		(0xfe)
-
-/* IRQ status bits definitions */
-#define MAX310X_IRQ_TX			(MAX310X_IRQ_TXFIFO_BIT | \
-					 MAX310X_IRQ_TXEMPTY_BIT)
-#define MAX310X_IRQ_RX			(MAX310X_IRQ_RXFIFO_BIT | \
-					 MAX310X_IRQ_RXEMPTY_BIT)
-
-/* Supported chip types */
-enum {
-	MAX310X_TYPE_MAX3107	= 3107,
-	MAX310X_TYPE_MAX3108	= 3108,
+
+/* MAX3109 specific */
+#define MAX3109_REV_ID			(0xc0)
+
+/* MAX14830 specific */
+#define MAX14830_BRGCFG_CLKDIS_BIT	(1 << 6) /* Clock Disable */
+#define MAX14830_REV_ID			(0xb0)
+
+struct max310x_devtype {
+	char	name[9];
+	int	nr;
+	int	(*detect)(struct device *);
+	void	(*power)(struct uart_port *, int);
 };
 };
 
 
-struct max310x_port {
-	struct uart_driver	uart;
+struct max310x_one {
 	struct uart_port	port;
 	struct uart_port	port;
+	struct work_struct	tx_work;
+};
 
 
-	const char		*name;
-	int			uartclk;
-
-	unsigned int		nr_gpio;
+struct max310x_port {
+	struct uart_driver	uart;
+	struct max310x_devtype	*devtype;
+	struct regmap		*regmap;
+	struct regmap_config	regcfg;
+	struct mutex		mutex;
+	struct max310x_pdata	*pdata;
+	int			gpio_used;
 #ifdef CONFIG_GPIOLIB
 #ifdef CONFIG_GPIOLIB
 	struct gpio_chip	gpio;
 	struct gpio_chip	gpio;
 #endif
 #endif
+	struct max310x_one	p[0];
+};
 
 
-	struct regmap		*regmap;
-	struct regmap_config	regcfg;
+static u8 max310x_port_read(struct uart_port *port, u8 reg)
+{
+	struct max310x_port *s = dev_get_drvdata(port->dev);
+	unsigned int val = 0;
 
 
-	struct workqueue_struct	*wq;
-	struct work_struct	tx_work;
+	regmap_read(s->regmap, port->iobase + reg, &val);
 
 
-	struct mutex		max310x_mutex;
+	return val;
+}
 
 
-	struct max310x_pdata	*pdata;
+static void max310x_port_write(struct uart_port *port, u8 reg, u8 val)
+{
+	struct max310x_port *s = dev_get_drvdata(port->dev);
+
+	regmap_write(s->regmap, port->iobase + reg, val);
+}
+
+static void max310x_port_update(struct uart_port *port, u8 reg, u8 mask, u8 val)
+{
+	struct max310x_port *s = dev_get_drvdata(port->dev);
+
+	regmap_update_bits(s->regmap, port->iobase + reg, mask, val);
+}
+
+static int max3107_detect(struct device *dev)
+{
+	struct max310x_port *s = dev_get_drvdata(dev);
+	unsigned int val = 0;
+	int ret;
+
+	ret = regmap_read(s->regmap, MAX310X_REVID_REG, &val);
+	if (ret)
+		return ret;
+
+	if (((val & MAX310x_REV_MASK) != MAX3107_REV_ID)) {
+		dev_err(dev,
+			"%s ID 0x%02x does not match\n", s->devtype->name, val);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static int max3108_detect(struct device *dev)
+{
+	struct max310x_port *s = dev_get_drvdata(dev);
+	unsigned int val = 0;
+	int ret;
+
+	/* MAX3108 have not REV ID register, we just check default value
+	 * from clocksource register to make sure everything works.
+	 */
+	ret = regmap_read(s->regmap, MAX310X_CLKSRC_REG, &val);
+	if (ret)
+		return ret;
+
+	if (val != (MAX310X_CLKSRC_EXTCLK_BIT | MAX310X_CLKSRC_PLLBYP_BIT)) {
+		dev_err(dev, "%s not present\n", s->devtype->name);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static int max3109_detect(struct device *dev)
+{
+	struct max310x_port *s = dev_get_drvdata(dev);
+	unsigned int val = 0;
+	int ret;
+
+	ret = regmap_read(s->regmap, MAX310X_REVID_REG, &val);
+	if (ret)
+		return ret;
+
+	if (((val & MAX310x_REV_MASK) != MAX3109_REV_ID)) {
+		dev_err(dev,
+			"%s ID 0x%02x does not match\n", s->devtype->name, val);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static void max310x_power(struct uart_port *port, int on)
+{
+	max310x_port_update(port, MAX310X_MODE1_REG,
+			    MAX310X_MODE1_FORCESLEEP_BIT,
+			    on ? 0 : MAX310X_MODE1_FORCESLEEP_BIT);
+	if (on)
+		msleep(50);
+}
+
+static int max14830_detect(struct device *dev)
+{
+	struct max310x_port *s = dev_get_drvdata(dev);
+	unsigned int val = 0;
+	int ret;
+
+	ret = regmap_write(s->regmap, MAX310X_GLOBALCMD_REG,
+			   MAX310X_EXTREG_ENBL);
+	if (ret)
+		return ret;
+	
+	regmap_read(s->regmap, MAX310X_REVID_EXTREG, &val);
+	regmap_write(s->regmap, MAX310X_GLOBALCMD_REG, MAX310X_EXTREG_DSBL);
+	if (((val & MAX310x_REV_MASK) != MAX14830_REV_ID)) {
+		dev_err(dev,
+			"%s ID 0x%02x does not match\n", s->devtype->name, val);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static void max14830_power(struct uart_port *port, int on)
+{
+	max310x_port_update(port, MAX310X_BRGCFG_REG,
+			    MAX14830_BRGCFG_CLKDIS_BIT,
+			    on ? 0 : MAX14830_BRGCFG_CLKDIS_BIT);
+	if (on)
+		msleep(50);
+}
+
+static const struct max310x_devtype max3107_devtype = {
+	.name	= "MAX3107",
+	.nr	= 1,
+	.detect	= max3107_detect,
+	.power	= max310x_power,
+};
+
+static const struct max310x_devtype max3108_devtype = {
+	.name	= "MAX3108",
+	.nr	= 1,
+	.detect	= max3108_detect,
+	.power	= max310x_power,
+};
+
+static const struct max310x_devtype max3109_devtype = {
+	.name	= "MAX3109",
+	.nr	= 2,
+	.detect	= max3109_detect,
+	.power	= max310x_power,
+};
+
+static const struct max310x_devtype max14830_devtype = {
+	.name	= "MAX14830",
+	.nr	= 4,
+	.detect	= max14830_detect,
+	.power	= max14830_power,
 };
 };
 
 
-static bool max3107_8_reg_writeable(struct device *dev, unsigned int reg)
+static bool max310x_reg_writeable(struct device *dev, unsigned int reg)
 {
 {
-	switch (reg) {
+	switch (reg & 0x1f) {
 	case MAX310X_IRQSTS_REG:
 	case MAX310X_IRQSTS_REG:
 	case MAX310X_LSR_IRQSTS_REG:
 	case MAX310X_LSR_IRQSTS_REG:
 	case MAX310X_SPCHR_IRQSTS_REG:
 	case MAX310X_SPCHR_IRQSTS_REG:
 	case MAX310X_STS_IRQSTS_REG:
 	case MAX310X_STS_IRQSTS_REG:
 	case MAX310X_TXFIFOLVL_REG:
 	case MAX310X_TXFIFOLVL_REG:
 	case MAX310X_RXFIFOLVL_REG:
 	case MAX310X_RXFIFOLVL_REG:
-	case MAX3107_REVID_REG: /* Only available on MAX3107 */
 		return false;
 		return false;
 	default:
 	default:
 		break;
 		break;
@@ -308,7 +469,7 @@ static bool max3107_8_reg_writeable(struct device *dev, unsigned int reg)
 
 
 static bool max310x_reg_volatile(struct device *dev, unsigned int reg)
 static bool max310x_reg_volatile(struct device *dev, unsigned int reg)
 {
 {
-	switch (reg) {
+	switch (reg & 0x1f) {
 	case MAX310X_RHR_REG:
 	case MAX310X_RHR_REG:
 	case MAX310X_IRQSTS_REG:
 	case MAX310X_IRQSTS_REG:
 	case MAX310X_LSR_IRQSTS_REG:
 	case MAX310X_LSR_IRQSTS_REG:
@@ -317,6 +478,9 @@ static bool max310x_reg_volatile(struct device *dev, unsigned int reg)
 	case MAX310X_TXFIFOLVL_REG:
 	case MAX310X_TXFIFOLVL_REG:
 	case MAX310X_RXFIFOLVL_REG:
 	case MAX310X_RXFIFOLVL_REG:
 	case MAX310X_GPIODATA_REG:
 	case MAX310X_GPIODATA_REG:
+	case MAX310X_BRGDIVLSB_REG:
+	case MAX310X_REG_05:
+	case MAX310X_REG_1F:
 		return true;
 		return true;
 	default:
 	default:
 		break;
 		break;
@@ -327,7 +491,7 @@ static bool max310x_reg_volatile(struct device *dev, unsigned int reg)
 
 
 static bool max310x_reg_precious(struct device *dev, unsigned int reg)
 static bool max310x_reg_precious(struct device *dev, unsigned int reg)
 {
 {
-	switch (reg) {
+	switch (reg & 0x1f) {
 	case MAX310X_RHR_REG:
 	case MAX310X_RHR_REG:
 	case MAX310X_IRQSTS_REG:
 	case MAX310X_IRQSTS_REG:
 	case MAX310X_SPCHR_IRQSTS_REG:
 	case MAX310X_SPCHR_IRQSTS_REG:
@@ -340,42 +504,25 @@ static bool max310x_reg_precious(struct device *dev, unsigned int reg)
 	return false;
 	return false;
 }
 }
 
 
-static void max310x_set_baud(struct max310x_port *s, int baud)
+static void max310x_set_baud(struct uart_port *port, int baud)
 {
 {
-	unsigned int mode = 0, div = s->uartclk / baud;
+	unsigned int mode = 0, div = port->uartclk / baud;
 
 
 	if (!(div / 16)) {
 	if (!(div / 16)) {
 		/* Mode x2 */
 		/* Mode x2 */
 		mode = MAX310X_BRGCFG_2XMODE_BIT;
 		mode = MAX310X_BRGCFG_2XMODE_BIT;
-		div = (s->uartclk * 2) / baud;
+		div = (port->uartclk * 2) / baud;
 	}
 	}
 
 
 	if (!(div / 16)) {
 	if (!(div / 16)) {
 		/* Mode x4 */
 		/* Mode x4 */
 		mode = MAX310X_BRGCFG_4XMODE_BIT;
 		mode = MAX310X_BRGCFG_4XMODE_BIT;
-		div = (s->uartclk * 4) / baud;
+		div = (port->uartclk * 4) / baud;
 	}
 	}
 
 
-	regmap_write(s->regmap, MAX310X_BRGDIVMSB_REG,
-		     ((div / 16) >> 8) & 0xff);
-	regmap_write(s->regmap, MAX310X_BRGDIVLSB_REG, (div / 16) & 0xff);
-	regmap_write(s->regmap, MAX310X_BRGCFG_REG, (div % 16) | mode);
-}
-
-static void max310x_wait_pll(struct max310x_port *s)
-{
-	int tryes = 1000;
-
-	/* Wait for PLL only if crystal is used */
-	if (!(s->pdata->driver_flags & MAX310X_EXT_CLK)) {
-		unsigned int sts = 0;
-
-		while (tryes--) {
-			regmap_read(s->regmap, MAX310X_STS_IRQSTS_REG, &sts);
-			if (sts & MAX310X_STS_CLKREADY_BIT)
-				break;
-		}
-	}
+	max310x_port_write(port, MAX310X_BRGDIVMSB_REG, (div / 16) >> 8);
+	max310x_port_write(port, MAX310X_BRGDIVLSB_REG, div / 16);
+	max310x_port_write(port, MAX310X_BRGCFG_REG, (div % 16) | mode);
 }
 }
 
 
 static int max310x_update_best_err(unsigned long f, long *besterr)
 static int max310x_update_best_err(unsigned long f, long *besterr)
@@ -449,49 +596,49 @@ static int max310x_set_ref_clk(struct max310x_port *s)
 
 
 	regmap_write(s->regmap, MAX310X_CLKSRC_REG, clksrc);
 	regmap_write(s->regmap, MAX310X_CLKSRC_REG, clksrc);
 
 
-	if (pllcfg)
-		max310x_wait_pll(s);
-
-	dev_dbg(s->port.dev, "Reference clock set to %lu Hz\n", bestfreq);
+	/* Wait for crystal */
+	if (pllcfg && !(s->pdata->driver_flags & MAX310X_EXT_CLK))
+		msleep(10);
 
 
 	return (int)bestfreq;
 	return (int)bestfreq;
 }
 }
 
 
-static void max310x_handle_rx(struct max310x_port *s, unsigned int rxlen)
+static void max310x_handle_rx(struct uart_port *port, unsigned int rxlen)
 {
 {
-	unsigned int sts = 0, ch = 0, flag;
+	unsigned int sts, ch, flag;
 
 
-	if (unlikely(rxlen >= MAX310X_FIFO_SIZE)) {
-		dev_warn(s->port.dev, "Possible RX FIFO overrun %d\n", rxlen);
+	if (unlikely(rxlen >= port->fifosize)) {
+		dev_warn_ratelimited(port->dev,
+				     "Port %i: Possible RX FIFO overrun\n",
+				     port->line);
+		port->icount.buf_overrun++;
 		/* Ensure sanity of RX level */
 		/* Ensure sanity of RX level */
-		rxlen = MAX310X_FIFO_SIZE;
+		rxlen = port->fifosize;
 	}
 	}
 
 
-	dev_dbg(s->port.dev, "RX Len = %u\n", rxlen);
-
 	while (rxlen--) {
 	while (rxlen--) {
-		regmap_read(s->regmap, MAX310X_RHR_REG, &ch);
-		regmap_read(s->regmap, MAX310X_LSR_IRQSTS_REG, &sts);
+		ch = max310x_port_read(port, MAX310X_RHR_REG);
+		sts = max310x_port_read(port, MAX310X_LSR_IRQSTS_REG);
 
 
 		sts &= MAX310X_LSR_RXPAR_BIT | MAX310X_LSR_FRERR_BIT |
 		sts &= MAX310X_LSR_RXPAR_BIT | MAX310X_LSR_FRERR_BIT |
 		       MAX310X_LSR_RXOVR_BIT | MAX310X_LSR_RXBRK_BIT;
 		       MAX310X_LSR_RXOVR_BIT | MAX310X_LSR_RXBRK_BIT;
 
 
-		s->port.icount.rx++;
+		port->icount.rx++;
 		flag = TTY_NORMAL;
 		flag = TTY_NORMAL;
 
 
 		if (unlikely(sts)) {
 		if (unlikely(sts)) {
 			if (sts & MAX310X_LSR_RXBRK_BIT) {
 			if (sts & MAX310X_LSR_RXBRK_BIT) {
-				s->port.icount.brk++;
-				if (uart_handle_break(&s->port))
+				port->icount.brk++;
+				if (uart_handle_break(port))
 					continue;
 					continue;
 			} else if (sts & MAX310X_LSR_RXPAR_BIT)
 			} else if (sts & MAX310X_LSR_RXPAR_BIT)
-				s->port.icount.parity++;
+				port->icount.parity++;
 			else if (sts & MAX310X_LSR_FRERR_BIT)
 			else if (sts & MAX310X_LSR_FRERR_BIT)
-				s->port.icount.frame++;
+				port->icount.frame++;
 			else if (sts & MAX310X_LSR_RXOVR_BIT)
 			else if (sts & MAX310X_LSR_RXOVR_BIT)
-				s->port.icount.overrun++;
+				port->icount.overrun++;
 
 
-			sts &= s->port.read_status_mask;
+			sts &= port->read_status_mask;
 			if (sts & MAX310X_LSR_RXBRK_BIT)
 			if (sts & MAX310X_LSR_RXBRK_BIT)
 				flag = TTY_BREAK;
 				flag = TTY_BREAK;
 			else if (sts & MAX310X_LSR_RXPAR_BIT)
 			else if (sts & MAX310X_LSR_RXPAR_BIT)
@@ -502,129 +649,129 @@ static void max310x_handle_rx(struct max310x_port *s, unsigned int rxlen)
 				flag = TTY_OVERRUN;
 				flag = TTY_OVERRUN;
 		}
 		}
 
 
-		if (uart_handle_sysrq_char(s->port, ch))
+		if (uart_handle_sysrq_char(port, ch))
 			continue;
 			continue;
 
 
-		if (sts & s->port.ignore_status_mask)
+		if (sts & port->ignore_status_mask)
 			continue;
 			continue;
 
 
-		uart_insert_char(&s->port, sts, MAX310X_LSR_RXOVR_BIT,
-				 ch, flag);
+		uart_insert_char(port, sts, MAX310X_LSR_RXOVR_BIT, ch, flag);
 	}
 	}
 
 
-	tty_flip_buffer_push(&s->port.state->port);
+	tty_flip_buffer_push(&port->state->port);
 }
 }
 
 
-static void max310x_handle_tx(struct max310x_port *s)
+static void max310x_handle_tx(struct uart_port *port)
 {
 {
-	struct circ_buf *xmit = &s->port.state->xmit;
-	unsigned int txlen = 0, to_send;
+	struct circ_buf *xmit = &port->state->xmit;
+	unsigned int txlen, to_send;
 
 
-	if (unlikely(s->port.x_char)) {
-		regmap_write(s->regmap, MAX310X_THR_REG, s->port.x_char);
-		s->port.icount.tx++;
-		s->port.x_char = 0;
+	if (unlikely(port->x_char)) {
+		max310x_port_write(port, MAX310X_THR_REG, port->x_char);
+		port->icount.tx++;
+		port->x_char = 0;
 		return;
 		return;
 	}
 	}
 
 
-	if (uart_circ_empty(xmit) || uart_tx_stopped(&s->port))
+	if (uart_circ_empty(xmit) || uart_tx_stopped(port))
 		return;
 		return;
 
 
 	/* Get length of data pending in circular buffer */
 	/* Get length of data pending in circular buffer */
 	to_send = uart_circ_chars_pending(xmit);
 	to_send = uart_circ_chars_pending(xmit);
 	if (likely(to_send)) {
 	if (likely(to_send)) {
 		/* Limit to size of TX FIFO */
 		/* Limit to size of TX FIFO */
-		regmap_read(s->regmap, MAX310X_TXFIFOLVL_REG, &txlen);
-		txlen = MAX310X_FIFO_SIZE - txlen;
+		txlen = max310x_port_read(port, MAX310X_TXFIFOLVL_REG);
+		txlen = port->fifosize - txlen;
 		to_send = (to_send > txlen) ? txlen : to_send;
 		to_send = (to_send > txlen) ? txlen : to_send;
 
 
-		dev_dbg(s->port.dev, "TX Len = %u\n", to_send);
-
 		/* Add data to send */
 		/* Add data to send */
-		s->port.icount.tx += to_send;
+		port->icount.tx += to_send;
 		while (to_send--) {
 		while (to_send--) {
-			regmap_write(s->regmap, MAX310X_THR_REG,
-				     xmit->buf[xmit->tail]);
+			max310x_port_write(port, MAX310X_THR_REG,
+					   xmit->buf[xmit->tail]);
 			xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
 			xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
 		};
 		};
 	}
 	}
 
 
 	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
 	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
-		uart_write_wakeup(&s->port);
+		uart_write_wakeup(port);
 }
 }
 
 
-static irqreturn_t max310x_ist(int irq, void *dev_id)
+static void max310x_port_irq(struct max310x_port *s, int portno)
 {
 {
-	struct max310x_port *s = (struct max310x_port *)dev_id;
-	unsigned int ists = 0, lsr = 0, rxlen = 0;
+	struct uart_port *port = &s->p[portno].port;
 
 
-	mutex_lock(&s->max310x_mutex);
+	do {
+		unsigned int ists, lsr, rxlen;
 
 
-	for (;;) {
 		/* Read IRQ status & RX FIFO level */
 		/* Read IRQ status & RX FIFO level */
-		regmap_read(s->regmap, MAX310X_IRQSTS_REG, &ists);
-		regmap_read(s->regmap, MAX310X_LSR_IRQSTS_REG, &lsr);
-		regmap_read(s->regmap, MAX310X_RXFIFOLVL_REG, &rxlen);
-		if (!ists && !(lsr & MAX310X_LSR_RXTO_BIT) && !rxlen)
+		ists = max310x_port_read(port, MAX310X_IRQSTS_REG);
+		rxlen = max310x_port_read(port, MAX310X_RXFIFOLVL_REG);
+		if (!ists && !rxlen)
 			break;
 			break;
 
 
-		dev_dbg(s->port.dev, "IRQ status: 0x%02x\n", ists);
-
-		if (rxlen)
-			max310x_handle_rx(s, rxlen);
-		if (ists & MAX310X_IRQ_TX)
-			max310x_handle_tx(s);
-		if (ists & MAX310X_IRQ_CTS_BIT)
-			uart_handle_cts_change(&s->port,
+		if (ists & MAX310X_IRQ_CTS_BIT) {
+			lsr = max310x_port_read(port, MAX310X_LSR_IRQSTS_REG);
+			uart_handle_cts_change(port,
 					       !!(lsr & MAX310X_LSR_CTS_BIT));
 					       !!(lsr & MAX310X_LSR_CTS_BIT));
-	}
+		}
+		if (rxlen)
+			max310x_handle_rx(port, rxlen);
+		if (ists & MAX310X_IRQ_TXEMPTY_BIT) {
+			mutex_lock(&s->mutex);
+			max310x_handle_tx(port);
+			mutex_unlock(&s->mutex);
+		}
+	} while (1);
+}
+
+static irqreturn_t max310x_ist(int irq, void *dev_id)
+{
+	struct max310x_port *s = (struct max310x_port *)dev_id;
 
 
-	mutex_unlock(&s->max310x_mutex);
+	if (s->uart.nr > 1) {
+		do {
+			unsigned int val = ~0;
+
+			WARN_ON_ONCE(regmap_read(s->regmap,
+						 MAX310X_GLOBALIRQ_REG, &val));
+			val = ((1 << s->uart.nr) - 1) & ~val;
+			if (!val)
+				break;
+			max310x_port_irq(s, fls(val) - 1);
+		} while (1);
+	} else
+		max310x_port_irq(s, 0);
 
 
 	return IRQ_HANDLED;
 	return IRQ_HANDLED;
 }
 }
 
 
 static void max310x_wq_proc(struct work_struct *ws)
 static void max310x_wq_proc(struct work_struct *ws)
 {
 {
-	struct max310x_port *s = container_of(ws, struct max310x_port, tx_work);
+	struct max310x_one *one = container_of(ws, struct max310x_one, tx_work);
+	struct max310x_port *s = dev_get_drvdata(one->port.dev);
 
 
-	mutex_lock(&s->max310x_mutex);
-	max310x_handle_tx(s);
-	mutex_unlock(&s->max310x_mutex);
+	mutex_lock(&s->mutex);
+	max310x_handle_tx(&one->port);
+	mutex_unlock(&s->mutex);
 }
 }
 
 
 static void max310x_start_tx(struct uart_port *port)
 static void max310x_start_tx(struct uart_port *port)
 {
 {
-	struct max310x_port *s = container_of(port, struct max310x_port, port);
-
-	queue_work(s->wq, &s->tx_work);
-}
-
-static void max310x_stop_tx(struct uart_port *port)
-{
-	/* Do nothing */
-}
+	struct max310x_one *one = container_of(port, struct max310x_one, port);
 
 
-static void max310x_stop_rx(struct uart_port *port)
-{
-	/* Do nothing */
+	if (!work_pending(&one->tx_work))
+		schedule_work(&one->tx_work);
 }
 }
 
 
 static unsigned int max310x_tx_empty(struct uart_port *port)
 static unsigned int max310x_tx_empty(struct uart_port *port)
 {
 {
-	unsigned int val = 0;
-	struct max310x_port *s = container_of(port, struct max310x_port, port);
+	unsigned int lvl, sts;
 
 
-	mutex_lock(&s->max310x_mutex);
-	regmap_read(s->regmap, MAX310X_TXFIFOLVL_REG, &val);
-	mutex_unlock(&s->max310x_mutex);
+	lvl = max310x_port_read(port, MAX310X_TXFIFOLVL_REG);
+	sts = max310x_port_read(port, MAX310X_IRQSTS_REG);
 
 
-	return val ? 0 : TIOCSER_TEMT;
-}
-
-static void max310x_enable_ms(struct uart_port *port)
-{
-	/* Modem status not supported */
+	return ((sts & MAX310X_IRQ_TXEMPTY_BIT) && !lvl) ? TIOCSER_TEMT : 0;
 }
 }
 
 
 static unsigned int max310x_get_mctrl(struct uart_port *port)
 static unsigned int max310x_get_mctrl(struct uart_port *port)
@@ -644,28 +791,20 @@ static void max310x_set_mctrl(struct uart_port *port, unsigned int mctrl)
 
 
 static void max310x_break_ctl(struct uart_port *port, int break_state)
 static void max310x_break_ctl(struct uart_port *port, int break_state)
 {
 {
-	struct max310x_port *s = container_of(port, struct max310x_port, port);
-
-	mutex_lock(&s->max310x_mutex);
-	regmap_update_bits(s->regmap, MAX310X_LCR_REG,
-			   MAX310X_LCR_TXBREAK_BIT,
-			   break_state ? MAX310X_LCR_TXBREAK_BIT : 0);
-	mutex_unlock(&s->max310x_mutex);
+	max310x_port_update(port, MAX310X_LCR_REG,
+			    MAX310X_LCR_TXBREAK_BIT,
+			    break_state ? MAX310X_LCR_TXBREAK_BIT : 0);
 }
 }
 
 
 static void max310x_set_termios(struct uart_port *port,
 static void max310x_set_termios(struct uart_port *port,
 				struct ktermios *termios,
 				struct ktermios *termios,
 				struct ktermios *old)
 				struct ktermios *old)
 {
 {
-	struct max310x_port *s = container_of(port, struct max310x_port, port);
 	unsigned int lcr, flow = 0;
 	unsigned int lcr, flow = 0;
 	int baud;
 	int baud;
 
 
-	mutex_lock(&s->max310x_mutex);
-
 	/* Mask termios capabilities we don't support */
 	/* Mask termios capabilities we don't support */
 	termios->c_cflag &= ~CMSPAR;
 	termios->c_cflag &= ~CMSPAR;
-	termios->c_iflag &= ~IXANY;
 
 
 	/* Word size */
 	/* Word size */
 	switch (termios->c_cflag & CSIZE) {
 	switch (termios->c_cflag & CSIZE) {
@@ -696,7 +835,7 @@ static void max310x_set_termios(struct uart_port *port,
 		lcr |= MAX310X_LCR_STOPLEN_BIT; /* 2 stops */
 		lcr |= MAX310X_LCR_STOPLEN_BIT; /* 2 stops */
 
 
 	/* Update LCR register */
 	/* Update LCR register */
-	regmap_write(s->regmap, MAX310X_LCR_REG, lcr);
+	max310x_port_write(port, MAX310X_LCR_REG, lcr);
 
 
 	/* Set read status mask */
 	/* Set read status mask */
 	port->read_status_mask = MAX310X_LSR_RXOVR_BIT;
 	port->read_status_mask = MAX310X_LSR_RXOVR_BIT;
@@ -717,8 +856,8 @@ static void max310x_set_termios(struct uart_port *port,
 					    MAX310X_LSR_RXBRK_BIT;
 					    MAX310X_LSR_RXBRK_BIT;
 
 
 	/* Configure flow control */
 	/* Configure flow control */
-	regmap_write(s->regmap, MAX310X_XON1_REG, termios->c_cc[VSTART]);
-	regmap_write(s->regmap, MAX310X_XOFF1_REG, termios->c_cc[VSTOP]);
+	max310x_port_write(port, MAX310X_XON1_REG, termios->c_cc[VSTART]);
+	max310x_port_write(port, MAX310X_XOFF1_REG, termios->c_cc[VSTOP]);
 	if (termios->c_cflag & CRTSCTS)
 	if (termios->c_cflag & CRTSCTS)
 		flow |= MAX310X_FLOWCTRL_AUTOCTS_BIT |
 		flow |= MAX310X_FLOWCTRL_AUTOCTS_BIT |
 			MAX310X_FLOWCTRL_AUTORTS_BIT;
 			MAX310X_FLOWCTRL_AUTORTS_BIT;
@@ -728,7 +867,7 @@ static void max310x_set_termios(struct uart_port *port,
 	if (termios->c_iflag & IXOFF)
 	if (termios->c_iflag & IXOFF)
 		flow |= MAX310X_FLOWCTRL_SWFLOW1_BIT |
 		flow |= MAX310X_FLOWCTRL_SWFLOW1_BIT |
 			MAX310X_FLOWCTRL_SWFLOWEN_BIT;
 			MAX310X_FLOWCTRL_SWFLOWEN_BIT;
-	regmap_write(s->regmap, MAX310X_FLOWCTRL_REG, flow);
+	max310x_port_write(port, MAX310X_FLOWCTRL_REG, flow);
 
 
 	/* Get baud rate generator configuration */
 	/* Get baud rate generator configuration */
 	baud = uart_get_baud_rate(port, termios, old,
 	baud = uart_get_baud_rate(port, termios, old,
@@ -736,36 +875,30 @@ static void max310x_set_termios(struct uart_port *port,
 				  port->uartclk / 4);
 				  port->uartclk / 4);
 
 
 	/* Setup baudrate generator */
 	/* Setup baudrate generator */
-	max310x_set_baud(s, baud);
+	max310x_set_baud(port, baud);
 
 
 	/* Update timeout according to new baud rate */
 	/* Update timeout according to new baud rate */
 	uart_update_timeout(port, termios->c_cflag, baud);
 	uart_update_timeout(port, termios->c_cflag, baud);
-
-	mutex_unlock(&s->max310x_mutex);
 }
 }
 
 
 static int max310x_startup(struct uart_port *port)
 static int max310x_startup(struct uart_port *port)
 {
 {
 	unsigned int val, line = port->line;
 	unsigned int val, line = port->line;
-	struct max310x_port *s = container_of(port, struct max310x_port, port);
-
-	if (s->pdata->suspend)
-		s->pdata->suspend(0);
+	struct max310x_port *s = dev_get_drvdata(port->dev);
 
 
-	mutex_lock(&s->max310x_mutex);
+	s->devtype->power(port, 1);
 
 
 	/* Configure baud rate, 9600 as default */
 	/* Configure baud rate, 9600 as default */
-	max310x_set_baud(s, 9600);
+	max310x_set_baud(port, 9600);
 
 
 	/* Configure LCR register, 8N1 mode by default */
 	/* Configure LCR register, 8N1 mode by default */
-	val = MAX310X_LCR_WORD_LEN_8;
-	regmap_write(s->regmap, MAX310X_LCR_REG, val);
+	max310x_port_write(port, MAX310X_LCR_REG, MAX310X_LCR_WORD_LEN_8);
 
 
 	/* Configure MODE1 register */
 	/* Configure MODE1 register */
-	regmap_update_bits(s->regmap, MAX310X_MODE1_REG,
-			   MAX310X_MODE1_TRNSCVCTRL_BIT,
-			   (s->pdata->uart_flags[line] & MAX310X_AUTO_DIR_CTRL)
-			   ? MAX310X_MODE1_TRNSCVCTRL_BIT : 0);
+	max310x_port_update(port, MAX310X_MODE1_REG,
+			    MAX310X_MODE1_TRNSCVCTRL_BIT,
+			    (s->pdata->uart_flags[line] & MAX310X_AUTO_DIR_CTRL)
+			    ? MAX310X_MODE1_TRNSCVCTRL_BIT : 0);
 
 
 	/* Configure MODE2 register */
 	/* Configure MODE2 register */
 	val = MAX310X_MODE2_RXEMPTINV_BIT;
 	val = MAX310X_MODE2_RXEMPTINV_BIT;
@@ -776,63 +909,40 @@ static int max310x_startup(struct uart_port *port)
 
 
 	/* Reset FIFOs */
 	/* Reset FIFOs */
 	val |= MAX310X_MODE2_FIFORST_BIT;
 	val |= MAX310X_MODE2_FIFORST_BIT;
-	regmap_write(s->regmap, MAX310X_MODE2_REG, val);
-
-	/* Configure FIFO trigger level register */
-	/* RX FIFO trigger for 16 words, TX FIFO trigger for 64 words */
-	val = MAX310X_FIFOTRIGLVL_RX(16) | MAX310X_FIFOTRIGLVL_TX(64);
-	regmap_write(s->regmap, MAX310X_FIFOTRIGLVL_REG, val);
+	max310x_port_write(port, MAX310X_MODE2_REG, val);
+	max310x_port_update(port, MAX310X_MODE2_REG,
+			    MAX310X_MODE2_FIFORST_BIT, 0);
 
 
 	/* Configure flow control levels */
 	/* Configure flow control levels */
 	/* Flow control halt level 96, resume level 48 */
 	/* Flow control halt level 96, resume level 48 */
-	val = MAX310X_FLOWLVL_RES(48) | MAX310X_FLOWLVL_HALT(96);
-	regmap_write(s->regmap, MAX310X_FLOWLVL_REG, val);
-
-	/* Clear timeout register */
-	regmap_write(s->regmap, MAX310X_RXTO_REG, 0);
+	max310x_port_write(port, MAX310X_FLOWLVL_REG,
+			   MAX310X_FLOWLVL_RES(48) | MAX310X_FLOWLVL_HALT(96));
 
 
-	/* Configure LSR interrupt enable register */
-	/* Enable RX timeout interrupt */
-	val = MAX310X_LSR_RXTO_BIT;
-	regmap_write(s->regmap, MAX310X_LSR_IRQEN_REG, val);
+	/* Clear IRQ status register */
+	max310x_port_read(port, MAX310X_IRQSTS_REG);
 
 
-	/* Clear FIFO reset */
-	regmap_update_bits(s->regmap, MAX310X_MODE2_REG,
-			   MAX310X_MODE2_FIFORST_BIT, 0);
-
-	/* Clear IRQ status register by reading it */
-	regmap_read(s->regmap, MAX310X_IRQSTS_REG, &val);
-
-	/* Configure interrupt enable register */
-	/* Enable CTS change interrupt */
-	val = MAX310X_IRQ_CTS_BIT;
-	/* Enable RX, TX interrupts */
-	val |= MAX310X_IRQ_RX | MAX310X_IRQ_TX;
-	regmap_write(s->regmap, MAX310X_IRQEN_REG, val);
-
-	mutex_unlock(&s->max310x_mutex);
+	/* Enable RX, TX, CTS change interrupts */
+	val = MAX310X_IRQ_RXEMPTY_BIT | MAX310X_IRQ_TXEMPTY_BIT;
+	max310x_port_write(port, MAX310X_IRQEN_REG, val | MAX310X_IRQ_CTS_BIT);
 
 
 	return 0;
 	return 0;
 }
 }
 
 
 static void max310x_shutdown(struct uart_port *port)
 static void max310x_shutdown(struct uart_port *port)
 {
 {
-	struct max310x_port *s = container_of(port, struct max310x_port, port);
+	struct max310x_port *s = dev_get_drvdata(port->dev);
 
 
 	/* Disable all interrupts */
 	/* Disable all interrupts */
-	mutex_lock(&s->max310x_mutex);
-	regmap_write(s->regmap, MAX310X_IRQEN_REG, 0);
-	mutex_unlock(&s->max310x_mutex);
+	max310x_port_write(port, MAX310X_IRQEN_REG, 0);
 
 
-	if (s->pdata->suspend)
-		s->pdata->suspend(1);
+	s->devtype->power(port, 0);
 }
 }
 
 
 static const char *max310x_type(struct uart_port *port)
 static const char *max310x_type(struct uart_port *port)
 {
 {
-	struct max310x_port *s = container_of(port, struct max310x_port, port);
+	struct max310x_port *s = dev_get_drvdata(port->dev);
 
 
-	return (port->type == PORT_MAX310X) ? s->name : NULL;
+	return (port->type == PORT_MAX310X) ? s->devtype->name : NULL;
 }
 }
 
 
 static int max310x_request_port(struct uart_port *port)
 static int max310x_request_port(struct uart_port *port)
@@ -841,134 +951,99 @@ static int max310x_request_port(struct uart_port *port)
 	return 0;
 	return 0;
 }
 }
 
 
-static void max310x_release_port(struct uart_port *port)
-{
-	/* Do nothing */
-}
-
 static void max310x_config_port(struct uart_port *port, int flags)
 static void max310x_config_port(struct uart_port *port, int flags)
 {
 {
 	if (flags & UART_CONFIG_TYPE)
 	if (flags & UART_CONFIG_TYPE)
 		port->type = PORT_MAX310X;
 		port->type = PORT_MAX310X;
 }
 }
 
 
-static int max310x_verify_port(struct uart_port *port, struct serial_struct *ser)
+static int max310x_verify_port(struct uart_port *port, struct serial_struct *s)
 {
 {
-	if ((ser->type == PORT_UNKNOWN) || (ser->type == PORT_MAX310X))
-		return 0;
-	if (ser->irq == port->irq)
-		return 0;
+	if ((s->type != PORT_UNKNOWN) && (s->type != PORT_MAX310X))
+		return -EINVAL;
+	if (s->irq != port->irq)
+		return -EINVAL;
 
 
-	return -EINVAL;
+	return 0;
 }
 }
 
 
-static struct uart_ops max310x_ops = {
+static void max310x_null_void(struct uart_port *port)
+{
+	/* Do nothing */
+}
+
+static const struct uart_ops max310x_ops = {
 	.tx_empty	= max310x_tx_empty,
 	.tx_empty	= max310x_tx_empty,
 	.set_mctrl	= max310x_set_mctrl,
 	.set_mctrl	= max310x_set_mctrl,
 	.get_mctrl	= max310x_get_mctrl,
 	.get_mctrl	= max310x_get_mctrl,
-	.stop_tx	= max310x_stop_tx,
+	.stop_tx	= max310x_null_void,
 	.start_tx	= max310x_start_tx,
 	.start_tx	= max310x_start_tx,
-	.stop_rx	= max310x_stop_rx,
-	.enable_ms	= max310x_enable_ms,
+	.stop_rx	= max310x_null_void,
+	.enable_ms	= max310x_null_void,
 	.break_ctl	= max310x_break_ctl,
 	.break_ctl	= max310x_break_ctl,
 	.startup	= max310x_startup,
 	.startup	= max310x_startup,
 	.shutdown	= max310x_shutdown,
 	.shutdown	= max310x_shutdown,
 	.set_termios	= max310x_set_termios,
 	.set_termios	= max310x_set_termios,
 	.type		= max310x_type,
 	.type		= max310x_type,
 	.request_port	= max310x_request_port,
 	.request_port	= max310x_request_port,
-	.release_port	= max310x_release_port,
+	.release_port	= max310x_null_void,
 	.config_port	= max310x_config_port,
 	.config_port	= max310x_config_port,
 	.verify_port	= max310x_verify_port,
 	.verify_port	= max310x_verify_port,
 };
 };
 
 
-#ifdef CONFIG_PM_SLEEP
-
-static int max310x_suspend(struct device *dev)
+static int __maybe_unused max310x_suspend(struct device *dev)
 {
 {
-	int ret;
 	struct max310x_port *s = dev_get_drvdata(dev);
 	struct max310x_port *s = dev_get_drvdata(dev);
+	int i;
 
 
-	dev_dbg(dev, "Suspend\n");
-
-	ret = uart_suspend_port(&s->uart, &s->port);
-
-	mutex_lock(&s->max310x_mutex);
-
-	/* Enable sleep mode */
-	regmap_update_bits(s->regmap, MAX310X_MODE1_REG,
-			   MAX310X_MODE1_FORCESLEEP_BIT,
-			   MAX310X_MODE1_FORCESLEEP_BIT);
-
-	mutex_unlock(&s->max310x_mutex);
-
-	if (s->pdata->suspend)
-		s->pdata->suspend(1);
+	for (i = 0; i < s->uart.nr; i++) {
+		uart_suspend_port(&s->uart, &s->p[i].port);
+		s->devtype->power(&s->p[i].port, 0);
+	}
 
 
-	return ret;
+	return 0;
 }
 }
 
 
-static int max310x_resume(struct device *dev)
+static int __maybe_unused max310x_resume(struct device *dev)
 {
 {
 	struct max310x_port *s = dev_get_drvdata(dev);
 	struct max310x_port *s = dev_get_drvdata(dev);
+	int i;
 
 
-	dev_dbg(dev, "Resume\n");
-
-	if (s->pdata->suspend)
-		s->pdata->suspend(0);
-
-	mutex_lock(&s->max310x_mutex);
-
-	/* Disable sleep mode */
-	regmap_update_bits(s->regmap, MAX310X_MODE1_REG,
-			   MAX310X_MODE1_FORCESLEEP_BIT,
-			   0);
-
-	max310x_wait_pll(s);
-
-	mutex_unlock(&s->max310x_mutex);
+	for (i = 0; i < s->uart.nr; i++) {
+		s->devtype->power(&s->p[i].port, 1);
+		uart_resume_port(&s->uart, &s->p[i].port);
+	}
 
 
-	return uart_resume_port(&s->uart, &s->port);
+	return 0;
 }
 }
 
 
-static SIMPLE_DEV_PM_OPS(max310x_pm_ops, max310x_suspend, max310x_resume);
-#define MAX310X_PM_OPS (&max310x_pm_ops)
-
-#else
-#define MAX310X_PM_OPS NULL
-#endif
-
 #ifdef CONFIG_GPIOLIB
 #ifdef CONFIG_GPIOLIB
 static int max310x_gpio_get(struct gpio_chip *chip, unsigned offset)
 static int max310x_gpio_get(struct gpio_chip *chip, unsigned offset)
 {
 {
-	unsigned int val = 0;
+	unsigned int val;
 	struct max310x_port *s = container_of(chip, struct max310x_port, gpio);
 	struct max310x_port *s = container_of(chip, struct max310x_port, gpio);
+	struct uart_port *port = &s->p[offset / 4].port;
 
 
-	mutex_lock(&s->max310x_mutex);
-	regmap_read(s->regmap, MAX310X_GPIODATA_REG, &val);
-	mutex_unlock(&s->max310x_mutex);
+	val = max310x_port_read(port, MAX310X_GPIODATA_REG);
 
 
-	return !!((val >> 4) & (1 << offset));
+	return !!((val >> 4) & (1 << (offset % 4)));
 }
 }
 
 
 static void max310x_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
 static void max310x_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
 {
 {
 	struct max310x_port *s = container_of(chip, struct max310x_port, gpio);
 	struct max310x_port *s = container_of(chip, struct max310x_port, gpio);
+	struct uart_port *port = &s->p[offset / 4].port;
 
 
-	mutex_lock(&s->max310x_mutex);
-	regmap_update_bits(s->regmap, MAX310X_GPIODATA_REG, 1 << offset, value ?
-							    1 << offset : 0);
-	mutex_unlock(&s->max310x_mutex);
+	max310x_port_update(port, MAX310X_GPIODATA_REG, 1 << (offset % 4),
+			    value ? 1 << (offset % 4) : 0);
 }
 }
 
 
 static int max310x_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
 static int max310x_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
 {
 {
 	struct max310x_port *s = container_of(chip, struct max310x_port, gpio);
 	struct max310x_port *s = container_of(chip, struct max310x_port, gpio);
+	struct uart_port *port = &s->p[offset / 4].port;
 
 
-	mutex_lock(&s->max310x_mutex);
-
-	regmap_update_bits(s->regmap, MAX310X_GPIOCFG_REG, 1 << offset, 0);
-
-	mutex_unlock(&s->max310x_mutex);
+	max310x_port_update(port, MAX310X_GPIOCFG_REG, 1 << (offset % 4), 0);
 
 
 	return 0;
 	return 0;
 }
 }
@@ -977,74 +1052,42 @@ static int max310x_gpio_direction_output(struct gpio_chip *chip,
 					 unsigned offset, int value)
 					 unsigned offset, int value)
 {
 {
 	struct max310x_port *s = container_of(chip, struct max310x_port, gpio);
 	struct max310x_port *s = container_of(chip, struct max310x_port, gpio);
+	struct uart_port *port = &s->p[offset / 4].port;
 
 
-	mutex_lock(&s->max310x_mutex);
-
-	regmap_update_bits(s->regmap, MAX310X_GPIOCFG_REG, 1 << offset,
-							   1 << offset);
-	regmap_update_bits(s->regmap, MAX310X_GPIODATA_REG, 1 << offset, value ?
-							    1 << offset : 0);
-
-	mutex_unlock(&s->max310x_mutex);
+	max310x_port_update(port, MAX310X_GPIODATA_REG, 1 << (offset % 4),
+			    value ? 1 << (offset % 4) : 0);
+	max310x_port_update(port, MAX310X_GPIOCFG_REG, 1 << (offset % 4),
+			    1 << (offset % 4));
 
 
 	return 0;
 	return 0;
 }
 }
 #endif
 #endif
 
 
-/* Generic platform data */
-static struct max310x_pdata generic_plat_data = {
-	.driver_flags	= MAX310X_EXT_CLK,
-	.uart_flags[0]	= MAX310X_ECHO_SUPRESS,
-	.frequency	= 26000000,
-};
-
-static int max310x_probe(struct spi_device *spi)
+static int max310x_probe(struct device *dev, int is_spi,
+			 struct max310x_devtype *devtype, int irq)
 {
 {
 	struct max310x_port *s;
 	struct max310x_port *s;
-	struct device *dev = &spi->dev;
-	int chiptype = spi_get_device_id(spi)->driver_data;
-	struct max310x_pdata *pdata = dev->platform_data;
-	unsigned int val = 0;
-	int ret;
+	struct max310x_pdata *pdata = dev_get_platdata(dev);
+	int i, ret, uartclk;
 
 
 	/* Check for IRQ */
 	/* Check for IRQ */
-	if (spi->irq <= 0) {
+	if (irq <= 0) {
 		dev_err(dev, "No IRQ specified\n");
 		dev_err(dev, "No IRQ specified\n");
 		return -ENOTSUPP;
 		return -ENOTSUPP;
 	}
 	}
 
 
+	if (!pdata) {
+		dev_err(dev, "No platform data supplied\n");
+		return -EINVAL;
+	}
+
 	/* Alloc port structure */
 	/* Alloc port structure */
-	s = devm_kzalloc(dev, sizeof(struct max310x_port), GFP_KERNEL);
+	s = devm_kzalloc(dev, sizeof(*s) +
+			 sizeof(struct max310x_one) * devtype->nr, GFP_KERNEL);
 	if (!s) {
 	if (!s) {
 		dev_err(dev, "Error allocating port structure\n");
 		dev_err(dev, "Error allocating port structure\n");
 		return -ENOMEM;
 		return -ENOMEM;
 	}
 	}
-	dev_set_drvdata(dev, s);
-
-	if (!pdata) {
-		dev_warn(dev, "No platform data supplied, using defaults\n");
-		pdata = &generic_plat_data;
-	}
-	s->pdata = pdata;
-
-	/* Individual chip settings */
-	switch (chiptype) {
-	case MAX310X_TYPE_MAX3107:
-		s->name = "MAX3107";
-		s->nr_gpio = 4;
-		s->uart.nr = 1;
-		s->regcfg.max_register = 0x1f;
-		break;
-	case MAX310X_TYPE_MAX3108:
-		s->name = "MAX3108";
-		s->nr_gpio = 4;
-		s->uart.nr = 1;
-		s->regcfg.max_register = 0x1e;
-		break;
-	default:
-		dev_err(dev, "Unsupported chip type %i\n", chiptype);
-		return -ENOTSUPP;
-	}
 
 
 	/* Check input frequency */
 	/* Check input frequency */
 	if ((pdata->driver_flags & MAX310X_EXT_CLK) &&
 	if ((pdata->driver_flags & MAX310X_EXT_CLK) &&
@@ -1055,13 +1098,11 @@ static int max310x_probe(struct spi_device *spi)
 	   ((pdata->frequency < 1000000) || (pdata->frequency > 4000000)))
 	   ((pdata->frequency < 1000000) || (pdata->frequency > 4000000)))
 		goto err_freq;
 		goto err_freq;
 
 
-	mutex_init(&s->max310x_mutex);
+	s->pdata = pdata;
+	s->devtype = devtype;
+	dev_set_drvdata(dev, s);
 
 
-	/* Setup SPI bus */
-	spi->mode		= SPI_MODE_0;
-	spi->bits_per_word	= 8;
-	spi->max_speed_hz	= 26000000;
-	spi_setup(spi);
+	mutex_init(&s->mutex);
 
 
 	/* Setup regmap */
 	/* Setup regmap */
 	s->regcfg.reg_bits		= 8;
 	s->regcfg.reg_bits		= 8;
@@ -1069,109 +1110,100 @@ static int max310x_probe(struct spi_device *spi)
 	s->regcfg.read_flag_mask	= 0x00;
 	s->regcfg.read_flag_mask	= 0x00;
 	s->regcfg.write_flag_mask	= 0x80;
 	s->regcfg.write_flag_mask	= 0x80;
 	s->regcfg.cache_type		= REGCACHE_RBTREE;
 	s->regcfg.cache_type		= REGCACHE_RBTREE;
-	s->regcfg.writeable_reg		= max3107_8_reg_writeable;
+	s->regcfg.writeable_reg		= max310x_reg_writeable;
 	s->regcfg.volatile_reg		= max310x_reg_volatile;
 	s->regcfg.volatile_reg		= max310x_reg_volatile;
 	s->regcfg.precious_reg		= max310x_reg_precious;
 	s->regcfg.precious_reg		= max310x_reg_precious;
-	s->regmap = devm_regmap_init_spi(spi, &s->regcfg);
+	s->regcfg.max_register		= devtype->nr * 0x20 - 1;
+
+	if (IS_ENABLED(CONFIG_SPI_MASTER) && is_spi) {
+		struct spi_device *spi = to_spi_device(dev);
+
+		s->regmap = devm_regmap_init_spi(spi, &s->regcfg);
+	} else
+		return -ENOTSUPP;
+
 	if (IS_ERR(s->regmap)) {
 	if (IS_ERR(s->regmap)) {
-		ret = PTR_ERR(s->regmap);
 		dev_err(dev, "Failed to initialize register map\n");
 		dev_err(dev, "Failed to initialize register map\n");
-		goto err_out;
-	}
-
-	/* Reset chip & check SPI function */
-	ret = regmap_write(s->regmap, MAX310X_MODE2_REG, MAX310X_MODE2_RST_BIT);
-	if (ret) {
-		dev_err(dev, "SPI transfer failed\n");
-		goto err_out;
-	}
-	/* Clear chip reset */
-	regmap_write(s->regmap, MAX310X_MODE2_REG, 0);
-
-	switch (chiptype) {
-	case MAX310X_TYPE_MAX3107:
-		/* Check REV ID to ensure we are talking to what we expect */
-		regmap_read(s->regmap, MAX3107_REVID_REG, &val);
-		if (((val & MAX3107_REV_MASK) != MAX3107_REV_ID)) {
-			dev_err(dev, "%s ID 0x%02x does not match\n",
-				s->name, val);
-			ret = -ENODEV;
-			goto err_out;
-		}
-		break;
-	case MAX310X_TYPE_MAX3108:
-		/* MAX3108 have not REV ID register, we just check default value
-		 * from clocksource register to make sure everything works.
-		 */
-		regmap_read(s->regmap, MAX310X_CLKSRC_REG, &val);
-		if (val != (MAX310X_CLKSRC_EXTCLK_BIT |
-			    MAX310X_CLKSRC_PLLBYP_BIT)) {
-			dev_err(dev, "%s not present\n", s->name);
-			ret = -ENODEV;
-			goto err_out;
-		}
-		break;
+		return PTR_ERR(s->regmap);
 	}
 	}
 
 
 	/* Board specific configure */
 	/* Board specific configure */
-	if (pdata->init)
-		pdata->init();
-	if (pdata->suspend)
-		pdata->suspend(0);
-
-	/* Calculate referecne clock */
-	s->uartclk = max310x_set_ref_clk(s);
-
-	/* Disable all interrupts */
-	regmap_write(s->regmap, MAX310X_IRQEN_REG, 0);
-
-	/* Setup MODE1 register */
-	val = MAX310X_MODE1_IRQSEL_BIT; /* Enable IRQ pin */
-	if (pdata->driver_flags & MAX310X_AUTOSLEEP)
-		val = MAX310X_MODE1_AUTOSLEEP_BIT;
-	regmap_write(s->regmap, MAX310X_MODE1_REG, val);
-
-	/* Setup interrupt */
-	ret = devm_request_threaded_irq(dev, spi->irq, NULL, max310x_ist,
-					IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
-					dev_name(dev), s);
-	if (ret) {
-		dev_err(dev, "Unable to reguest IRQ %i\n", spi->irq);
-		goto err_out;
+	if (s->pdata->init)
+		s->pdata->init();
+
+	/* Check device to ensure we are talking to what we expect */
+	ret = devtype->detect(dev);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < devtype->nr; i++) {
+		unsigned int offs = i << 5;
+
+		/* Reset port */
+		regmap_write(s->regmap, MAX310X_MODE2_REG + offs,
+			     MAX310X_MODE2_RST_BIT);
+		/* Clear port reset */
+		regmap_write(s->regmap, MAX310X_MODE2_REG + offs, 0);
+
+		/* Wait for port startup */
+		do {
+			regmap_read(s->regmap,
+				    MAX310X_BRGDIVLSB_REG + offs, &ret);
+		} while (ret != 0x01);
+
+		regmap_update_bits(s->regmap, MAX310X_MODE1_REG + offs,
+				   MAX310X_MODE1_AUTOSLEEP_BIT,
+				   MAX310X_MODE1_AUTOSLEEP_BIT);
 	}
 	}
 
 
+	uartclk = max310x_set_ref_clk(s);
+	dev_dbg(dev, "Reference clock set to %i Hz\n", uartclk);
+
 	/* Register UART driver */
 	/* Register UART driver */
 	s->uart.owner		= THIS_MODULE;
 	s->uart.owner		= THIS_MODULE;
-	s->uart.driver_name	= dev_name(dev);
 	s->uart.dev_name	= "ttyMAX";
 	s->uart.dev_name	= "ttyMAX";
 	s->uart.major		= MAX310X_MAJOR;
 	s->uart.major		= MAX310X_MAJOR;
 	s->uart.minor		= MAX310X_MINOR;
 	s->uart.minor		= MAX310X_MINOR;
+	s->uart.nr		= devtype->nr;
 	ret = uart_register_driver(&s->uart);
 	ret = uart_register_driver(&s->uart);
 	if (ret) {
 	if (ret) {
 		dev_err(dev, "Registering UART driver failed\n");
 		dev_err(dev, "Registering UART driver failed\n");
-		goto err_out;
+		return ret;
 	}
 	}
 
 
-	/* Initialize workqueue for start TX */
-	s->wq = create_freezable_workqueue(dev_name(dev));
-	INIT_WORK(&s->tx_work, max310x_wq_proc);
-
-	/* Initialize UART port data */
-	s->port.line		= 0;
-	s->port.dev		= dev;
-	s->port.irq		= spi->irq;
-	s->port.type		= PORT_MAX310X;
-	s->port.fifosize	= MAX310X_FIFO_SIZE;
-	s->port.flags		= UPF_SKIP_TEST | UPF_FIXED_TYPE;
-	s->port.iotype		= UPIO_PORT;
-	s->port.membase		= (void __iomem *)0xffffffff; /* Bogus value */
-	s->port.uartclk		= s->uartclk;
-	s->port.ops		= &max310x_ops;
-	uart_add_one_port(&s->uart, &s->port);
+	for (i = 0; i < devtype->nr; i++) {
+		/* Initialize port data */
+		s->p[i].port.line	= i;
+		s->p[i].port.dev	= dev;
+		s->p[i].port.irq	= irq;
+		s->p[i].port.type	= PORT_MAX310X;
+		s->p[i].port.fifosize	= MAX310X_FIFO_SIZE;
+		s->p[i].port.flags	= UPF_SKIP_TEST | UPF_FIXED_TYPE |
+					  UPF_LOW_LATENCY;
+		s->p[i].port.iotype	= UPIO_PORT;
+		s->p[i].port.iobase	= i * 0x20;
+		s->p[i].port.membase	= (void __iomem *)~0;
+		s->p[i].port.uartclk	= uartclk;
+		s->p[i].port.ops	= &max310x_ops;
+		/* Disable all interrupts */
+		max310x_port_write(&s->p[i].port, MAX310X_IRQEN_REG, 0);
+		/* Clear IRQ status register */
+		max310x_port_read(&s->p[i].port, MAX310X_IRQSTS_REG);
+		/* Enable IRQ pin */
+		max310x_port_update(&s->p[i].port, MAX310X_MODE1_REG,
+				    MAX310X_MODE1_IRQSEL_BIT,
+				    MAX310X_MODE1_IRQSEL_BIT);
+		/* Initialize queue for start TX */
+		INIT_WORK(&s->p[i].tx_work, max310x_wq_proc);
+		/* Register port */
+		uart_add_one_port(&s->uart, &s->p[i].port);
+		/* Go to suspend mode */
+		devtype->power(&s->p[i].port, 0);
+	}
 
 
 #ifdef CONFIG_GPIOLIB
 #ifdef CONFIG_GPIOLIB
 	/* Setup GPIO cotroller */
 	/* Setup GPIO cotroller */
-	if (pdata->gpio_base) {
+	if (s->pdata->gpio_base) {
 		s->gpio.owner		= THIS_MODULE;
 		s->gpio.owner		= THIS_MODULE;
 		s->gpio.dev		= dev;
 		s->gpio.dev		= dev;
 		s->gpio.label		= dev_name(dev);
 		s->gpio.label		= dev_name(dev);
@@ -1179,86 +1211,107 @@ static int max310x_probe(struct spi_device *spi)
 		s->gpio.get		= max310x_gpio_get;
 		s->gpio.get		= max310x_gpio_get;
 		s->gpio.direction_output= max310x_gpio_direction_output;
 		s->gpio.direction_output= max310x_gpio_direction_output;
 		s->gpio.set		= max310x_gpio_set;
 		s->gpio.set		= max310x_gpio_set;
-		s->gpio.base		= pdata->gpio_base;
-		s->gpio.ngpio		= s->nr_gpio;
+		s->gpio.base		= s->pdata->gpio_base;
+		s->gpio.ngpio		= devtype->nr * 4;
 		s->gpio.can_sleep	= 1;
 		s->gpio.can_sleep	= 1;
-		if (gpiochip_add(&s->gpio)) {
-			/* Indicate that we should not call gpiochip_remove */
-			s->gpio.base = 0;
-		}
+		if (!gpiochip_add(&s->gpio))
+			s->gpio_used = 1;
 	} else
 	} else
 		dev_info(dev, "GPIO support not enabled\n");
 		dev_info(dev, "GPIO support not enabled\n");
 #endif
 #endif
 
 
-	/* Go to suspend mode */
-	if (pdata->suspend)
-		pdata->suspend(1);
+	/* Setup interrupt */
+	ret = devm_request_threaded_irq(dev, irq, NULL, max310x_ist,
+					IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+					dev_name(dev), s);
+	if (ret) {
+		dev_err(dev, "Unable to reguest IRQ %i\n", irq);
+#ifdef CONFIG_GPIOLIB
+		if (s->gpio_used)
+			WARN_ON(gpiochip_remove(&s->gpio));
+#endif
+	}
 
 
-	return 0;
+	return ret;
 
 
 err_freq:
 err_freq:
 	dev_err(dev, "Frequency parameter incorrect\n");
 	dev_err(dev, "Frequency parameter incorrect\n");
-	ret = -EINVAL;
-
-err_out:
-	dev_set_drvdata(dev, NULL);
-
-	return ret;
+	return -EINVAL;
 }
 }
 
 
-static int max310x_remove(struct spi_device *spi)
+static int max310x_remove(struct device *dev)
 {
 {
-	struct device *dev = &spi->dev;
 	struct max310x_port *s = dev_get_drvdata(dev);
 	struct max310x_port *s = dev_get_drvdata(dev);
-	int ret = 0;
-
-	dev_dbg(dev, "Removing port\n");
-
-	devm_free_irq(dev, s->port.irq, s);
-
-	destroy_workqueue(s->wq);
+	int i, ret = 0;
 
 
-	uart_remove_one_port(&s->uart, &s->port);
+	for (i = 0; i < s->uart.nr; i++) {
+		cancel_work_sync(&s->p[i].tx_work);
+		uart_remove_one_port(&s->uart, &s->p[i].port);
+		s->devtype->power(&s->p[i].port, 0);
+	}
 
 
 	uart_unregister_driver(&s->uart);
 	uart_unregister_driver(&s->uart);
 
 
 #ifdef CONFIG_GPIOLIB
 #ifdef CONFIG_GPIOLIB
-	if (s->pdata->gpio_base) {
+	if (s->gpio_used)
 		ret = gpiochip_remove(&s->gpio);
 		ret = gpiochip_remove(&s->gpio);
-		if (ret)
-			dev_err(dev, "Failed to remove gpio chip: %d\n", ret);
-	}
 #endif
 #endif
 
 
-	dev_set_drvdata(dev, NULL);
-
-	if (s->pdata->suspend)
-		s->pdata->suspend(1);
 	if (s->pdata->exit)
 	if (s->pdata->exit)
 		s->pdata->exit();
 		s->pdata->exit();
 
 
 	return ret;
 	return ret;
 }
 }
 
 
+#ifdef CONFIG_SPI_MASTER
+static int max310x_spi_probe(struct spi_device *spi)
+{
+	struct max310x_devtype *devtype =
+		(struct max310x_devtype *)spi_get_device_id(spi)->driver_data;
+	int ret;
+
+	/* Setup SPI bus */
+	spi->bits_per_word	= 8;
+	spi->mode		= spi->mode ? : SPI_MODE_0;
+	spi->max_speed_hz	= spi->max_speed_hz ? : 26000000;
+	ret = spi_setup(spi);
+	if (ret) {
+		dev_err(&spi->dev, "SPI setup failed\n");
+		return ret;
+	}
+
+	return max310x_probe(&spi->dev, 1, devtype, spi->irq);
+}
+
+static int max310x_spi_remove(struct spi_device *spi)
+{
+	return max310x_remove(&spi->dev);
+}
+
+static SIMPLE_DEV_PM_OPS(max310x_pm_ops, max310x_suspend, max310x_resume);
+
 static const struct spi_device_id max310x_id_table[] = {
 static const struct spi_device_id max310x_id_table[] = {
-	{ "max3107",	MAX310X_TYPE_MAX3107 },
-	{ "max3108",	MAX310X_TYPE_MAX3108 },
+	{ "max3107",	(kernel_ulong_t)&max3107_devtype, },
+	{ "max3108",	(kernel_ulong_t)&max3108_devtype, },
+	{ "max3109",	(kernel_ulong_t)&max3109_devtype, },
+	{ "max14830",	(kernel_ulong_t)&max14830_devtype, },
 	{ }
 	{ }
 };
 };
 MODULE_DEVICE_TABLE(spi, max310x_id_table);
 MODULE_DEVICE_TABLE(spi, max310x_id_table);
 
 
-static struct spi_driver max310x_driver = {
+static struct spi_driver max310x_uart_driver = {
 	.driver = {
 	.driver = {
-		.name	= "max310x",
+		.name	= MAX310X_NAME,
 		.owner	= THIS_MODULE,
 		.owner	= THIS_MODULE,
-		.pm	= MAX310X_PM_OPS,
+		.pm	= &max310x_pm_ops,
 	},
 	},
-	.probe		= max310x_probe,
-	.remove		= max310x_remove,
+	.probe		= max310x_spi_probe,
+	.remove		= max310x_spi_remove,
 	.id_table	= max310x_id_table,
 	.id_table	= max310x_id_table,
 };
 };
-module_spi_driver(max310x_driver);
+module_spi_driver(max310x_uart_driver);
+#endif
 
 
-MODULE_LICENSE("GPL v2");
+MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>");
 MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>");
 MODULE_DESCRIPTION("MAX310X serial driver");
 MODULE_DESCRIPTION("MAX310X serial driver");

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

@@ -24,6 +24,7 @@
 #include <linux/serial_core.h>
 #include <linux/serial_core.h>
 #include <linux/io.h>
 #include <linux/io.h>
 #include <linux/uaccess.h>
 #include <linux/uaccess.h>
+#include <linux/platform_device.h>
 #include <asm/coldfire.h>
 #include <asm/coldfire.h>
 #include <asm/mcfsim.h>
 #include <asm/mcfsim.h>
 #include <asm/mcfuart.h>
 #include <asm/mcfuart.h>
@@ -324,7 +325,9 @@ static void mcf_rx_chars(struct mcf_uart *pp)
 		uart_insert_char(port, status, MCFUART_USR_RXOVERRUN, ch, flag);
 		uart_insert_char(port, status, MCFUART_USR_RXOVERRUN, ch, flag);
 	}
 	}
 
 
+	spin_unlock(&port->lock);
 	tty_flip_buffer_push(&port->state->port);
 	tty_flip_buffer_push(&port->state->port);
+	spin_lock(&port->lock);
 }
 }
 
 
 /****************************************************************************/
 /****************************************************************************/
@@ -644,7 +647,7 @@ static struct uart_driver mcf_driver = {
 
 
 static int mcf_probe(struct platform_device *pdev)
 static int mcf_probe(struct platform_device *pdev)
 {
 {
-	struct mcf_platform_uart *platp = pdev->dev.platform_data;
+	struct mcf_platform_uart *platp = dev_get_platdata(&pdev->dev);
 	struct uart_port *port;
 	struct uart_port *port;
 	int i;
 	int i;
 
 

+ 10 - 4
drivers/tty/serial/mfd.c

@@ -386,7 +386,7 @@ static void serial_hsu_stop_tx(struct uart_port *port)
 
 
 /* This is always called in spinlock protected mode, so
 /* This is always called in spinlock protected mode, so
  * modify timeout timer is safe here */
  * modify timeout timer is safe here */
-void hsu_dma_rx(struct uart_hsu_port *up, u32 int_sts)
+void hsu_dma_rx(struct uart_hsu_port *up, u32 int_sts, unsigned long *flags)
 {
 {
 	struct hsu_dma_buffer *dbuf = &up->rxbuf;
 	struct hsu_dma_buffer *dbuf = &up->rxbuf;
 	struct hsu_dma_chan *chan = up->rxc;
 	struct hsu_dma_chan *chan = up->rxc;
@@ -438,7 +438,9 @@ void hsu_dma_rx(struct uart_hsu_port *up, u32 int_sts)
 					 | (0x1 << 16)
 					 | (0x1 << 16)
 					 | (0x1 << 24)	/* timeout bit, see HSU Errata 1 */
 					 | (0x1 << 24)	/* timeout bit, see HSU Errata 1 */
 					 );
 					 );
+	spin_unlock_irqrestore(&up->port.lock, *flags);
 	tty_flip_buffer_push(tport);
 	tty_flip_buffer_push(tport);
+	spin_lock_irqsave(&up->port.lock, *flags);
 
 
 	chan_writel(chan, HSU_CH_CR, 0x3);
 	chan_writel(chan, HSU_CH_CR, 0x3);
 
 
@@ -459,7 +461,8 @@ static void serial_hsu_stop_rx(struct uart_port *port)
 	}
 	}
 }
 }
 
 
-static inline void receive_chars(struct uart_hsu_port *up, int *status)
+static inline void receive_chars(struct uart_hsu_port *up, int *status,
+		unsigned long *flags)
 {
 {
 	unsigned int ch, flag;
 	unsigned int ch, flag;
 	unsigned int max_count = 256;
 	unsigned int max_count = 256;
@@ -519,7 +522,10 @@ static inline void receive_chars(struct uart_hsu_port *up, int *status)
 	ignore_char:
 	ignore_char:
 		*status = serial_in(up, UART_LSR);
 		*status = serial_in(up, UART_LSR);
 	} while ((*status & UART_LSR_DR) && max_count--);
 	} while ((*status & UART_LSR_DR) && max_count--);
+
+	spin_unlock_irqrestore(&up->port.lock, *flags);
 	tty_flip_buffer_push(&up->port.state->port);
 	tty_flip_buffer_push(&up->port.state->port);
+	spin_lock_irqsave(&up->port.lock, *flags);
 }
 }
 
 
 static void transmit_chars(struct uart_hsu_port *up)
 static void transmit_chars(struct uart_hsu_port *up)
@@ -613,7 +619,7 @@ static irqreturn_t port_irq(int irq, void *dev_id)
 
 
 	lsr = serial_in(up, UART_LSR);
 	lsr = serial_in(up, UART_LSR);
 	if (lsr & UART_LSR_DR)
 	if (lsr & UART_LSR_DR)
-		receive_chars(up, &lsr);
+		receive_chars(up, &lsr, &flags);
 	check_modem_status(up);
 	check_modem_status(up);
 
 
 	/* lsr will be renewed during the receive_chars */
 	/* lsr will be renewed during the receive_chars */
@@ -643,7 +649,7 @@ static inline void dma_chan_irq(struct hsu_dma_chan *chan)
 
 
 	/* Rx channel */
 	/* Rx channel */
 	if (chan->dirt == DMA_FROM_DEVICE)
 	if (chan->dirt == DMA_FROM_DEVICE)
-		hsu_dma_rx(up, int_sts);
+		hsu_dma_rx(up, int_sts, &flags);
 
 
 	/* Tx channel */
 	/* Tx channel */
 	if (chan->dirt == DMA_TO_DEVICE) {
 	if (chan->dirt == DMA_TO_DEVICE) {

+ 10 - 5
drivers/tty/serial/mpsc.c

@@ -934,7 +934,7 @@ static int serial_polled;
  ******************************************************************************
  ******************************************************************************
  */
  */
 
 
-static int mpsc_rx_intr(struct mpsc_port_info *pi)
+static int mpsc_rx_intr(struct mpsc_port_info *pi, unsigned long *flags)
 {
 {
 	struct mpsc_rx_desc *rxre;
 	struct mpsc_rx_desc *rxre;
 	struct tty_port *port = &pi->port.state->port;
 	struct tty_port *port = &pi->port.state->port;
@@ -969,8 +969,11 @@ static int mpsc_rx_intr(struct mpsc_port_info *pi)
 #endif
 #endif
 		/* Following use of tty struct directly is deprecated */
 		/* Following use of tty struct directly is deprecated */
 		if (tty_buffer_request_room(port, bytes_in) < bytes_in) {
 		if (tty_buffer_request_room(port, bytes_in) < bytes_in) {
-			if (port->low_latency)
+			if (port->low_latency) {
+				spin_unlock_irqrestore(&pi->port.lock, *flags);
 				tty_flip_buffer_push(port);
 				tty_flip_buffer_push(port);
+				spin_lock_irqsave(&pi->port.lock, *flags);
+			}
 			/*
 			/*
 			 * If this failed then we will throw away the bytes
 			 * If this failed then we will throw away the bytes
 			 * but must do so to clear interrupts.
 			 * but must do so to clear interrupts.
@@ -1080,7 +1083,9 @@ next_frame:
 	if ((readl(pi->sdma_base + SDMA_SDCM) & SDMA_SDCM_ERD) == 0)
 	if ((readl(pi->sdma_base + SDMA_SDCM) & SDMA_SDCM_ERD) == 0)
 		mpsc_start_rx(pi);
 		mpsc_start_rx(pi);
 
 
+	spin_unlock_irqrestore(&pi->port.lock, *flags);
 	tty_flip_buffer_push(port);
 	tty_flip_buffer_push(port);
+	spin_lock_irqsave(&pi->port.lock, *flags);
 	return rc;
 	return rc;
 }
 }
 
 
@@ -1222,7 +1227,7 @@ static irqreturn_t mpsc_sdma_intr(int irq, void *dev_id)
 
 
 	spin_lock_irqsave(&pi->port.lock, iflags);
 	spin_lock_irqsave(&pi->port.lock, iflags);
 	mpsc_sdma_intr_ack(pi);
 	mpsc_sdma_intr_ack(pi);
-	if (mpsc_rx_intr(pi))
+	if (mpsc_rx_intr(pi, &iflags))
 		rc = IRQ_HANDLED;
 		rc = IRQ_HANDLED;
 	if (mpsc_tx_intr(pi))
 	if (mpsc_tx_intr(pi))
 		rc = IRQ_HANDLED;
 		rc = IRQ_HANDLED;
@@ -1884,7 +1889,7 @@ static int mpsc_shared_drv_probe(struct platform_device *dev)
 	if (dev->id == 0) {
 	if (dev->id == 0) {
 		if (!(rc = mpsc_shared_map_regs(dev))) {
 		if (!(rc = mpsc_shared_map_regs(dev))) {
 			pdata = (struct mpsc_shared_pdata *)
 			pdata = (struct mpsc_shared_pdata *)
-				dev->dev.platform_data;
+				dev_get_platdata(&dev->dev);
 
 
 			mpsc_shared_regs.MPSC_MRR_m = pdata->mrr_val;
 			mpsc_shared_regs.MPSC_MRR_m = pdata->mrr_val;
 			mpsc_shared_regs.MPSC_RCRR_m= pdata->rcrr_val;
 			mpsc_shared_regs.MPSC_RCRR_m= pdata->rcrr_val;
@@ -2025,7 +2030,7 @@ static void mpsc_drv_get_platform_data(struct mpsc_port_info *pi,
 {
 {
 	struct mpsc_pdata	*pdata;
 	struct mpsc_pdata	*pdata;
 
 
-	pdata = (struct mpsc_pdata *)pd->dev.platform_data;
+	pdata = (struct mpsc_pdata *)dev_get_platdata(&pd->dev);
 
 
 	pi->port.uartclk = pdata->brg_clk_freq;
 	pi->port.uartclk = pdata->brg_clk_freq;
 	pi->port.iotype = UPIO_MEM;
 	pi->port.iotype = UPIO_MEM;

+ 2 - 2
drivers/tty/serial/mrst_max3110.c

@@ -713,7 +713,7 @@ static void serial_m3110_enable_ms(struct uart_port *port)
 {
 {
 }
 }
 
 
-struct uart_ops serial_m3110_ops = {
+static struct uart_ops serial_m3110_ops = {
 	.tx_empty	= serial_m3110_tx_empty,
 	.tx_empty	= serial_m3110_tx_empty,
 	.set_mctrl	= serial_m3110_set_mctrl,
 	.set_mctrl	= serial_m3110_set_mctrl,
 	.get_mctrl	= serial_m3110_get_mctrl,
 	.get_mctrl	= serial_m3110_get_mctrl,
@@ -844,7 +844,7 @@ static int serial_m3110_probe(struct spi_device *spi)
 	pmax = max;
 	pmax = max;
 
 
 	/* Give membase a psudo value to pass serial_core's check */
 	/* Give membase a psudo value to pass serial_core's check */
-	max->port.membase = (void *)0xff110000;
+	max->port.membase = (unsigned char __iomem *)0xff110000;
 	uart_add_one_port(&serial_m3110_reg, &max->port);
 	uart_add_one_port(&serial_m3110_reg, &max->port);
 
 
 	return 0;
 	return 0;

+ 158 - 118
drivers/tty/serial/msm_serial.c

@@ -45,16 +45,19 @@ struct msm_port {
 	struct clk		*clk;
 	struct clk		*clk;
 	struct clk		*pclk;
 	struct clk		*pclk;
 	unsigned int		imr;
 	unsigned int		imr;
-	unsigned int            *gsbi_base;
+	void __iomem		*gsbi_base;
 	int			is_uartdm;
 	int			is_uartdm;
 	unsigned int		old_snap_state;
 	unsigned int		old_snap_state;
 };
 };
 
 
-static inline void wait_for_xmitr(struct uart_port *port, int bits)
+static inline void wait_for_xmitr(struct uart_port *port)
 {
 {
-	if (!(msm_read(port, UART_SR) & UART_SR_TX_EMPTY))
-		while ((msm_read(port, UART_ISR) & bits) != bits)
-			cpu_relax();
+	while (!(msm_read(port, UART_SR) & UART_SR_TX_EMPTY)) {
+		if (msm_read(port, UART_ISR) & UART_ISR_TX_READY)
+			break;
+		udelay(1);
+	}
+	msm_write(port, UART_CR_CMD_RESET_TX_READY, UART_CR);
 }
 }
 
 
 static void msm_stop_tx(struct uart_port *port)
 static void msm_stop_tx(struct uart_port *port)
@@ -137,7 +140,10 @@ static void handle_rx_dm(struct uart_port *port, unsigned int misr)
 		count -= 4;
 		count -= 4;
 	}
 	}
 
 
+	spin_unlock(&port->lock);
 	tty_flip_buffer_push(tport);
 	tty_flip_buffer_push(tport);
+	spin_lock(&port->lock);
+
 	if (misr & (UART_IMR_RXSTALE))
 	if (misr & (UART_IMR_RXSTALE))
 		msm_write(port, UART_CR_CMD_RESET_STALE_INT, UART_CR);
 		msm_write(port, UART_CR_CMD_RESET_STALE_INT, UART_CR);
 	msm_write(port, 0xFFFFFF, UARTDM_DMRX);
 	msm_write(port, 0xFFFFFF, UARTDM_DMRX);
@@ -189,52 +195,69 @@ static void handle_rx(struct uart_port *port)
 			tty_insert_flip_char(tport, c, flag);
 			tty_insert_flip_char(tport, c, flag);
 	}
 	}
 
 
+	spin_unlock(&port->lock);
 	tty_flip_buffer_push(tport);
 	tty_flip_buffer_push(tport);
+	spin_lock(&port->lock);
 }
 }
 
 
-static void reset_dm_count(struct uart_port *port)
+static void reset_dm_count(struct uart_port *port, int count)
 {
 {
-	wait_for_xmitr(port, UART_ISR_TX_READY);
-	msm_write(port, 1, UARTDM_NCF_TX);
+	wait_for_xmitr(port);
+	msm_write(port, count, UARTDM_NCF_TX);
+	msm_read(port, UARTDM_NCF_TX);
 }
 }
 
 
 static void handle_tx(struct uart_port *port)
 static void handle_tx(struct uart_port *port)
 {
 {
 	struct circ_buf *xmit = &port->state->xmit;
 	struct circ_buf *xmit = &port->state->xmit;
 	struct msm_port *msm_port = UART_TO_MSM(port);
 	struct msm_port *msm_port = UART_TO_MSM(port);
-	int sent_tx;
+	unsigned int tx_count, num_chars;
+	unsigned int tf_pointer = 0;
+
+	tx_count = uart_circ_chars_pending(xmit);
+	tx_count = min3(tx_count, (unsigned int)UART_XMIT_SIZE - xmit->tail,
+			port->fifosize);
 
 
 	if (port->x_char) {
 	if (port->x_char) {
 		if (msm_port->is_uartdm)
 		if (msm_port->is_uartdm)
-			reset_dm_count(port);
+			reset_dm_count(port, tx_count + 1);
 
 
 		msm_write(port, port->x_char,
 		msm_write(port, port->x_char,
 			  msm_port->is_uartdm ? UARTDM_TF : UART_TF);
 			  msm_port->is_uartdm ? UARTDM_TF : UART_TF);
 		port->icount.tx++;
 		port->icount.tx++;
 		port->x_char = 0;
 		port->x_char = 0;
+	} else if (tx_count && msm_port->is_uartdm) {
+		reset_dm_count(port, tx_count);
 	}
 	}
 
 
-	if (msm_port->is_uartdm)
-		reset_dm_count(port);
+	while (tf_pointer < tx_count) {
+		int i;
+		char buf[4] = { 0 };
+		unsigned int *bf = (unsigned int *)&buf;
 
 
-	while (msm_read(port, UART_SR) & UART_SR_TX_READY) {
-		if (uart_circ_empty(xmit)) {
-			/* disable tx interrupts */
-			msm_port->imr &= ~UART_IMR_TXLEV;
-			msm_write(port, msm_port->imr, UART_IMR);
+		if (!(msm_read(port, UART_SR) & UART_SR_TX_READY))
 			break;
 			break;
-		}
-		msm_write(port, xmit->buf[xmit->tail],
-			  msm_port->is_uartdm ? UARTDM_TF : UART_TF);
 
 
 		if (msm_port->is_uartdm)
 		if (msm_port->is_uartdm)
-			reset_dm_count(port);
+			num_chars = min(tx_count - tf_pointer,
+					(unsigned int)sizeof(buf));
+		else
+			num_chars = 1;
 
 
-		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
-		port->icount.tx++;
-		sent_tx = 1;
+		for (i = 0; i < num_chars; i++) {
+			buf[i] = xmit->buf[xmit->tail + i];
+			port->icount.tx++;
+		}
+
+		msm_write(port, *bf, msm_port->is_uartdm ? UARTDM_TF : UART_TF);
+		xmit->tail = (xmit->tail + num_chars) & (UART_XMIT_SIZE - 1);
+		tf_pointer += num_chars;
 	}
 	}
 
 
+	/* disable tx interrupts if nothing more to send */
+	if (uart_circ_empty(xmit))
+		msm_stop_tx(port);
+
 	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
 	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
 		uart_write_wakeup(port);
 		uart_write_wakeup(port);
 }
 }
@@ -295,7 +318,7 @@ static void msm_reset(struct uart_port *port)
 	msm_write(port, UART_CR_CMD_SET_RFR, UART_CR);
 	msm_write(port, UART_CR_CMD_SET_RFR, UART_CR);
 }
 }
 
 
-void msm_set_mctrl(struct uart_port *port, unsigned int mctrl)
+static void msm_set_mctrl(struct uart_port *port, unsigned int mctrl)
 {
 {
 	unsigned int mr;
 	unsigned int mr;
 	mr = msm_read(port, UART_MR1);
 	mr = msm_read(port, UART_MR1);
@@ -318,70 +341,60 @@ static void msm_break_ctl(struct uart_port *port, int break_ctl)
 		msm_write(port, UART_CR_CMD_STOP_BREAK, UART_CR);
 		msm_write(port, UART_CR_CMD_STOP_BREAK, UART_CR);
 }
 }
 
 
+struct msm_baud_map {
+	u16	divisor;
+	u8	code;
+	u8	rxstale;
+};
+
+static const struct msm_baud_map *
+msm_find_best_baud(struct uart_port *port, unsigned int baud)
+{
+	unsigned int i, divisor;
+	const struct msm_baud_map *entry;
+	static const struct msm_baud_map table[] = {
+		{ 1536, 0x00,  1 },
+		{  768, 0x11,  1 },
+		{  384, 0x22,  1 },
+		{  192, 0x33,  1 },
+		{   96, 0x44,  1 },
+		{   48, 0x55,  1 },
+		{   32, 0x66,  1 },
+		{   24, 0x77,  1 },
+		{   16, 0x88,  1 },
+		{   12, 0x99,  6 },
+		{    8, 0xaa,  6 },
+		{    6, 0xbb,  6 },
+		{    4, 0xcc,  6 },
+		{    3, 0xdd,  8 },
+		{    2, 0xee, 16 },
+		{    1, 0xff, 31 },
+	};
+
+	divisor = uart_get_divisor(port, baud);
+
+	for (i = 0, entry = table; i < ARRAY_SIZE(table); i++, entry++)
+		if (entry->divisor <= divisor)
+			break;
+
+	return entry; /* Default to smallest divider */
+}
+
 static int msm_set_baud_rate(struct uart_port *port, unsigned int baud)
 static int msm_set_baud_rate(struct uart_port *port, unsigned int baud)
 {
 {
-	unsigned int baud_code, rxstale, watermark;
+	unsigned int rxstale, watermark;
 	struct msm_port *msm_port = UART_TO_MSM(port);
 	struct msm_port *msm_port = UART_TO_MSM(port);
+	const struct msm_baud_map *entry;
 
 
-	switch (baud) {
-	case 300:
-		baud_code = UART_CSR_300;
-		rxstale = 1;
-		break;
-	case 600:
-		baud_code = UART_CSR_600;
-		rxstale = 1;
-		break;
-	case 1200:
-		baud_code = UART_CSR_1200;
-		rxstale = 1;
-		break;
-	case 2400:
-		baud_code = UART_CSR_2400;
-		rxstale = 1;
-		break;
-	case 4800:
-		baud_code = UART_CSR_4800;
-		rxstale = 1;
-		break;
-	case 9600:
-		baud_code = UART_CSR_9600;
-		rxstale = 2;
-		break;
-	case 14400:
-		baud_code = UART_CSR_14400;
-		rxstale = 3;
-		break;
-	case 19200:
-		baud_code = UART_CSR_19200;
-		rxstale = 4;
-		break;
-	case 28800:
-		baud_code = UART_CSR_28800;
-		rxstale = 6;
-		break;
-	case 38400:
-		baud_code = UART_CSR_38400;
-		rxstale = 8;
-		break;
-	case 57600:
-		baud_code = UART_CSR_57600;
-		rxstale = 16;
-		break;
-	case 115200:
-	default:
-		baud_code = UART_CSR_115200;
-		baud = 115200;
-		rxstale = 31;
-		break;
-	}
+	entry = msm_find_best_baud(port, baud);
 
 
 	if (msm_port->is_uartdm)
 	if (msm_port->is_uartdm)
 		msm_write(port, UART_CR_CMD_RESET_RX, UART_CR);
 		msm_write(port, UART_CR_CMD_RESET_RX, UART_CR);
 
 
-	msm_write(port, baud_code, UART_CSR);
+	msm_write(port, entry->code, UART_CSR);
 
 
 	/* RX stale watermark */
 	/* RX stale watermark */
+	rxstale = entry->rxstale;
 	watermark = UART_IPR_STALE_LSB & rxstale;
 	watermark = UART_IPR_STALE_LSB & rxstale;
 	watermark |= UART_IPR_RXSTALE_LAST;
 	watermark |= UART_IPR_RXSTALE_LAST;
 	watermark |= UART_IPR_STALE_TIMEOUT_MSB & (rxstale << 2);
 	watermark |= UART_IPR_STALE_TIMEOUT_MSB & (rxstale << 2);
@@ -409,8 +422,7 @@ static void msm_init_clock(struct uart_port *port)
 	struct msm_port *msm_port = UART_TO_MSM(port);
 	struct msm_port *msm_port = UART_TO_MSM(port);
 
 
 	clk_prepare_enable(msm_port->clk);
 	clk_prepare_enable(msm_port->clk);
-	if (!IS_ERR(msm_port->pclk))
-		clk_prepare_enable(msm_port->pclk);
+	clk_prepare_enable(msm_port->pclk);
 	msm_serial_set_mnd_regs(port);
 	msm_serial_set_mnd_regs(port);
 }
 }
 
 
@@ -589,12 +601,10 @@ static void msm_release_port(struct uart_port *port)
 	port->membase = NULL;
 	port->membase = NULL;
 
 
 	if (msm_port->gsbi_base) {
 	if (msm_port->gsbi_base) {
-		iowrite32(GSBI_PROTOCOL_IDLE, msm_port->gsbi_base +
-			  GSBI_CONTROL);
-
-		gsbi_resource = platform_get_resource(pdev,
-							IORESOURCE_MEM, 1);
+		writel_relaxed(GSBI_PROTOCOL_IDLE,
+				msm_port->gsbi_base + GSBI_CONTROL);
 
 
+		gsbi_resource = platform_get_resource(pdev, IORESOURCE_MEM, 1);
 		if (unlikely(!gsbi_resource))
 		if (unlikely(!gsbi_resource))
 			return;
 			return;
 
 
@@ -637,7 +647,7 @@ static int msm_request_port(struct uart_port *port)
 		if (!request_mem_region(gsbi_resource->start, size,
 		if (!request_mem_region(gsbi_resource->start, size,
 						 "msm_serial")) {
 						 "msm_serial")) {
 			ret = -EBUSY;
 			ret = -EBUSY;
-			goto fail_release_port;
+			goto fail_release_port_membase;
 		}
 		}
 
 
 		msm_port->gsbi_base = ioremap(gsbi_resource->start, size);
 		msm_port->gsbi_base = ioremap(gsbi_resource->start, size);
@@ -651,6 +661,8 @@ static int msm_request_port(struct uart_port *port)
 
 
 fail_release_gsbi:
 fail_release_gsbi:
 	release_mem_region(gsbi_resource->start, size);
 	release_mem_region(gsbi_resource->start, size);
+fail_release_port_membase:
+	iounmap(port->membase);
 fail_release_port:
 fail_release_port:
 	release_mem_region(port->mapbase, size);
 	release_mem_region(port->mapbase, size);
 	return ret;
 	return ret;
@@ -666,10 +678,9 @@ static void msm_config_port(struct uart_port *port, int flags)
 		if (ret)
 		if (ret)
 			return;
 			return;
 	}
 	}
-
-	if (msm_port->is_uartdm)
-		iowrite32(GSBI_PROTOCOL_UART, msm_port->gsbi_base +
-			  GSBI_CONTROL);
+	if (msm_port->gsbi_base)
+		writel_relaxed(GSBI_PROTOCOL_UART,
+				msm_port->gsbi_base + GSBI_CONTROL);
 }
 }
 
 
 static int msm_verify_port(struct uart_port *port, struct serial_struct *ser)
 static int msm_verify_port(struct uart_port *port, struct serial_struct *ser)
@@ -689,13 +700,11 @@ static void msm_power(struct uart_port *port, unsigned int state,
 	switch (state) {
 	switch (state) {
 	case 0:
 	case 0:
 		clk_prepare_enable(msm_port->clk);
 		clk_prepare_enable(msm_port->clk);
-		if (!IS_ERR(msm_port->pclk))
-			clk_prepare_enable(msm_port->pclk);
+		clk_prepare_enable(msm_port->pclk);
 		break;
 		break;
 	case 3:
 	case 3:
 		clk_disable_unprepare(msm_port->clk);
 		clk_disable_unprepare(msm_port->clk);
-		if (!IS_ERR(msm_port->pclk))
-			clk_disable_unprepare(msm_port->pclk);
+		clk_disable_unprepare(msm_port->pclk);
 		break;
 		break;
 	default:
 	default:
 		printk(KERN_ERR "msm_serial: Unknown PM state %d\n", state);
 		printk(KERN_ERR "msm_serial: Unknown PM state %d\n", state);
@@ -760,32 +769,63 @@ static inline struct uart_port *get_port_from_line(unsigned int line)
 }
 }
 
 
 #ifdef CONFIG_SERIAL_MSM_CONSOLE
 #ifdef CONFIG_SERIAL_MSM_CONSOLE
-
-static void msm_console_putchar(struct uart_port *port, int c)
-{
-	struct msm_port *msm_port = UART_TO_MSM(port);
-
-	if (msm_port->is_uartdm)
-		reset_dm_count(port);
-
-	while (!(msm_read(port, UART_SR) & UART_SR_TX_READY))
-		;
-	msm_write(port, c, msm_port->is_uartdm ? UARTDM_TF : UART_TF);
-}
-
 static void msm_console_write(struct console *co, const char *s,
 static void msm_console_write(struct console *co, const char *s,
 			      unsigned int count)
 			      unsigned int count)
 {
 {
+	int i;
 	struct uart_port *port;
 	struct uart_port *port;
 	struct msm_port *msm_port;
 	struct msm_port *msm_port;
+	int num_newlines = 0;
+	bool replaced = false;
 
 
 	BUG_ON(co->index < 0 || co->index >= UART_NR);
 	BUG_ON(co->index < 0 || co->index >= UART_NR);
 
 
 	port = get_port_from_line(co->index);
 	port = get_port_from_line(co->index);
 	msm_port = UART_TO_MSM(port);
 	msm_port = UART_TO_MSM(port);
 
 
+	/* Account for newlines that will get a carriage return added */
+	for (i = 0; i < count; i++)
+		if (s[i] == '\n')
+			num_newlines++;
+	count += num_newlines;
+
 	spin_lock(&port->lock);
 	spin_lock(&port->lock);
-	uart_console_write(port, s, count, msm_console_putchar);
+	if (msm_port->is_uartdm)
+		reset_dm_count(port, count);
+
+	i = 0;
+	while (i < count) {
+		int j;
+		unsigned int num_chars;
+		char buf[4] = { 0 };
+		unsigned int *bf = (unsigned int *)&buf;
+
+		if (msm_port->is_uartdm)
+			num_chars = min(count - i, (unsigned int)sizeof(buf));
+		else
+			num_chars = 1;
+
+		for (j = 0; j < num_chars; j++) {
+			char c = *s;
+
+			if (c == '\n' && !replaced) {
+				buf[j] = '\r';
+				j++;
+				replaced = true;
+			}
+			if (j < num_chars) {
+				buf[j] = c;
+				s++;
+				replaced = false;
+			}
+		}
+
+		while (!(msm_read(port, UART_SR) & UART_SR_TX_READY))
+			cpu_relax();
+
+		msm_write(port, *bf, msm_port->is_uartdm ? UARTDM_TF : UART_TF);
+		i += num_chars;
+	}
 	spin_unlock(&port->lock);
 	spin_unlock(&port->lock);
 }
 }
 
 
@@ -859,6 +899,11 @@ static struct uart_driver msm_uart_driver = {
 
 
 static atomic_t msm_uart_next_id = ATOMIC_INIT(0);
 static atomic_t msm_uart_next_id = ATOMIC_INIT(0);
 
 
+static const struct of_device_id msm_uartdm_table[] = {
+	{ .compatible = "qcom,msm-uartdm" },
+	{ }
+};
+
 static int __init msm_serial_probe(struct platform_device *pdev)
 static int __init msm_serial_probe(struct platform_device *pdev)
 {
 {
 	struct msm_port *msm_port;
 	struct msm_port *msm_port;
@@ -878,23 +923,17 @@ static int __init msm_serial_probe(struct platform_device *pdev)
 	port->dev = &pdev->dev;
 	port->dev = &pdev->dev;
 	msm_port = UART_TO_MSM(port);
 	msm_port = UART_TO_MSM(port);
 
 
-	if (platform_get_resource(pdev, IORESOURCE_MEM, 1))
+	if (of_match_device(msm_uartdm_table, &pdev->dev))
 		msm_port->is_uartdm = 1;
 		msm_port->is_uartdm = 1;
 	else
 	else
 		msm_port->is_uartdm = 0;
 		msm_port->is_uartdm = 0;
 
 
-	if (msm_port->is_uartdm) {
-		msm_port->clk = devm_clk_get(&pdev->dev, "gsbi_uart_clk");
-		msm_port->pclk = devm_clk_get(&pdev->dev, "gsbi_pclk");
-	} else {
-		msm_port->clk = devm_clk_get(&pdev->dev, "uart_clk");
-		msm_port->pclk = ERR_PTR(-ENOENT);
-	}
-
+	msm_port->clk = devm_clk_get(&pdev->dev, "core");
 	if (IS_ERR(msm_port->clk))
 	if (IS_ERR(msm_port->clk))
 		return PTR_ERR(msm_port->clk);
 		return PTR_ERR(msm_port->clk);
 
 
 	if (msm_port->is_uartdm) {
 	if (msm_port->is_uartdm) {
+		msm_port->pclk = devm_clk_get(&pdev->dev, "iface");
 		if (IS_ERR(msm_port->pclk))
 		if (IS_ERR(msm_port->pclk))
 			return PTR_ERR(msm_port->pclk);
 			return PTR_ERR(msm_port->pclk);
 
 
@@ -931,6 +970,7 @@ static int msm_serial_remove(struct platform_device *pdev)
 
 
 static struct of_device_id msm_match_table[] = {
 static struct of_device_id msm_match_table[] = {
 	{ .compatible = "qcom,msm-uart" },
 	{ .compatible = "qcom,msm-uart" },
+	{ .compatible = "qcom,msm-uartdm" },
 	{}
 	{}
 };
 };
 
 

+ 5 - 14
drivers/tty/serial/msm_serial.h

@@ -38,19 +38,7 @@
 #define UART_MR2_PARITY_MODE_SPACE	0x3
 #define UART_MR2_PARITY_MODE_SPACE	0x3
 #define UART_MR2_PARITY_MODE		0x3
 #define UART_MR2_PARITY_MODE		0x3
 
 
-#define UART_CSR	0x0008
-#define UART_CSR_115200	0xFF
-#define UART_CSR_57600	0xEE
-#define UART_CSR_38400	0xDD
-#define UART_CSR_28800	0xCC
-#define UART_CSR_19200	0xBB
-#define UART_CSR_14400	0xAA
-#define UART_CSR_9600	0x99
-#define UART_CSR_4800	0x77
-#define UART_CSR_2400	0x55
-#define UART_CSR_1200	0x44
-#define UART_CSR_600	0x33
-#define UART_CSR_300	0x22
+#define UART_CSR			0x0008
 
 
 #define UART_TF		0x000C
 #define UART_TF		0x000C
 #define UARTDM_TF	0x0070
 #define UARTDM_TF	0x0070
@@ -71,6 +59,7 @@
 #define UART_CR_CMD_RESET_RFR		(14 << 4)
 #define UART_CR_CMD_RESET_RFR		(14 << 4)
 #define UART_CR_CMD_PROTECTION_EN	(16 << 4)
 #define UART_CR_CMD_PROTECTION_EN	(16 << 4)
 #define UART_CR_CMD_STALE_EVENT_ENABLE	(80 << 4)
 #define UART_CR_CMD_STALE_EVENT_ENABLE	(80 << 4)
+#define UART_CR_CMD_RESET_TX_READY	(3 << 8)
 #define UART_CR_TX_DISABLE		(1 << 3)
 #define UART_CR_TX_DISABLE		(1 << 3)
 #define UART_CR_TX_ENABLE		(1 << 2)
 #define UART_CR_TX_ENABLE		(1 << 2)
 #define UART_CR_RX_DISABLE		(1 << 1)
 #define UART_CR_RX_DISABLE		(1 << 1)
@@ -151,6 +140,7 @@ static inline void msm_serial_set_mnd_regs_tcxo(struct uart_port *port)
 	msm_write(port, 0xF1, UART_NREG);
 	msm_write(port, 0xF1, UART_NREG);
 	msm_write(port, 0x0F, UART_DREG);
 	msm_write(port, 0x0F, UART_DREG);
 	msm_write(port, 0x1A, UART_MNDREG);
 	msm_write(port, 0x1A, UART_MNDREG);
+	port->uartclk = 1843200;
 }
 }
 
 
 /*
 /*
@@ -162,6 +152,7 @@ static inline void msm_serial_set_mnd_regs_tcxoby4(struct uart_port *port)
 	msm_write(port, 0xF6, UART_NREG);
 	msm_write(port, 0xF6, UART_NREG);
 	msm_write(port, 0x0F, UART_DREG);
 	msm_write(port, 0x0F, UART_DREG);
 	msm_write(port, 0x0A, UART_MNDREG);
 	msm_write(port, 0x0A, UART_MNDREG);
+	port->uartclk = 1843200;
 }
 }
 
 
 static inline
 static inline
@@ -169,7 +160,7 @@ void msm_serial_set_mnd_regs_from_uartclk(struct uart_port *port)
 {
 {
 	if (port->uartclk == 19200000)
 	if (port->uartclk == 19200000)
 		msm_serial_set_mnd_regs_tcxo(port);
 		msm_serial_set_mnd_regs_tcxo(port);
-	else
+	else if (port->uartclk == 4800000)
 		msm_serial_set_mnd_regs_tcxoby4(port);
 		msm_serial_set_mnd_regs_tcxoby4(port);
 }
 }
 
 

+ 1 - 1
drivers/tty/serial/msm_serial_hs.c

@@ -1618,7 +1618,7 @@ static int msm_hs_probe(struct platform_device *pdev)
 	struct msm_hs_port *msm_uport;
 	struct msm_hs_port *msm_uport;
 	struct resource *resource;
 	struct resource *resource;
 	const struct msm_serial_hs_platform_data *pdata =
 	const struct msm_serial_hs_platform_data *pdata =
-						pdev->dev.platform_data;
+						dev_get_platdata(&pdev->dev);
 
 
 	if (pdev->id < 0 || pdev->id >= UARTDM_NR) {
 	if (pdev->id < 0 || pdev->id >= UARTDM_NR) {
 		printk(KERN_ERR "Invalid plaform device ID = %d\n", pdev->id);
 		printk(KERN_ERR "Invalid plaform device ID = %d\n", pdev->id);

+ 5 - 11
drivers/tty/serial/mxs-auart.c

@@ -32,7 +32,6 @@
 #include <linux/clk.h>
 #include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/delay.h>
 #include <linux/io.h>
 #include <linux/io.h>
-#include <linux/pinctrl/consumer.h>
 #include <linux/of_device.h>
 #include <linux/of_device.h>
 #include <linux/dma-mapping.h>
 #include <linux/dma-mapping.h>
 #include <linux/dmaengine.h>
 #include <linux/dmaengine.h>
@@ -134,10 +133,10 @@ enum mxs_auart_type {
 struct mxs_auart_port {
 struct mxs_auart_port {
 	struct uart_port port;
 	struct uart_port port;
 
 
-#define MXS_AUART_DMA_CONFIG	0x1
 #define MXS_AUART_DMA_ENABLED	0x2
 #define MXS_AUART_DMA_ENABLED	0x2
 #define MXS_AUART_DMA_TX_SYNC	2  /* bit 2 */
 #define MXS_AUART_DMA_TX_SYNC	2  /* bit 2 */
 #define MXS_AUART_DMA_RX_READY	3  /* bit 3 */
 #define MXS_AUART_DMA_RX_READY	3  /* bit 3 */
+#define MXS_AUART_RTSCTS	4  /* bit 4 */
 	unsigned long flags;
 	unsigned long flags;
 	unsigned int ctrl;
 	unsigned int ctrl;
 	enum mxs_auart_type devtype;
 	enum mxs_auart_type devtype;
@@ -640,7 +639,8 @@ static void mxs_auart_settermios(struct uart_port *u,
 		 * we can only implement the DMA support for auart
 		 * we can only implement the DMA support for auart
 		 * in mx28.
 		 * in mx28.
 		 */
 		 */
-		if (is_imx28_auart(s) && (s->flags & MXS_AUART_DMA_CONFIG)) {
+		if (is_imx28_auart(s)
+				&& test_bit(MXS_AUART_RTSCTS, &s->flags)) {
 			if (!mxs_auart_dma_init(s))
 			if (!mxs_auart_dma_init(s))
 				/* enable DMA tranfer */
 				/* enable DMA tranfer */
 				ctrl2 |= AUART_CTRL2_TXDMAE | AUART_CTRL2_RXDMAE
 				ctrl2 |= AUART_CTRL2_TXDMAE | AUART_CTRL2_RXDMAE
@@ -1008,7 +1008,8 @@ static int serial_mxs_probe_dt(struct mxs_auart_port *s,
 	}
 	}
 	s->port.line = ret;
 	s->port.line = ret;
 
 
-	s->flags |= MXS_AUART_DMA_CONFIG;
+	if (of_get_property(np, "fsl,uart-has-rtscts", NULL))
+		set_bit(MXS_AUART_RTSCTS, &s->flags);
 
 
 	return 0;
 	return 0;
 }
 }
@@ -1021,7 +1022,6 @@ static int mxs_auart_probe(struct platform_device *pdev)
 	u32 version;
 	u32 version;
 	int ret = 0;
 	int ret = 0;
 	struct resource *r;
 	struct resource *r;
-	struct pinctrl *pinctrl;
 
 
 	s = kzalloc(sizeof(struct mxs_auart_port), GFP_KERNEL);
 	s = kzalloc(sizeof(struct mxs_auart_port), GFP_KERNEL);
 	if (!s) {
 	if (!s) {
@@ -1035,12 +1035,6 @@ static int mxs_auart_probe(struct platform_device *pdev)
 	else if (ret < 0)
 	else if (ret < 0)
 		goto out_free;
 		goto out_free;
 
 
-	pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
-	if (IS_ERR(pinctrl)) {
-		ret = PTR_ERR(pinctrl);
-		goto out_free;
-	}
-
 	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;

+ 4 - 4
drivers/tty/serial/netx-serial.c

@@ -196,7 +196,7 @@ static void netx_txint(struct uart_port *port)
 		uart_write_wakeup(port);
 		uart_write_wakeup(port);
 }
 }
 
 
-static void netx_rxint(struct uart_port *port)
+static void netx_rxint(struct uart_port *port, unsigned long *flags)
 {
 {
 	unsigned char rx, flg, status;
 	unsigned char rx, flg, status;
 
 
@@ -236,7 +236,9 @@ static void netx_rxint(struct uart_port *port)
 		uart_insert_char(port, status, SR_OE, rx, flg);
 		uart_insert_char(port, status, SR_OE, rx, flg);
 	}
 	}
 
 
+	spin_unlock_irqrestore(&port->lock, *flags);
 	tty_flip_buffer_push(&port->state->port);
 	tty_flip_buffer_push(&port->state->port);
+	spin_lock_irqsave(&port->lock, *flags);
 }
 }
 
 
 static irqreturn_t netx_int(int irq, void *dev_id)
 static irqreturn_t netx_int(int irq, void *dev_id)
@@ -250,7 +252,7 @@ static irqreturn_t netx_int(int irq, void *dev_id)
 	status = readl(port->membase + UART_IIR) & IIR_MASK;
 	status = readl(port->membase + UART_IIR) & IIR_MASK;
 	while (status) {
 	while (status) {
 		if (status & IIR_RIS)
 		if (status & IIR_RIS)
-			netx_rxint(port);
+			netx_rxint(port, &flags);
 		if (status & IIR_TIS)
 		if (status & IIR_TIS)
 			netx_txint(port);
 			netx_txint(port);
 		if (status & IIR_MIS) {
 		if (status & IIR_MIS) {
@@ -693,8 +695,6 @@ static int serial_netx_remove(struct platform_device *pdev)
 {
 {
 	struct netx_port *sport = platform_get_drvdata(pdev);
 	struct netx_port *sport = platform_get_drvdata(pdev);
 
 
-	platform_set_drvdata(pdev, NULL);
-
 	if (sport)
 	if (sport)
 		uart_remove_one_port(&netx_reg, &sport->port);
 		uart_remove_one_port(&netx_reg, &sport->port);
 
 

+ 3 - 0
drivers/tty/serial/nwpserial.c

@@ -149,7 +149,10 @@ static irqreturn_t nwpserial_interrupt(int irq, void *dev_id)
 			tty_insert_flip_char(port, ch, TTY_NORMAL);
 			tty_insert_flip_char(port, ch, TTY_NORMAL);
 	} while (dcr_read(up->dcr_host, UART_LSR) & UART_LSR_DR);
 	} while (dcr_read(up->dcr_host, UART_LSR) & UART_LSR_DR);
 
 
+	spin_unlock(&up->port.lock);
 	tty_flip_buffer_push(port);
 	tty_flip_buffer_push(port);
+	spin_lock(&up->port.lock);
+
 	ret = IRQ_HANDLED;
 	ret = IRQ_HANDLED;
 
 
 	/* clear interrupt */
 	/* clear interrupt */

+ 199 - 15
drivers/tty/serial/omap-serial.c

@@ -40,9 +40,11 @@
 #include <linux/pm_runtime.h>
 #include <linux/pm_runtime.h>
 #include <linux/of.h>
 #include <linux/of.h>
 #include <linux/gpio.h>
 #include <linux/gpio.h>
-#include <linux/pinctrl/consumer.h>
+#include <linux/of_gpio.h>
 #include <linux/platform_data/serial-omap.h>
 #include <linux/platform_data/serial-omap.h>
 
 
+#include <dt-bindings/gpio/gpio.h>
+
 #define OMAP_MAX_HSUART_PORTS	6
 #define OMAP_MAX_HSUART_PORTS	6
 
 
 #define UART_BUILD_REVISION(x, y)	(((x) << 8) | (y))
 #define UART_BUILD_REVISION(x, y)	(((x) << 8) | (y))
@@ -52,6 +54,11 @@
 #define OMAP_UART_REV_52 0x0502
 #define OMAP_UART_REV_52 0x0502
 #define OMAP_UART_REV_63 0x0603
 #define OMAP_UART_REV_63 0x0603
 
 
+#define OMAP_UART_TX_WAKEUP_EN		BIT(7)
+
+/* Feature flags */
+#define OMAP_UART_WER_HAS_TX_WAKEUP	BIT(0)
+
 #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)
 
 
@@ -137,6 +144,7 @@ struct uart_omap_port {
 	unsigned char		dlh;
 	unsigned char		dlh;
 	unsigned char		mdr1;
 	unsigned char		mdr1;
 	unsigned char		scr;
 	unsigned char		scr;
+	unsigned char		wer;
 
 
 	int			use_dma;
 	int			use_dma;
 	/*
 	/*
@@ -151,16 +159,19 @@ struct uart_omap_port {
 	int			context_loss_cnt;
 	int			context_loss_cnt;
 	u32			errata;
 	u32			errata;
 	u8			wakeups_enabled;
 	u8			wakeups_enabled;
+	u32			features;
 
 
 	int			DTR_gpio;
 	int			DTR_gpio;
 	int			DTR_inverted;
 	int			DTR_inverted;
 	int			DTR_active;
 	int			DTR_active;
 
 
+	struct serial_rs485	rs485;
+	int			rts_gpio;
+
 	struct pm_qos_request	pm_qos_request;
 	struct pm_qos_request	pm_qos_request;
 	u32			latency;
 	u32			latency;
 	u32			calc_latency;
 	u32			calc_latency;
 	struct work_struct	qos_work;
 	struct work_struct	qos_work;
-	struct pinctrl		*pins;
 	bool			is_suspending;
 	bool			is_suspending;
 };
 };
 
 
@@ -195,7 +206,7 @@ static inline void serial_omap_clear_fifos(struct uart_omap_port *up)
 
 
 static int serial_omap_get_context_loss_count(struct uart_omap_port *up)
 static int serial_omap_get_context_loss_count(struct uart_omap_port *up)
 {
 {
-	struct omap_uart_port_info *pdata = up->dev->platform_data;
+	struct omap_uart_port_info *pdata = dev_get_platdata(up->dev);
 
 
 	if (!pdata || !pdata->get_context_loss_count)
 	if (!pdata || !pdata->get_context_loss_count)
 		return -EINVAL;
 		return -EINVAL;
@@ -205,7 +216,7 @@ static int serial_omap_get_context_loss_count(struct uart_omap_port *up)
 
 
 static void serial_omap_enable_wakeup(struct uart_omap_port *up, bool enable)
 static void serial_omap_enable_wakeup(struct uart_omap_port *up, bool enable)
 {
 {
-	struct omap_uart_port_info *pdata = up->dev->platform_data;
+	struct omap_uart_port_info *pdata = dev_get_platdata(up->dev);
 
 
 	if (!pdata || !pdata->enable_wakeup)
 	if (!pdata || !pdata->enable_wakeup)
 		return;
 		return;
@@ -272,13 +283,42 @@ static void serial_omap_enable_ms(struct uart_port *port)
 static void serial_omap_stop_tx(struct uart_port *port)
 static void serial_omap_stop_tx(struct uart_port *port)
 {
 {
 	struct uart_omap_port *up = to_uart_omap_port(port);
 	struct uart_omap_port *up = to_uart_omap_port(port);
+	struct circ_buf *xmit = &up->port.state->xmit;
+	int res;
 
 
 	pm_runtime_get_sync(up->dev);
 	pm_runtime_get_sync(up->dev);
+
+	/* handle rs485 */
+	if (up->rs485.flags & SER_RS485_ENABLED) {
+		/* do nothing if current tx not yet completed */
+		res = serial_in(up, UART_LSR) & UART_LSR_TEMT;
+		if (!res)
+			return;
+
+		/* if there's no more data to send, turn off rts */
+		if (uart_circ_empty(xmit)) {
+			/* if rts not already disabled */
+			res = (up->rs485.flags & SER_RS485_RTS_AFTER_SEND) ? 1 : 0;
+			if (gpio_get_value(up->rts_gpio) != res) {
+				if (up->rs485.delay_rts_after_send > 0) {
+					mdelay(up->rs485.delay_rts_after_send);
+				}
+				gpio_set_value(up->rts_gpio, res);
+			}
+		}
+	}
+
 	if (up->ier & UART_IER_THRI) {
 	if (up->ier & UART_IER_THRI) {
 		up->ier &= ~UART_IER_THRI;
 		up->ier &= ~UART_IER_THRI;
 		serial_out(up, UART_IER, up->ier);
 		serial_out(up, UART_IER, up->ier);
 	}
 	}
 
 
+	if ((up->rs485.flags & SER_RS485_ENABLED) &&
+	    !(up->rs485.flags & SER_RS485_RX_DURING_TX)) {
+		up->ier = UART_IER_RLSI | UART_IER_RDI;
+		serial_out(up, UART_IER, up->ier);
+	}
+
 	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);
 }
 }
@@ -340,8 +380,26 @@ static inline void serial_omap_enable_ier_thri(struct uart_omap_port *up)
 static void serial_omap_start_tx(struct uart_port *port)
 static void serial_omap_start_tx(struct uart_port *port)
 {
 {
 	struct uart_omap_port *up = to_uart_omap_port(port);
 	struct uart_omap_port *up = to_uart_omap_port(port);
+	int res;
 
 
 	pm_runtime_get_sync(up->dev);
 	pm_runtime_get_sync(up->dev);
+
+	/* handle rs485 */
+	if (up->rs485.flags & SER_RS485_ENABLED) {
+		/* if rts not already enabled */
+		res = (up->rs485.flags & SER_RS485_RTS_ON_SEND) ? 1 : 0;
+		if (gpio_get_value(up->rts_gpio) != res) {
+			gpio_set_value(up->rts_gpio, res);
+			if (up->rs485.delay_rts_before_send > 0) {
+				mdelay(up->rs485.delay_rts_before_send);
+			}
+		}
+	}
+
+	if ((up->rs485.flags & SER_RS485_ENABLED) &&
+	    !(up->rs485.flags & SER_RS485_RX_DURING_TX))
+		serial_omap_stop_rx(port);
+
 	serial_omap_enable_ier_thri(up);
 	serial_omap_enable_ier_thri(up);
 	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);
@@ -683,7 +741,11 @@ static int serial_omap_startup(struct uart_port *port)
 	serial_out(up, UART_IER, up->ier);
 	serial_out(up, UART_IER, up->ier);
 
 
 	/* Enable module level wake up */
 	/* Enable module level wake up */
-	serial_out(up, UART_OMAP_WER, OMAP_UART_WER_MOD_WKUP);
+	up->wer = OMAP_UART_WER_MOD_WKUP;
+	if (up->features & OMAP_UART_WER_HAS_TX_WAKEUP)
+		up->wer |= OMAP_UART_TX_WAKEUP_EN;
+
+	serial_out(up, UART_OMAP_WER, up->wer);
 
 
 	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);
@@ -1254,6 +1316,76 @@ static inline void serial_omap_add_console_port(struct uart_omap_port *up)
 
 
 #endif
 #endif
 
 
+/* Enable or disable the rs485 support */
+static void
+serial_omap_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf)
+{
+	struct uart_omap_port *up = to_uart_omap_port(port);
+	unsigned long flags;
+	unsigned int mode;
+	int val;
+
+	pm_runtime_get_sync(up->dev);
+	spin_lock_irqsave(&up->port.lock, flags);
+
+	/* Disable interrupts from this port */
+	mode = up->ier;
+	up->ier = 0;
+	serial_out(up, UART_IER, 0);
+
+	/* store new config */
+	up->rs485 = *rs485conf;
+
+	/*
+	 * Just as a precaution, only allow rs485
+	 * to be enabled if the gpio pin is valid
+	 */
+	if (gpio_is_valid(up->rts_gpio)) {
+		/* enable / disable rts */
+		val = (up->rs485.flags & SER_RS485_ENABLED) ?
+			SER_RS485_RTS_AFTER_SEND : SER_RS485_RTS_ON_SEND;
+		val = (up->rs485.flags & val) ? 1 : 0;
+		gpio_set_value(up->rts_gpio, val);
+	} else
+		up->rs485.flags &= ~SER_RS485_ENABLED;
+
+	/* Enable interrupts */
+	up->ier = mode;
+	serial_out(up, UART_IER, up->ier);
+
+	spin_unlock_irqrestore(&up->port.lock, flags);
+	pm_runtime_mark_last_busy(up->dev);
+	pm_runtime_put_autosuspend(up->dev);
+}
+
+static int
+serial_omap_ioctl(struct uart_port *port, unsigned int cmd, unsigned long arg)
+{
+	struct serial_rs485 rs485conf;
+
+	switch (cmd) {
+	case TIOCSRS485:
+		if (copy_from_user(&rs485conf, (struct serial_rs485 *) arg,
+					sizeof(rs485conf)))
+			return -EFAULT;
+
+		serial_omap_config_rs485(port, &rs485conf);
+		break;
+
+	case TIOCGRS485:
+		if (copy_to_user((struct serial_rs485 *) arg,
+					&(to_uart_omap_port(port)->rs485),
+					sizeof(rs485conf)))
+			return -EFAULT;
+		break;
+
+	default:
+		return -ENOIOCTLCMD;
+	}
+	return 0;
+}
+
+
 static struct uart_ops serial_omap_pops = {
 static struct uart_ops serial_omap_pops = {
 	.tx_empty	= serial_omap_tx_empty,
 	.tx_empty	= serial_omap_tx_empty,
 	.set_mctrl	= serial_omap_set_mctrl,
 	.set_mctrl	= serial_omap_set_mctrl,
@@ -1275,6 +1407,7 @@ static struct uart_ops serial_omap_pops = {
 	.request_port	= serial_omap_request_port,
 	.request_port	= serial_omap_request_port,
 	.config_port	= serial_omap_config_port,
 	.config_port	= serial_omap_config_port,
 	.verify_port	= serial_omap_verify_port,
 	.verify_port	= serial_omap_verify_port,
+	.ioctl		= serial_omap_ioctl,
 #ifdef CONFIG_CONSOLE_POLL
 #ifdef CONFIG_CONSOLE_POLL
 	.poll_put_char  = serial_omap_poll_put_char,
 	.poll_put_char  = serial_omap_poll_put_char,
 	.poll_get_char  = serial_omap_poll_get_char,
 	.poll_get_char  = serial_omap_poll_get_char,
@@ -1334,7 +1467,7 @@ static void omap_serial_fill_features_erratas(struct uart_omap_port *up)
 	u32 mvr, scheme;
 	u32 mvr, scheme;
 	u16 revision, major, minor;
 	u16 revision, major, minor;
 
 
-	mvr = serial_in(up, UART_OMAP_MVER);
+	mvr = readl(up->port.membase + (UART_OMAP_MVER << up->port.regshift));
 
 
 	/* Check revision register scheme */
 	/* Check revision register scheme */
 	scheme = mvr >> OMAP_UART_MVR_SCHEME_SHIFT;
 	scheme = mvr >> OMAP_UART_MVR_SCHEME_SHIFT;
@@ -1373,9 +1506,11 @@ static void omap_serial_fill_features_erratas(struct uart_omap_port *up)
 	case OMAP_UART_REV_52:
 	case OMAP_UART_REV_52:
 		up->errata |= (UART_ERRATA_i202_MDR1_ACCESS |
 		up->errata |= (UART_ERRATA_i202_MDR1_ACCESS |
 				UART_ERRATA_i291_DMA_FORCEIDLE);
 				UART_ERRATA_i291_DMA_FORCEIDLE);
+		up->features |= OMAP_UART_WER_HAS_TX_WAKEUP;
 		break;
 		break;
 	case OMAP_UART_REV_63:
 	case OMAP_UART_REV_63:
 		up->errata |= UART_ERRATA_i202_MDR1_ACCESS;
 		up->errata |= UART_ERRATA_i202_MDR1_ACCESS;
+		up->features |= OMAP_UART_WER_HAS_TX_WAKEUP;
 		break;
 		break;
 	default:
 	default:
 		break;
 		break;
@@ -1395,15 +1530,64 @@ static struct omap_uart_port_info *of_get_uart_port_info(struct device *dev)
 	return omap_up_info;
 	return omap_up_info;
 }
 }
 
 
+static int serial_omap_probe_rs485(struct uart_omap_port *up,
+				   struct device_node *np)
+{
+	struct serial_rs485 *rs485conf = &up->rs485;
+	u32 rs485_delay[2];
+	enum of_gpio_flags flags;
+	int ret;
+
+	rs485conf->flags = 0;
+	up->rts_gpio = -EINVAL;
+
+	if (!np)
+		return 0;
+
+	if (of_property_read_bool(np, "rs485-rts-active-high"))
+		rs485conf->flags |= SER_RS485_RTS_ON_SEND;
+	else
+		rs485conf->flags |= SER_RS485_RTS_AFTER_SEND;
+
+	/* check for tx enable gpio */
+	up->rts_gpio = of_get_named_gpio_flags(np, "rts-gpio", 0, &flags);
+	if (gpio_is_valid(up->rts_gpio)) {
+		ret = gpio_request(up->rts_gpio, "omap-serial");
+		if (ret < 0)
+			return ret;
+		ret = gpio_direction_output(up->rts_gpio,
+					    flags & SER_RS485_RTS_AFTER_SEND);
+		if (ret < 0)
+			return ret;
+	} else
+		up->rts_gpio = -EINVAL;
+
+	if (of_property_read_u32_array(np, "rs485-rts-delay",
+				    rs485_delay, 2) == 0) {
+		rs485conf->delay_rts_before_send = rs485_delay[0];
+		rs485conf->delay_rts_after_send = rs485_delay[1];
+	}
+
+	if (of_property_read_bool(np, "rs485-rx-during-tx"))
+		rs485conf->flags |= SER_RS485_RX_DURING_TX;
+
+	if (of_property_read_bool(np, "linux,rs485-enabled-at-boot-time"))
+		rs485conf->flags |= SER_RS485_ENABLED;
+
+	return 0;
+}
+
 static int serial_omap_probe(struct platform_device *pdev)
 static int serial_omap_probe(struct platform_device *pdev)
 {
 {
 	struct uart_omap_port	*up;
 	struct uart_omap_port	*up;
 	struct resource		*mem, *irq;
 	struct resource		*mem, *irq;
-	struct omap_uart_port_info *omap_up_info = pdev->dev.platform_data;
+	struct omap_uart_port_info *omap_up_info = dev_get_platdata(&pdev->dev);
 	int ret;
 	int ret;
 
 
-	if (pdev->dev.of_node)
+	if (pdev->dev.of_node) {
 		omap_up_info = of_get_uart_port_info(&pdev->dev);
 		omap_up_info = of_get_uart_port_info(&pdev->dev);
+		pdev->dev.platform_data = omap_up_info;
+	}
 
 
 	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	if (!mem) {
 	if (!mem) {
@@ -1468,12 +1652,9 @@ static int serial_omap_probe(struct platform_device *pdev)
 		goto err_port_line;
 		goto err_port_line;
 	}
 	}
 
 
-	up->pins = devm_pinctrl_get_select_default(&pdev->dev);
-	if (IS_ERR(up->pins)) {
-		dev_warn(&pdev->dev, "did not get pins for uart%i error: %li\n",
-			 up->port.line, PTR_ERR(up->pins));
-		up->pins = NULL;
-	}
+	ret = serial_omap_probe_rs485(up, pdev->dev.of_node);
+	if (ret < 0)
+		goto err_rs485;
 
 
 	sprintf(up->name, "OMAP UART%d", up->port.line);
 	sprintf(up->name, "OMAP UART%d", up->port.line);
 	up->port.mapbase = mem->start;
 	up->port.mapbase = mem->start;
@@ -1501,7 +1682,6 @@ static int serial_omap_probe(struct platform_device *pdev)
 	INIT_WORK(&up->qos_work, serial_omap_uart_qos_work);
 	INIT_WORK(&up->qos_work, serial_omap_uart_qos_work);
 
 
 	platform_set_drvdata(pdev, up);
 	platform_set_drvdata(pdev, up);
-	pm_runtime_enable(&pdev->dev);
 	if (omap_up_info->autosuspend_timeout == 0)
 	if (omap_up_info->autosuspend_timeout == 0)
 		omap_up_info->autosuspend_timeout = -1;
 		omap_up_info->autosuspend_timeout = -1;
 	device_init_wakeup(up->dev, true);
 	device_init_wakeup(up->dev, true);
@@ -1510,6 +1690,8 @@ static int serial_omap_probe(struct platform_device *pdev)
 			omap_up_info->autosuspend_timeout);
 			omap_up_info->autosuspend_timeout);
 
 
 	pm_runtime_irq_safe(&pdev->dev);
 	pm_runtime_irq_safe(&pdev->dev);
+	pm_runtime_enable(&pdev->dev);
+
 	pm_runtime_get_sync(&pdev->dev);
 	pm_runtime_get_sync(&pdev->dev);
 
 
 	omap_serial_fill_features_erratas(up);
 	omap_serial_fill_features_erratas(up);
@@ -1529,6 +1711,7 @@ err_add_port:
 	pm_runtime_put(&pdev->dev);
 	pm_runtime_put(&pdev->dev);
 	pm_runtime_disable(&pdev->dev);
 	pm_runtime_disable(&pdev->dev);
 err_ioremap:
 err_ioremap:
+err_rs485:
 err_port_line:
 err_port_line:
 	dev_err(&pdev->dev, "[UART%d]: failure [%s]: %d\n",
 	dev_err(&pdev->dev, "[UART%d]: failure [%s]: %d\n",
 				pdev->id, __func__, ret);
 				pdev->id, __func__, ret);
@@ -1609,6 +1792,7 @@ static void serial_omap_restore_context(struct uart_omap_port *up)
 		serial_omap_mdr1_errataset(up, up->mdr1);
 		serial_omap_mdr1_errataset(up, up->mdr1);
 	else
 	else
 		serial_out(up, UART_OMAP_MDR1, up->mdr1);
 		serial_out(up, UART_OMAP_MDR1, up->mdr1);
+	serial_out(up, UART_OMAP_WER, up->wer);
 }
 }
 
 
 static int serial_omap_runtime_suspend(struct device *dev)
 static int serial_omap_runtime_suspend(struct device *dev)

+ 56 - 28
drivers/tty/serial/pch_uart.c

@@ -232,7 +232,7 @@ struct eg20t_port {
 	unsigned int iobase;
 	unsigned int iobase;
 	struct pci_dev *pdev;
 	struct pci_dev *pdev;
 	int fifo_size;
 	int fifo_size;
-	int uartclk;
+	unsigned int uartclk;
 	int start_tx;
 	int start_tx;
 	int start_rx;
 	int start_rx;
 	int tx_empty;
 	int tx_empty;
@@ -373,35 +373,62 @@ static const struct file_operations port_regs_ops = {
 };
 };
 #endif	/* CONFIG_DEBUG_FS */
 #endif	/* CONFIG_DEBUG_FS */
 
 
+static struct dmi_system_id pch_uart_dmi_table[] = {
+	{
+		.ident = "CM-iTC",
+		{
+			DMI_MATCH(DMI_BOARD_NAME, "CM-iTC"),
+		},
+		(void *)CMITC_UARTCLK,
+	},
+	{
+		.ident = "FRI2",
+		{
+			DMI_MATCH(DMI_BIOS_VERSION, "FRI2"),
+		},
+		(void *)FRI2_64_UARTCLK,
+	},
+	{
+		.ident = "Fish River Island II",
+		{
+			DMI_MATCH(DMI_PRODUCT_NAME, "Fish River Island II"),
+		},
+		(void *)FRI2_48_UARTCLK,
+	},
+	{
+		.ident = "COMe-mTT",
+		{
+			DMI_MATCH(DMI_BOARD_NAME, "COMe-mTT"),
+		},
+		(void *)NTC1_UARTCLK,
+	},
+	{
+		.ident = "nanoETXexpress-TT",
+		{
+			DMI_MATCH(DMI_BOARD_NAME, "nanoETXexpress-TT"),
+		},
+		(void *)NTC1_UARTCLK,
+	},
+	{
+		.ident = "MinnowBoard",
+		{
+			DMI_MATCH(DMI_BOARD_NAME, "MinnowBoard"),
+		},
+		(void *)MINNOW_UARTCLK,
+	},
+};
+
 /* Return UART clock, checking for board specific clocks. */
 /* Return UART clock, checking for board specific clocks. */
-static int pch_uart_get_uartclk(void)
+static unsigned int pch_uart_get_uartclk(void)
 {
 {
-	const char *cmp;
+	const struct dmi_system_id *d;
 
 
 	if (user_uartclk)
 	if (user_uartclk)
 		return user_uartclk;
 		return user_uartclk;
 
 
-	cmp = dmi_get_system_info(DMI_BOARD_NAME);
-	if (cmp && strstr(cmp, "CM-iTC"))
-		return CMITC_UARTCLK;
-
-	cmp = dmi_get_system_info(DMI_BIOS_VERSION);
-	if (cmp && strnstr(cmp, "FRI2", 4))
-		return FRI2_64_UARTCLK;
-
-	cmp = dmi_get_system_info(DMI_PRODUCT_NAME);
-	if (cmp && strstr(cmp, "Fish River Island II"))
-		return FRI2_48_UARTCLK;
-
-	/* Kontron COMe-mTT10 (nanoETXexpress-TT) */
-	cmp = dmi_get_system_info(DMI_BOARD_NAME);
-	if (cmp && (strstr(cmp, "COMe-mTT") ||
-		    strstr(cmp, "nanoETXexpress-TT")))
-		return NTC1_UARTCLK;
-
-	cmp = dmi_get_system_info(DMI_BOARD_NAME);
-	if (cmp && strstr(cmp, "MinnowBoard"))
-		return MINNOW_UARTCLK;
+	d = dmi_first_match(pch_uart_dmi_table);
+	if (d)
+		return (unsigned long)d->driver_data;
 
 
 	return DEFAULT_UARTCLK;
 	return DEFAULT_UARTCLK;
 }
 }
@@ -422,7 +449,7 @@ static void pch_uart_hal_disable_interrupt(struct eg20t_port *priv,
 	iowrite8(ier, priv->membase + UART_IER);
 	iowrite8(ier, priv->membase + UART_IER);
 }
 }
 
 
-static int pch_uart_hal_set_line(struct eg20t_port *priv, int baud,
+static int pch_uart_hal_set_line(struct eg20t_port *priv, unsigned int baud,
 				 unsigned int parity, unsigned int bits,
 				 unsigned int parity, unsigned int bits,
 				 unsigned int stb)
 				 unsigned int stb)
 {
 {
@@ -457,7 +484,7 @@ static int pch_uart_hal_set_line(struct eg20t_port *priv, int baud,
 	lcr |= bits;
 	lcr |= bits;
 	lcr |= stb;
 	lcr |= stb;
 
 
-	dev_dbg(priv->port.dev, "%s:baud = %d, div = %04x, lcr = %02x (%lu)\n",
+	dev_dbg(priv->port.dev, "%s:baud = %u, div = %04x, lcr = %02x (%lu)\n",
 		 __func__, baud, div, lcr, jiffies);
 		 __func__, baud, div, lcr, jiffies);
 	iowrite8(PCH_UART_LCR_DLAB, priv->membase + UART_LCR);
 	iowrite8(PCH_UART_LCR_DLAB, priv->membase + UART_LCR);
 	iowrite8(dll, priv->membase + PCH_UART_DLL);
 	iowrite8(dll, priv->membase + PCH_UART_DLL);
@@ -1363,9 +1390,8 @@ static void pch_uart_shutdown(struct uart_port *port)
 static void pch_uart_set_termios(struct uart_port *port,
 static void pch_uart_set_termios(struct uart_port *port,
 				 struct ktermios *termios, struct ktermios *old)
 				 struct ktermios *termios, struct ktermios *old)
 {
 {
-	int baud;
 	int rtn;
 	int rtn;
-	unsigned int parity, bits, stb;
+	unsigned int baud, parity, bits, stb;
 	struct eg20t_port *priv;
 	struct eg20t_port *priv;
 	unsigned long flags;
 	unsigned long flags;
 
 
@@ -1498,6 +1524,7 @@ static int pch_uart_verify_port(struct uart_port *port,
 	return 0;
 	return 0;
 }
 }
 
 
+#if defined(CONFIG_CONSOLE_POLL) || defined(CONFIG_SERIAL_PCH_UART_CONSOLE)
 /*
 /*
  *	Wait for transmitter & holding register to empty
  *	Wait for transmitter & holding register to empty
  */
  */
@@ -1528,6 +1555,7 @@ static void wait_for_xmitr(struct eg20t_port *up, int bits)
 		}
 		}
 	}
 	}
 }
 }
+#endif /* CONFIG_CONSOLE_POLL || CONFIG_SERIAL_PCH_UART_CONSOLE */
 
 
 #ifdef CONFIG_CONSOLE_POLL
 #ifdef CONFIG_CONSOLE_POLL
 /*
 /*

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

@@ -1798,7 +1798,6 @@ static int __exit pmz_detach(struct platform_device *pdev)
 
 
 	uart_remove_one_port(&pmz_uart_reg, &uap->port);
 	uart_remove_one_port(&pmz_uart_reg, &uap->port);
 
 
-	platform_set_drvdata(pdev, NULL);
 	uap->port.dev = NULL;
 	uap->port.dev = NULL;
 
 
 	return 0;
 	return 0;

+ 3 - 2
drivers/tty/serial/pnx8xxx_uart.c

@@ -237,7 +237,10 @@ static void pnx8xxx_rx_chars(struct pnx8xxx_port *sport)
 		status = FIFO_TO_SM(serial_in(sport, PNX8XXX_FIFO)) |
 		status = FIFO_TO_SM(serial_in(sport, PNX8XXX_FIFO)) |
 			 ISTAT_TO_SM(serial_in(sport, PNX8XXX_ISTAT));
 			 ISTAT_TO_SM(serial_in(sport, PNX8XXX_ISTAT));
 	}
 	}
+
+	spin_unlock(&sport->port.lock);
 	tty_flip_buffer_push(&sport->port.state->port);
 	tty_flip_buffer_push(&sport->port.state->port);
+	spin_lock(&sport->port.lock);
 }
 }
 
 
 static void pnx8xxx_tx_chars(struct pnx8xxx_port *sport)
 static void pnx8xxx_tx_chars(struct pnx8xxx_port *sport)
@@ -801,8 +804,6 @@ static int pnx8xxx_serial_remove(struct platform_device *pdev)
 {
 {
 	struct pnx8xxx_port *sport = platform_get_drvdata(pdev);
 	struct pnx8xxx_port *sport = platform_get_drvdata(pdev);
 
 
-	platform_set_drvdata(pdev, NULL);
-
 	if (sport)
 	if (sport)
 		uart_remove_one_port(&pnx8xxx_reg, &sport->port);
 		uart_remove_one_port(&pnx8xxx_reg, &sport->port);
 
 

+ 3 - 30
drivers/tty/serial/pxa.c

@@ -332,31 +332,6 @@ static void serial_pxa_break_ctl(struct uart_port *port, int break_state)
 	spin_unlock_irqrestore(&up->port.lock, flags);
 	spin_unlock_irqrestore(&up->port.lock, flags);
 }
 }
 
 
-#if 0
-static void serial_pxa_dma_init(struct pxa_uart *up)
-{
-	up->rxdma =
-		pxa_request_dma(up->name, DMA_PRIO_LOW, pxa_receive_dma, up);
-	if (up->rxdma < 0)
-		goto out;
-	up->txdma =
-		pxa_request_dma(up->name, DMA_PRIO_LOW, pxa_transmit_dma, up);
-	if (up->txdma < 0)
-		goto err_txdma;
-	up->dmadesc = kmalloc(4 * sizeof(pxa_dma_desc), GFP_KERNEL);
-	if (!up->dmadesc)
-		goto err_alloc;
-
-	/* ... */
-err_alloc:
-	pxa_free_dma(up->txdma);
-err_rxdma:
-	pxa_free_dma(up->rxdma);
-out:
-	return;
-}
-#endif
-
 static int serial_pxa_startup(struct uart_port *port)
 static int serial_pxa_startup(struct uart_port *port)
 {
 {
 	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
 	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
@@ -790,7 +765,7 @@ static struct console serial_pxa_console = {
 #define PXA_CONSOLE	NULL
 #define PXA_CONSOLE	NULL
 #endif
 #endif
 
 
-struct uart_ops serial_pxa_pops = {
+static struct uart_ops serial_pxa_pops = {
 	.tx_empty	= serial_pxa_tx_empty,
 	.tx_empty	= serial_pxa_tx_empty,
 	.set_mctrl	= serial_pxa_set_mctrl,
 	.set_mctrl	= serial_pxa_set_mctrl,
 	.get_mctrl	= serial_pxa_get_mctrl,
 	.get_mctrl	= serial_pxa_get_mctrl,
@@ -945,8 +920,6 @@ static int serial_pxa_remove(struct platform_device *dev)
 {
 {
 	struct uart_pxa_port *sport = platform_get_drvdata(dev);
 	struct uart_pxa_port *sport = platform_get_drvdata(dev);
 
 
-	platform_set_drvdata(dev, NULL);
-
 	uart_remove_one_port(&serial_pxa_reg, &sport->port);
 	uart_remove_one_port(&serial_pxa_reg, &sport->port);
 
 
 	clk_unprepare(sport->clk);
 	clk_unprepare(sport->clk);
@@ -970,7 +943,7 @@ static struct platform_driver serial_pxa_driver = {
 	},
 	},
 };
 };
 
 
-int __init serial_pxa_init(void)
+static int __init serial_pxa_init(void)
 {
 {
 	int ret;
 	int ret;
 
 
@@ -985,7 +958,7 @@ int __init serial_pxa_init(void)
 	return ret;
 	return ret;
 }
 }
 
 
-void __exit serial_pxa_exit(void)
+static void __exit serial_pxa_exit(void)
 {
 {
 	platform_driver_unregister(&serial_pxa_driver);
 	platform_driver_unregister(&serial_pxa_driver);
 	uart_unregister_driver(&serial_pxa_reg);
 	uart_unregister_driver(&serial_pxa_reg);

+ 2 - 0
drivers/tty/serial/rp2.c

@@ -427,7 +427,9 @@ static void rp2_rx_chars(struct rp2_uart_port *up)
 		up->port.icount.rx++;
 		up->port.icount.rx++;
 	}
 	}
 
 
+	spin_unlock(&up->port.lock);
 	tty_flip_buffer_push(port);
 	tty_flip_buffer_push(port);
+	spin_lock(&up->port.lock);
 }
 }
 
 
 static void rp2_tx_chars(struct rp2_uart_port *up)
 static void rp2_tx_chars(struct rp2_uart_port *up)

+ 3 - 2
drivers/tty/serial/sa1100.c

@@ -232,7 +232,10 @@ sa1100_rx_chars(struct sa1100_port *sport)
 		status = UTSR1_TO_SM(UART_GET_UTSR1(sport)) |
 		status = UTSR1_TO_SM(UART_GET_UTSR1(sport)) |
 			 UTSR0_TO_SM(UART_GET_UTSR0(sport));
 			 UTSR0_TO_SM(UART_GET_UTSR0(sport));
 	}
 	}
+
+	spin_unlock(&sport->port.lock);
 	tty_flip_buffer_push(&sport->port.state->port);
 	tty_flip_buffer_push(&sport->port.state->port);
+	spin_lock(&sport->port.lock);
 }
 }
 
 
 static void sa1100_tx_chars(struct sa1100_port *sport)
 static void sa1100_tx_chars(struct sa1100_port *sport)
@@ -864,8 +867,6 @@ static int sa1100_serial_remove(struct platform_device *pdev)
 {
 {
 	struct sa1100_port *sport = platform_get_drvdata(pdev);
 	struct sa1100_port *sport = platform_get_drvdata(pdev);
 
 
-	platform_set_drvdata(pdev, NULL);
-
 	if (sport)
 	if (sport)
 		uart_remove_one_port(&sa1100_reg, &sport->port);
 		uart_remove_one_port(&sa1100_reg, &sport->port);
 
 

+ 6 - 3
drivers/tty/serial/samsung.c

@@ -249,6 +249,8 @@ s3c24xx_serial_rx_chars(int irq, void *dev_id)
 					ufcon |= S3C2410_UFCON_RESETRX;
 					ufcon |= S3C2410_UFCON_RESETRX;
 					wr_regl(port, S3C2410_UFCON, ufcon);
 					wr_regl(port, S3C2410_UFCON, ufcon);
 					rx_enabled(port) = 1;
 					rx_enabled(port) = 1;
+					spin_unlock_irqrestore(&port->lock,
+							flags);
 					goto out;
 					goto out;
 				}
 				}
 				continue;
 				continue;
@@ -297,10 +299,11 @@ s3c24xx_serial_rx_chars(int irq, void *dev_id)
  ignore_char:
  ignore_char:
 		continue;
 		continue;
 	}
 	}
+
+	spin_unlock_irqrestore(&port->lock, flags);
 	tty_flip_buffer_push(&port->state->port);
 	tty_flip_buffer_push(&port->state->port);
 
 
  out:
  out:
-	spin_unlock_irqrestore(&port->lock, flags);
 	return IRQ_HANDLED;
 	return IRQ_HANDLED;
 }
 }
 
 
@@ -1250,8 +1253,8 @@ static int s3c24xx_serial_probe(struct platform_device *pdev)
 
 
 	ourport->baudclk = ERR_PTR(-EINVAL);
 	ourport->baudclk = ERR_PTR(-EINVAL);
 	ourport->info = ourport->drv_data->info;
 	ourport->info = ourport->drv_data->info;
-	ourport->cfg = (pdev->dev.platform_data) ?
-			(struct s3c2410_uartcfg *)pdev->dev.platform_data :
+	ourport->cfg = (dev_get_platdata(&pdev->dev)) ?
+			(struct s3c2410_uartcfg *)dev_get_platdata(&pdev->dev) :
 			ourport->drv_data->def_cfg;
 			ourport->drv_data->def_cfg;
 
 
 	ourport->port.fifosize = (ourport->info->fifosize) ?
 	ourport->port.fifosize = (ourport->info->fifosize) ?

+ 2 - 1
drivers/tty/serial/samsung.h

@@ -68,7 +68,8 @@ struct s3c24xx_uart_port {
 /* register access controls */
 /* register access controls */
 
 
 #define portaddr(port, reg) ((port)->membase + (reg))
 #define portaddr(port, reg) ((port)->membase + (reg))
-#define portaddrl(port, reg) ((unsigned long *)((port)->membase + (reg)))
+#define portaddrl(port, reg) \
+	((unsigned long *)(unsigned long)((port)->membase + (reg)))
 
 
 #define rd_regb(port, reg) (__raw_readb(portaddr(port, reg)))
 #define rd_regb(port, reg) (__raw_readb(portaddr(port, reg)))
 #define rd_regl(port, reg) (__raw_readl(portaddr(port, reg)))
 #define rd_regl(port, reg) (__raw_readl(portaddr(port, reg)))

+ 1 - 1
drivers/tty/serial/sc26xx.c

@@ -637,7 +637,7 @@ static int sc26xx_probe(struct platform_device *dev)
 {
 {
 	struct resource *res;
 	struct resource *res;
 	struct uart_sc26xx_port *up;
 	struct uart_sc26xx_port *up;
-	unsigned int *sc26xx_data = dev->dev.platform_data;
+	unsigned int *sc26xx_data = dev_get_platdata(&dev->dev);
 	int err;
 	int err;
 
 
 	res = platform_get_resource(dev, IORESOURCE_MEM, 0);
 	res = platform_get_resource(dev, IORESOURCE_MEM, 0);

+ 158 - 181
drivers/tty/serial/sccnxp.c

@@ -15,6 +15,7 @@
 #define SUPPORT_SYSRQ
 #define SUPPORT_SYSRQ
 #endif
 #endif
 
 
+#include <linux/clk.h>
 #include <linux/err.h>
 #include <linux/err.h>
 #include <linux/module.h>
 #include <linux/module.h>
 #include <linux/device.h>
 #include <linux/device.h>
@@ -94,16 +95,17 @@
 #define MCTRL_IBIT(cfg, sig)		((((cfg) >> (sig)) & 0xf) - LINE_IP0)
 #define MCTRL_IBIT(cfg, sig)		((((cfg) >> (sig)) & 0xf) - LINE_IP0)
 #define MCTRL_OBIT(cfg, sig)		((((cfg) >> (sig)) & 0xf) - LINE_OP0)
 #define MCTRL_OBIT(cfg, sig)		((((cfg) >> (sig)) & 0xf) - LINE_OP0)
 
 
-/* Supported chip types */
-enum {
-	SCCNXP_TYPE_SC2681	= 2681,
-	SCCNXP_TYPE_SC2691	= 2691,
-	SCCNXP_TYPE_SC2692	= 2692,
-	SCCNXP_TYPE_SC2891	= 2891,
-	SCCNXP_TYPE_SC2892	= 2892,
-	SCCNXP_TYPE_SC28202	= 28202,
-	SCCNXP_TYPE_SC68681	= 68681,
-	SCCNXP_TYPE_SC68692	= 68692,
+#define SCCNXP_HAVE_IO		0x00000001
+#define SCCNXP_HAVE_MR0		0x00000002
+
+struct sccnxp_chip {
+	const char		*name;
+	unsigned int		nr;
+	unsigned long		freq_min;
+	unsigned long		freq_std;
+	unsigned long		freq_max;
+	unsigned int		flags;
+	unsigned int		fifosize;
 };
 };
 
 
 struct sccnxp_port {
 struct sccnxp_port {
@@ -111,16 +113,10 @@ struct sccnxp_port {
 	struct uart_port	port[SCCNXP_MAX_UARTS];
 	struct uart_port	port[SCCNXP_MAX_UARTS];
 	bool			opened[SCCNXP_MAX_UARTS];
 	bool			opened[SCCNXP_MAX_UARTS];
 
 
-	const char		*name;
 	int			irq;
 	int			irq;
-
 	u8			imr;
 	u8			imr;
-	u8			addr_mask;
-	int			freq_std;
 
 
-	int			flags;
-#define SCCNXP_HAVE_IO		0x00000001
-#define SCCNXP_HAVE_MR0		0x00000002
+	struct sccnxp_chip	*chip;
 
 
 #ifdef CONFIG_SERIAL_SCCNXP_CONSOLE
 #ifdef CONFIG_SERIAL_SCCNXP_CONSOLE
 	struct console		console;
 	struct console		console;
@@ -136,29 +132,94 @@ struct sccnxp_port {
 	struct regulator	*regulator;
 	struct regulator	*regulator;
 };
 };
 
 
-static inline u8 sccnxp_raw_read(void __iomem *base, u8 reg, u8 shift)
-{
-	return readb(base + (reg << shift));
-}
+static const struct sccnxp_chip sc2681 = {
+	.name		= "SC2681",
+	.nr		= 2,
+	.freq_min	= 1000000,
+	.freq_std	= 3686400,
+	.freq_max	= 4000000,
+	.flags		= SCCNXP_HAVE_IO,
+	.fifosize	= 3,
+};
 
 
-static inline void sccnxp_raw_write(void __iomem *base, u8 reg, u8 shift, u8 v)
-{
-	writeb(v, base + (reg << shift));
-}
+static const struct sccnxp_chip sc2691 = {
+	.name		= "SC2691",
+	.nr		= 1,
+	.freq_min	= 1000000,
+	.freq_std	= 3686400,
+	.freq_max	= 4000000,
+	.flags		= 0,
+	.fifosize	= 3,
+};
+
+static const struct sccnxp_chip sc2692 = {
+	.name		= "SC2692",
+	.nr		= 2,
+	.freq_min	= 1000000,
+	.freq_std	= 3686400,
+	.freq_max	= 4000000,
+	.flags		= SCCNXP_HAVE_IO,
+	.fifosize	= 3,
+};
+
+static const struct sccnxp_chip sc2891 = {
+	.name		= "SC2891",
+	.nr		= 1,
+	.freq_min	= 100000,
+	.freq_std	= 3686400,
+	.freq_max	= 8000000,
+	.flags		= SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0,
+	.fifosize	= 16,
+};
+
+static const struct sccnxp_chip sc2892 = {
+	.name		= "SC2892",
+	.nr		= 2,
+	.freq_min	= 100000,
+	.freq_std	= 3686400,
+	.freq_max	= 8000000,
+	.flags		= SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0,
+	.fifosize	= 16,
+};
+
+static const struct sccnxp_chip sc28202 = {
+	.name		= "SC28202",
+	.nr		= 2,
+	.freq_min	= 1000000,
+	.freq_std	= 14745600,
+	.freq_max	= 50000000,
+	.flags		= SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0,
+	.fifosize	= 256,
+};
+
+static const struct sccnxp_chip sc68681 = {
+	.name		= "SC68681",
+	.nr		= 2,
+	.freq_min	= 1000000,
+	.freq_std	= 3686400,
+	.freq_max	= 4000000,
+	.flags		= SCCNXP_HAVE_IO,
+	.fifosize	= 3,
+};
+
+static const struct sccnxp_chip sc68692 = {
+	.name		= "SC68692",
+	.nr		= 2,
+	.freq_min	= 1000000,
+	.freq_std	= 3686400,
+	.freq_max	= 4000000,
+	.flags		= SCCNXP_HAVE_IO,
+	.fifosize	= 3,
+};
 
 
 static inline u8 sccnxp_read(struct uart_port *port, u8 reg)
 static inline u8 sccnxp_read(struct uart_port *port, u8 reg)
 {
 {
-	struct sccnxp_port *s = dev_get_drvdata(port->dev);
-
-	return sccnxp_raw_read(port->membase, reg & s->addr_mask,
-			       port->regshift);
+	return readb(port->membase + (reg << port->regshift));
 }
 }
 
 
 static inline void sccnxp_write(struct uart_port *port, u8 reg, u8 v)
 static inline void sccnxp_write(struct uart_port *port, u8 reg, u8 v)
 {
 {
-	struct sccnxp_port *s = dev_get_drvdata(port->dev);
-
-	sccnxp_raw_write(port->membase, reg & s->addr_mask, port->regshift, v);
+	writeb(v, port->membase + (reg << port->regshift));
 }
 }
 
 
 static inline u8 sccnxp_port_read(struct uart_port *port, u8 reg)
 static inline u8 sccnxp_port_read(struct uart_port *port, u8 reg)
@@ -224,13 +285,14 @@ static int sccnxp_set_baud(struct uart_port *port, int baud)
 {
 {
 	struct sccnxp_port *s = dev_get_drvdata(port->dev);
 	struct sccnxp_port *s = dev_get_drvdata(port->dev);
 	int div_std, tmp_baud, bestbaud = baud, besterr = -1;
 	int div_std, tmp_baud, bestbaud = baud, besterr = -1;
+	struct sccnxp_chip *chip = s->chip;
 	u8 i, acr = 0, csr = 0, mr0 = 0;
 	u8 i, acr = 0, csr = 0, mr0 = 0;
 
 
 	/* Find best baud from table */
 	/* Find best baud from table */
 	for (i = 0; baud_std[i].baud && besterr; i++) {
 	for (i = 0; baud_std[i].baud && besterr; i++) {
-		if (baud_std[i].mr0 && !(s->flags & SCCNXP_HAVE_MR0))
+		if (baud_std[i].mr0 && !(chip->flags & SCCNXP_HAVE_MR0))
 			continue;
 			continue;
-		div_std = DIV_ROUND_CLOSEST(s->freq_std, baud_std[i].baud);
+		div_std = DIV_ROUND_CLOSEST(chip->freq_std, baud_std[i].baud);
 		tmp_baud = DIV_ROUND_CLOSEST(port->uartclk, div_std);
 		tmp_baud = DIV_ROUND_CLOSEST(port->uartclk, div_std);
 		if (!sccnxp_update_best_err(baud, tmp_baud, &besterr)) {
 		if (!sccnxp_update_best_err(baud, tmp_baud, &besterr)) {
 			acr = baud_std[i].acr;
 			acr = baud_std[i].acr;
@@ -240,7 +302,7 @@ static int sccnxp_set_baud(struct uart_port *port, int baud)
 		}
 		}
 	}
 	}
 
 
-	if (s->flags & SCCNXP_HAVE_MR0) {
+	if (chip->flags & SCCNXP_HAVE_MR0) {
 		/* Enable FIFO, set half level for TX */
 		/* Enable FIFO, set half level for TX */
 		mr0 |= MR0_FIFO | MR0_TXLVL;
 		mr0 |= MR0_FIFO | MR0_TXLVL;
 		/* Update MR0 */
 		/* Update MR0 */
@@ -363,7 +425,7 @@ static void sccnxp_handle_tx(struct uart_port *port)
 			sccnxp_disable_irq(port, IMR_TXRDY);
 			sccnxp_disable_irq(port, IMR_TXRDY);
 
 
 			/* Set direction to input */
 			/* Set direction to input */
-			if (s->flags & SCCNXP_HAVE_IO)
+			if (s->chip->flags & SCCNXP_HAVE_IO)
 				sccnxp_set_bit(port, DIR_OP, 0);
 				sccnxp_set_bit(port, DIR_OP, 0);
 		}
 		}
 		return;
 		return;
@@ -437,7 +499,7 @@ static void sccnxp_start_tx(struct uart_port *port)
 	spin_lock_irqsave(&s->lock, flags);
 	spin_lock_irqsave(&s->lock, flags);
 
 
 	/* Set direction to output */
 	/* Set direction to output */
-	if (s->flags & SCCNXP_HAVE_IO)
+	if (s->chip->flags & SCCNXP_HAVE_IO)
 		sccnxp_set_bit(port, DIR_OP, 1);
 		sccnxp_set_bit(port, DIR_OP, 1);
 
 
 	sccnxp_enable_irq(port, IMR_TXRDY);
 	sccnxp_enable_irq(port, IMR_TXRDY);
@@ -483,7 +545,7 @@ static void sccnxp_set_mctrl(struct uart_port *port, unsigned int mctrl)
 	struct sccnxp_port *s = dev_get_drvdata(port->dev);
 	struct sccnxp_port *s = dev_get_drvdata(port->dev);
 	unsigned long flags;
 	unsigned long flags;
 
 
-	if (!(s->flags & SCCNXP_HAVE_IO))
+	if (!(s->chip->flags & SCCNXP_HAVE_IO))
 		return;
 		return;
 
 
 	spin_lock_irqsave(&s->lock, flags);
 	spin_lock_irqsave(&s->lock, flags);
@@ -501,7 +563,7 @@ static unsigned int sccnxp_get_mctrl(struct uart_port *port)
 	struct sccnxp_port *s = dev_get_drvdata(port->dev);
 	struct sccnxp_port *s = dev_get_drvdata(port->dev);
 	unsigned int mctrl = TIOCM_DSR | TIOCM_CTS | TIOCM_CAR;
 	unsigned int mctrl = TIOCM_DSR | TIOCM_CTS | TIOCM_CAR;
 
 
-	if (!(s->flags & SCCNXP_HAVE_IO))
+	if (!(s->chip->flags & SCCNXP_HAVE_IO))
 		return mctrl;
 		return mctrl;
 
 
 	spin_lock_irqsave(&s->lock, flags);
 	spin_lock_irqsave(&s->lock, flags);
@@ -617,7 +679,7 @@ static void sccnxp_set_termios(struct uart_port *port,
 
 
 	/* Setup baudrate */
 	/* Setup baudrate */
 	baud = uart_get_baud_rate(port, termios, old, 50,
 	baud = uart_get_baud_rate(port, termios, old, 50,
-				  (s->flags & SCCNXP_HAVE_MR0) ?
+				  (s->chip->flags & SCCNXP_HAVE_MR0) ?
 				  230400 : 38400);
 				  230400 : 38400);
 	baud = sccnxp_set_baud(port, baud);
 	baud = sccnxp_set_baud(port, baud);
 
 
@@ -641,7 +703,7 @@ static int sccnxp_startup(struct uart_port *port)
 
 
 	spin_lock_irqsave(&s->lock, flags);
 	spin_lock_irqsave(&s->lock, flags);
 
 
-	if (s->flags & SCCNXP_HAVE_IO) {
+	if (s->chip->flags & SCCNXP_HAVE_IO) {
 		/* Outputs are controlled manually */
 		/* Outputs are controlled manually */
 		sccnxp_write(port, SCCNXP_OPCR_REG, 0);
 		sccnxp_write(port, SCCNXP_OPCR_REG, 0);
 	}
 	}
@@ -681,7 +743,7 @@ static void sccnxp_shutdown(struct uart_port *port)
 	sccnxp_port_write(port, SCCNXP_CR_REG, CR_RX_DISABLE | CR_TX_DISABLE);
 	sccnxp_port_write(port, SCCNXP_CR_REG, CR_RX_DISABLE | CR_TX_DISABLE);
 
 
 	/* Leave direction to input */
 	/* Leave direction to input */
-	if (s->flags & SCCNXP_HAVE_IO)
+	if (s->chip->flags & SCCNXP_HAVE_IO)
 		sccnxp_set_bit(port, DIR_OP, 0);
 		sccnxp_set_bit(port, DIR_OP, 0);
 
 
 	spin_unlock_irqrestore(&s->lock, flags);
 	spin_unlock_irqrestore(&s->lock, flags);
@@ -691,7 +753,7 @@ static const char *sccnxp_type(struct uart_port *port)
 {
 {
 	struct sccnxp_port *s = dev_get_drvdata(port->dev);
 	struct sccnxp_port *s = dev_get_drvdata(port->dev);
 
 
-	return (port->type == PORT_SC26XX) ? s->name : NULL;
+	return (port->type == PORT_SC26XX) ? s->chip->name : NULL;
 }
 }
 
 
 static void sccnxp_release_port(struct uart_port *port)
 static void sccnxp_release_port(struct uart_port *port)
@@ -778,19 +840,31 @@ static int sccnxp_console_setup(struct console *co, char *options)
 }
 }
 #endif
 #endif
 
 
+static const struct platform_device_id sccnxp_id_table[] = {
+	{ .name = "sc2681",	.driver_data = (kernel_ulong_t)&sc2681, },
+	{ .name = "sc2691",	.driver_data = (kernel_ulong_t)&sc2691, },
+	{ .name = "sc2692",	.driver_data = (kernel_ulong_t)&sc2692, },
+	{ .name = "sc2891",	.driver_data = (kernel_ulong_t)&sc2891, },
+	{ .name = "sc2892",	.driver_data = (kernel_ulong_t)&sc2892, },
+	{ .name = "sc28202",	.driver_data = (kernel_ulong_t)&sc28202, },
+	{ .name = "sc68681",	.driver_data = (kernel_ulong_t)&sc68681, },
+	{ .name = "sc68692",	.driver_data = (kernel_ulong_t)&sc68692, },
+	{ }
+};
+MODULE_DEVICE_TABLE(platform, sccnxp_id_table);
+
 static int sccnxp_probe(struct platform_device *pdev)
 static int sccnxp_probe(struct platform_device *pdev)
 {
 {
 	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	int chiptype = pdev->id_entry->driver_data;
 	struct sccnxp_pdata *pdata = dev_get_platdata(&pdev->dev);
 	struct sccnxp_pdata *pdata = dev_get_platdata(&pdev->dev);
-	int i, ret, fifosize, freq_min, freq_max;
+	int i, ret, uartclk;
 	struct sccnxp_port *s;
 	struct sccnxp_port *s;
 	void __iomem *membase;
 	void __iomem *membase;
+	struct clk *clk;
 
 
-	if (!res) {
-		dev_err(&pdev->dev, "Missing memory resource data\n");
-		return -EADDRNOTAVAIL;
-	}
+	membase = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(membase))
+		return PTR_ERR(membase);
 
 
 	s = devm_kzalloc(&pdev->dev, sizeof(struct sccnxp_port), GFP_KERNEL);
 	s = devm_kzalloc(&pdev->dev, sizeof(struct sccnxp_port), GFP_KERNEL);
 	if (!s) {
 	if (!s) {
@@ -801,99 +875,38 @@ static int sccnxp_probe(struct platform_device *pdev)
 
 
 	spin_lock_init(&s->lock);
 	spin_lock_init(&s->lock);
 
 
-	/* Individual chip settings */
-	switch (chiptype) {
-	case SCCNXP_TYPE_SC2681:
-		s->name		= "SC2681";
-		s->uart.nr	= 2;
-		s->freq_std	= 3686400;
-		s->addr_mask	= 0x0f;
-		s->flags	= SCCNXP_HAVE_IO;
-		fifosize	= 3;
-		freq_min	= 1000000;
-		freq_max	= 4000000;
-		break;
-	case SCCNXP_TYPE_SC2691:
-		s->name		= "SC2691";
-		s->uart.nr	= 1;
-		s->freq_std	= 3686400;
-		s->addr_mask	= 0x07;
-		s->flags	= 0;
-		fifosize	= 3;
-		freq_min	= 1000000;
-		freq_max	= 4000000;
-		break;
-	case SCCNXP_TYPE_SC2692:
-		s->name		= "SC2692";
-		s->uart.nr	= 2;
-		s->freq_std	= 3686400;
-		s->addr_mask	= 0x0f;
-		s->flags	= SCCNXP_HAVE_IO;
-		fifosize	= 3;
-		freq_min	= 1000000;
-		freq_max	= 4000000;
-		break;
-	case SCCNXP_TYPE_SC2891:
-		s->name		= "SC2891";
-		s->uart.nr	= 1;
-		s->freq_std	= 3686400;
-		s->addr_mask	= 0x0f;
-		s->flags	= SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0;
-		fifosize	= 16;
-		freq_min	= 100000;
-		freq_max	= 8000000;
-		break;
-	case SCCNXP_TYPE_SC2892:
-		s->name		= "SC2892";
-		s->uart.nr	= 2;
-		s->freq_std	= 3686400;
-		s->addr_mask	= 0x0f;
-		s->flags	= SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0;
-		fifosize	= 16;
-		freq_min	= 100000;
-		freq_max	= 8000000;
-		break;
-	case SCCNXP_TYPE_SC28202:
-		s->name		= "SC28202";
-		s->uart.nr	= 2;
-		s->freq_std	= 14745600;
-		s->addr_mask	= 0x7f;
-		s->flags	= SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0;
-		fifosize	= 256;
-		freq_min	= 1000000;
-		freq_max	= 50000000;
-		break;
-	case SCCNXP_TYPE_SC68681:
-		s->name		= "SC68681";
-		s->uart.nr	= 2;
-		s->freq_std	= 3686400;
-		s->addr_mask	= 0x0f;
-		s->flags	= SCCNXP_HAVE_IO;
-		fifosize	= 3;
-		freq_min	= 1000000;
-		freq_max	= 4000000;
-		break;
-	case SCCNXP_TYPE_SC68692:
-		s->name		= "SC68692";
-		s->uart.nr	= 2;
-		s->freq_std	= 3686400;
-		s->addr_mask	= 0x0f;
-		s->flags	= SCCNXP_HAVE_IO;
-		fifosize	= 3;
-		freq_min	= 1000000;
-		freq_max	= 4000000;
-		break;
-	default:
-		dev_err(&pdev->dev, "Unsupported chip type %i\n", chiptype);
-		ret = -ENOTSUPP;
+	s->chip = (struct sccnxp_chip *)pdev->id_entry->driver_data;
+
+	s->regulator = devm_regulator_get(&pdev->dev, "vcc");
+	if (!IS_ERR(s->regulator)) {
+		ret = regulator_enable(s->regulator);
+		if (ret) {
+			dev_err(&pdev->dev,
+				"Failed to enable regulator: %i\n", ret);
+			return ret;
+		}
+	} else if (PTR_ERR(s->regulator) == -EPROBE_DEFER)
+		return -EPROBE_DEFER;
+
+	clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(clk)) {
+		if (PTR_ERR(clk) == -EPROBE_DEFER) {
+			ret = -EPROBE_DEFER;
+			goto err_out;
+		}
+		dev_notice(&pdev->dev, "Using default clock frequency\n");
+		uartclk = s->chip->freq_std;
+	} else
+		uartclk = clk_get_rate(clk);
+
+	/* Check input frequency */
+	if ((uartclk < s->chip->freq_min) || (uartclk > s->chip->freq_max)) {
+		dev_err(&pdev->dev, "Frequency out of bounds\n");
+		ret = -EINVAL;
 		goto err_out;
 		goto err_out;
 	}
 	}
 
 
-	if (!pdata) {
-		dev_warn(&pdev->dev,
-			 "No platform data supplied, using defaults\n");
-		s->pdata.frequency = s->freq_std;
-	} else
+	if (pdata)
 		memcpy(&s->pdata, pdata, sizeof(struct sccnxp_pdata));
 		memcpy(&s->pdata, pdata, sizeof(struct sccnxp_pdata));
 
 
 	if (s->pdata.poll_time_us) {
 	if (s->pdata.poll_time_us) {
@@ -911,34 +924,11 @@ static int sccnxp_probe(struct platform_device *pdev)
 		}
 		}
 	}
 	}
 
 
-	/* Check input frequency */
-	if ((s->pdata.frequency < freq_min) ||
-	    (s->pdata.frequency > freq_max)) {
-		dev_err(&pdev->dev, "Frequency out of bounds\n");
-		ret = -EINVAL;
-		goto err_out;
-	}
-
-	s->regulator = devm_regulator_get(&pdev->dev, "VCC");
-	if (!IS_ERR(s->regulator)) {
-		ret = regulator_enable(s->regulator);
-		if (ret) {
-			dev_err(&pdev->dev,
-				"Failed to enable regulator: %i\n", ret);
-			return ret;
-		}
-	}
-
-	membase = devm_ioremap_resource(&pdev->dev, res);
-	if (IS_ERR(membase)) {
-		ret = PTR_ERR(membase);
-		goto err_out;
-	}
-
 	s->uart.owner		= THIS_MODULE;
 	s->uart.owner		= THIS_MODULE;
 	s->uart.dev_name	= "ttySC";
 	s->uart.dev_name	= "ttySC";
 	s->uart.major		= SCCNXP_MAJOR;
 	s->uart.major		= SCCNXP_MAJOR;
 	s->uart.minor		= SCCNXP_MINOR;
 	s->uart.minor		= SCCNXP_MINOR;
+	s->uart.nr		= s->chip->nr;
 #ifdef CONFIG_SERIAL_SCCNXP_CONSOLE
 #ifdef CONFIG_SERIAL_SCCNXP_CONSOLE
 	s->uart.cons		= &s->console;
 	s->uart.cons		= &s->console;
 	s->uart.cons->device	= uart_console_device;
 	s->uart.cons->device	= uart_console_device;
@@ -960,17 +950,17 @@ static int sccnxp_probe(struct platform_device *pdev)
 		s->port[i].dev		= &pdev->dev;
 		s->port[i].dev		= &pdev->dev;
 		s->port[i].irq		= s->irq;
 		s->port[i].irq		= s->irq;
 		s->port[i].type		= PORT_SC26XX;
 		s->port[i].type		= PORT_SC26XX;
-		s->port[i].fifosize	= fifosize;
+		s->port[i].fifosize	= s->chip->fifosize;
 		s->port[i].flags	= UPF_SKIP_TEST | UPF_FIXED_TYPE;
 		s->port[i].flags	= UPF_SKIP_TEST | UPF_FIXED_TYPE;
 		s->port[i].iotype	= UPIO_MEM;
 		s->port[i].iotype	= UPIO_MEM;
 		s->port[i].mapbase	= res->start;
 		s->port[i].mapbase	= res->start;
 		s->port[i].membase	= membase;
 		s->port[i].membase	= membase;
 		s->port[i].regshift	= s->pdata.reg_shift;
 		s->port[i].regshift	= s->pdata.reg_shift;
-		s->port[i].uartclk	= s->pdata.frequency;
+		s->port[i].uartclk	= uartclk;
 		s->port[i].ops		= &sccnxp_ops;
 		s->port[i].ops		= &sccnxp_ops;
 		uart_add_one_port(&s->uart, &s->port[i]);
 		uart_add_one_port(&s->uart, &s->port[i]);
 		/* Set direction to input */
 		/* Set direction to input */
-		if (s->flags & SCCNXP_HAVE_IO)
+		if (s->chip->flags & SCCNXP_HAVE_IO)
 			sccnxp_set_bit(&s->port[i], DIR_OP, 0);
 			sccnxp_set_bit(&s->port[i], DIR_OP, 0);
 	}
 	}
 
 
@@ -997,7 +987,8 @@ static int sccnxp_probe(struct platform_device *pdev)
 	}
 	}
 
 
 err_out:
 err_out:
-	platform_set_drvdata(pdev, NULL);
+	if (!IS_ERR(s->regulator))
+		return regulator_disable(s->regulator);
 
 
 	return ret;
 	return ret;
 }
 }
@@ -1016,7 +1007,6 @@ static int sccnxp_remove(struct platform_device *pdev)
 		uart_remove_one_port(&s->uart, &s->port[i]);
 		uart_remove_one_port(&s->uart, &s->port[i]);
 
 
 	uart_unregister_driver(&s->uart);
 	uart_unregister_driver(&s->uart);
-	platform_set_drvdata(pdev, NULL);
 
 
 	if (!IS_ERR(s->regulator))
 	if (!IS_ERR(s->regulator))
 		return regulator_disable(s->regulator);
 		return regulator_disable(s->regulator);
@@ -1024,19 +1014,6 @@ static int sccnxp_remove(struct platform_device *pdev)
 	return 0;
 	return 0;
 }
 }
 
 
-static const struct platform_device_id sccnxp_id_table[] = {
-	{ "sc2681",	SCCNXP_TYPE_SC2681 },
-	{ "sc2691",	SCCNXP_TYPE_SC2691 },
-	{ "sc2692",	SCCNXP_TYPE_SC2692 },
-	{ "sc2891",	SCCNXP_TYPE_SC2891 },
-	{ "sc2892",	SCCNXP_TYPE_SC2892 },
-	{ "sc28202",	SCCNXP_TYPE_SC28202 },
-	{ "sc68681",	SCCNXP_TYPE_SC68681 },
-	{ "sc68692",	SCCNXP_TYPE_SC68692 },
-	{ },
-};
-MODULE_DEVICE_TABLE(platform, sccnxp_id_table);
-
 static struct platform_driver sccnxp_uart_driver = {
 static struct platform_driver sccnxp_uart_driver = {
 	.driver = {
 	.driver = {
 		.name	= SCCNXP_NAME,
 		.name	= SCCNXP_NAME,

+ 11 - 5
drivers/tty/serial/serial-tegra.c

@@ -571,7 +571,9 @@ static void tegra_uart_rx_dma_complete(void *args)
 
 
 	tegra_uart_handle_rx_pio(tup, port);
 	tegra_uart_handle_rx_pio(tup, port);
 	if (tty) {
 	if (tty) {
+		spin_unlock_irqrestore(&u->lock, flags);
 		tty_flip_buffer_push(port);
 		tty_flip_buffer_push(port);
+		spin_lock_irqsave(&u->lock, flags);
 		tty_kref_put(tty);
 		tty_kref_put(tty);
 	}
 	}
 	tegra_uart_start_rx_dma(tup);
 	tegra_uart_start_rx_dma(tup);
@@ -583,11 +585,13 @@ static void tegra_uart_rx_dma_complete(void *args)
 	spin_unlock_irqrestore(&u->lock, flags);
 	spin_unlock_irqrestore(&u->lock, flags);
 }
 }
 
 
-static void tegra_uart_handle_rx_dma(struct tegra_uart_port *tup)
+static void tegra_uart_handle_rx_dma(struct tegra_uart_port *tup,
+		unsigned long *flags)
 {
 {
 	struct dma_tx_state state;
 	struct dma_tx_state state;
 	struct tty_struct *tty = tty_port_tty_get(&tup->uport.state->port);
 	struct tty_struct *tty = tty_port_tty_get(&tup->uport.state->port);
 	struct tty_port *port = &tup->uport.state->port;
 	struct tty_port *port = &tup->uport.state->port;
+	struct uart_port *u = &tup->uport;
 	int count;
 	int count;
 
 
 	/* Deactivate flow control to stop sender */
 	/* Deactivate flow control to stop sender */
@@ -604,7 +608,9 @@ static void tegra_uart_handle_rx_dma(struct tegra_uart_port *tup)
 
 
 	tegra_uart_handle_rx_pio(tup, port);
 	tegra_uart_handle_rx_pio(tup, port);
 	if (tty) {
 	if (tty) {
+		spin_unlock_irqrestore(&u->lock, *flags);
 		tty_flip_buffer_push(port);
 		tty_flip_buffer_push(port);
+		spin_lock_irqsave(&u->lock, *flags);
 		tty_kref_put(tty);
 		tty_kref_put(tty);
 	}
 	}
 	tegra_uart_start_rx_dma(tup);
 	tegra_uart_start_rx_dma(tup);
@@ -671,7 +677,7 @@ static irqreturn_t tegra_uart_isr(int irq, void *data)
 		iir = tegra_uart_read(tup, UART_IIR);
 		iir = tegra_uart_read(tup, UART_IIR);
 		if (iir & UART_IIR_NO_INT) {
 		if (iir & UART_IIR_NO_INT) {
 			if (is_rx_int) {
 			if (is_rx_int) {
-				tegra_uart_handle_rx_dma(tup);
+				tegra_uart_handle_rx_dma(tup, &flags);
 				if (tup->rx_in_progress) {
 				if (tup->rx_in_progress) {
 					ier = tup->ier_shadow;
 					ier = tup->ier_shadow;
 					ier |= (UART_IER_RLSI | UART_IER_RTOIE |
 					ier |= (UART_IER_RLSI | UART_IER_RTOIE |
@@ -1206,7 +1212,7 @@ static struct uart_driver tegra_uart_driver = {
 	.owner		= THIS_MODULE,
 	.owner		= THIS_MODULE,
 	.driver_name	= "tegra_hsuart",
 	.driver_name	= "tegra_hsuart",
 	.dev_name	= "ttyTHS",
 	.dev_name	= "ttyTHS",
-	.cons		= 0,
+	.cons		= NULL,
 	.nr		= TEGRA_UART_MAXIMUM,
 	.nr		= TEGRA_UART_MAXIMUM,
 };
 };
 
 
@@ -1237,13 +1243,13 @@ static int tegra_uart_parse_dt(struct platform_device *pdev,
 	return 0;
 	return 0;
 }
 }
 
 
-struct tegra_uart_chip_data tegra20_uart_chip_data = {
+static struct tegra_uart_chip_data tegra20_uart_chip_data = {
 	.tx_fifo_full_status		= false,
 	.tx_fifo_full_status		= false,
 	.allow_txfifo_reset_fifo_mode	= true,
 	.allow_txfifo_reset_fifo_mode	= true,
 	.support_clk_src_div		= false,
 	.support_clk_src_div		= false,
 };
 };
 
 
-struct tegra_uart_chip_data tegra30_uart_chip_data = {
+static struct tegra_uart_chip_data tegra30_uart_chip_data = {
 	.tx_fifo_full_status		= true,
 	.tx_fifo_full_status		= true,
 	.allow_txfifo_reset_fifo_mode	= false,
 	.allow_txfifo_reset_fifo_mode	= false,
 	.support_clk_src_div		= true,
 	.support_clk_src_div		= true,

+ 2 - 2
drivers/tty/serial/serial_core.c

@@ -2095,12 +2095,12 @@ uart_report_port(struct uart_driver *drv, struct uart_port *port)
 		break;
 		break;
 	}
 	}
 
 
-	printk(KERN_INFO "%s%s%s%d at %s (irq = %d) is a %s\n",
+	printk(KERN_INFO "%s%s%s%d at %s (irq = %d, base_baud = %d) is a %s\n",
 	       port->dev ? dev_name(port->dev) : "",
 	       port->dev ? dev_name(port->dev) : "",
 	       port->dev ? ": " : "",
 	       port->dev ? ": " : "",
 	       drv->dev_name,
 	       drv->dev_name,
 	       drv->tty_driver->name_base + port->line,
 	       drv->tty_driver->name_base + port->line,
-	       address, port->irq, uart_type(port));
+	       address, port->irq, port->uartclk / 16, uart_type(port));
 }
 }
 
 
 static void
 static void

+ 1 - 1
drivers/tty/serial/serial_txx9.c

@@ -1097,7 +1097,7 @@ static void serial_txx9_unregister_port(int line)
  */
  */
 static int serial_txx9_probe(struct platform_device *dev)
 static int serial_txx9_probe(struct platform_device *dev)
 {
 {
-	struct uart_port *p = dev->dev.platform_data;
+	struct uart_port *p = dev_get_platdata(&dev->dev);
 	struct uart_port port;
 	struct uart_port port;
 	int ret, i;
 	int ret, i;
 
 

+ 2 - 2
drivers/tty/serial/sh-sci.c

@@ -2380,7 +2380,7 @@ static char early_serial_buf[32];
 
 
 static int sci_probe_earlyprintk(struct platform_device *pdev)
 static int sci_probe_earlyprintk(struct platform_device *pdev)
 {
 {
-	struct plat_sci_port *cfg = pdev->dev.platform_data;
+	struct plat_sci_port *cfg = dev_get_platdata(&pdev->dev);
 
 
 	if (early_serial_console.data)
 	if (early_serial_console.data)
 		return -EEXIST;
 		return -EEXIST;
@@ -2469,7 +2469,7 @@ static int sci_probe_single(struct platform_device *dev,
 
 
 static int sci_probe(struct platform_device *dev)
 static int sci_probe(struct platform_device *dev)
 {
 {
-	struct plat_sci_port *p = dev->dev.platform_data;
+	struct plat_sci_port *p = dev_get_platdata(&dev->dev);
 	struct sci_port *sp = &sci_ports[dev->id];
 	struct sci_port *sp = &sci_ports[dev->id];
 	int ret;
 	int ret;
 
 

+ 981 - 214
drivers/tty/serial/sirfsoc_uart.c

@@ -20,9 +20,13 @@
 #include <linux/of.h>
 #include <linux/of.h>
 #include <linux/slab.h>
 #include <linux/slab.h>
 #include <linux/io.h>
 #include <linux/io.h>
+#include <linux/of_gpio.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-direction.h>
+#include <linux/dma-mapping.h>
+#include <linux/sirfsoc_dma.h>
 #include <asm/irq.h>
 #include <asm/irq.h>
 #include <asm/mach/irq.h>
 #include <asm/mach/irq.h>
-#include <linux/pinctrl/consumer.h>
 
 
 #include "sirfsoc_uart.h"
 #include "sirfsoc_uart.h"
 
 
@@ -32,6 +36,9 @@ static unsigned int
 sirfsoc_uart_pio_rx_chars(struct uart_port *port, unsigned int max_rx_count);
 sirfsoc_uart_pio_rx_chars(struct uart_port *port, unsigned int max_rx_count);
 static struct uart_driver sirfsoc_uart_drv;
 static struct uart_driver sirfsoc_uart_drv;
 
 
+static void sirfsoc_uart_tx_dma_complete_callback(void *param);
+static void sirfsoc_uart_start_next_rx_dma(struct uart_port *port);
+static void sirfsoc_uart_rx_dma_complete_callback(void *param);
 static const struct sirfsoc_baudrate_to_regv baudrate_to_regv[] = {
 static const struct sirfsoc_baudrate_to_regv baudrate_to_regv[] = {
 	{4000000, 2359296},
 	{4000000, 2359296},
 	{3500000, 1310721},
 	{3500000, 1310721},
@@ -89,6 +96,13 @@ static struct sirfsoc_uart_port sirfsoc_uart_ports[SIRFSOC_UART_NR] = {
 			.line		= 4,
 			.line		= 4,
 		},
 		},
 	},
 	},
+	[5] = {
+		.port = {
+			.iotype		= UPIO_MEM,
+			.flags		= UPF_BOOT_AUTOCONF,
+			.line		= 5,
+		},
+	},
 };
 };
 
 
 static inline struct sirfsoc_uart_port *to_sirfport(struct uart_port *port)
 static inline struct sirfsoc_uart_port *to_sirfport(struct uart_port *port)
@@ -99,21 +113,28 @@ static inline struct sirfsoc_uart_port *to_sirfport(struct uart_port *port)
 static inline unsigned int sirfsoc_uart_tx_empty(struct uart_port *port)
 static inline unsigned int sirfsoc_uart_tx_empty(struct uart_port *port)
 {
 {
 	unsigned long reg;
 	unsigned long reg;
-	reg = rd_regl(port, SIRFUART_TX_FIFO_STATUS);
-	if (reg & SIRFUART_FIFOEMPTY_MASK(port))
-		return TIOCSER_TEMT;
-	else
-		return 0;
+	struct sirfsoc_uart_port *sirfport = to_sirfport(port);
+	struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg;
+	struct sirfsoc_fifo_status *ufifo_st = &sirfport->uart_reg->fifo_status;
+	reg = rd_regl(port, ureg->sirfsoc_tx_fifo_status);
+
+	return (reg & ufifo_st->ff_empty(port->line)) ? TIOCSER_TEMT : 0;
 }
 }
 
 
 static unsigned int sirfsoc_uart_get_mctrl(struct uart_port *port)
 static unsigned int sirfsoc_uart_get_mctrl(struct uart_port *port)
 {
 {
 	struct sirfsoc_uart_port *sirfport = to_sirfport(port);
 	struct sirfsoc_uart_port *sirfport = to_sirfport(port);
-	if (!(sirfport->ms_enabled)) {
+	struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg;
+	if (!sirfport->hw_flow_ctrl || !sirfport->ms_enabled)
 		goto cts_asserted;
 		goto cts_asserted;
-	} else if (sirfport->hw_flow_ctrl) {
-		if (!(rd_regl(port, SIRFUART_AFC_CTRL) &
-						SIRFUART_CTS_IN_STATUS))
+	if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) {
+		if (!(rd_regl(port, ureg->sirfsoc_afc_ctrl) &
+						SIRFUART_AFC_CTS_STATUS))
+			goto cts_asserted;
+		else
+			goto cts_deasserted;
+	} else {
+		if (!gpio_get_value(sirfport->cts_gpio))
 			goto cts_asserted;
 			goto cts_asserted;
 		else
 		else
 			goto cts_deasserted;
 			goto cts_deasserted;
@@ -127,89 +148,276 @@ cts_asserted:
 static void sirfsoc_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
 static void sirfsoc_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
 {
 {
 	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;
 	unsigned int assert = mctrl & TIOCM_RTS;
 	unsigned int assert = mctrl & TIOCM_RTS;
 	unsigned int val = assert ? SIRFUART_AFC_CTRL_RX_THD : 0x0;
 	unsigned int val = assert ? SIRFUART_AFC_CTRL_RX_THD : 0x0;
 	unsigned int current_val;
 	unsigned int current_val;
-	if (sirfport->hw_flow_ctrl) {
-		current_val = rd_regl(port, SIRFUART_AFC_CTRL) & ~0xFF;
+
+	if (!sirfport->hw_flow_ctrl || !sirfport->ms_enabled)
+		return;
+	if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) {
+		current_val = rd_regl(port, ureg->sirfsoc_afc_ctrl) & ~0xFF;
 		val |= current_val;
 		val |= current_val;
-		wr_regl(port, SIRFUART_AFC_CTRL, val);
+		wr_regl(port, ureg->sirfsoc_afc_ctrl, val);
+	} else {
+		if (!val)
+			gpio_set_value(sirfport->rts_gpio, 1);
+		else
+			gpio_set_value(sirfport->rts_gpio, 0);
 	}
 	}
 }
 }
 
 
 static void sirfsoc_uart_stop_tx(struct uart_port *port)
 static void sirfsoc_uart_stop_tx(struct uart_port *port)
 {
 {
-	unsigned int regv;
-	regv = rd_regl(port, SIRFUART_INT_EN);
-	wr_regl(port, SIRFUART_INT_EN, regv & ~SIRFUART_TX_INT_EN);
+	struct sirfsoc_uart_port *sirfport = to_sirfport(port);
+	struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg;
+	struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en;
+
+	if (IS_DMA_CHAN_VALID(sirfport->tx_dma_no)) {
+		if (sirfport->tx_dma_state == TX_DMA_RUNNING) {
+			dmaengine_pause(sirfport->tx_dma_chan);
+			sirfport->tx_dma_state = TX_DMA_PAUSE;
+		} else {
+			if (!sirfport->is_marco)
+				wr_regl(port, ureg->sirfsoc_int_en_reg,
+				rd_regl(port, ureg->sirfsoc_int_en_reg) &
+				~uint_en->sirfsoc_txfifo_empty_en);
+			else
+				wr_regl(port, SIRFUART_INT_EN_CLR,
+				uint_en->sirfsoc_txfifo_empty_en);
+		}
+	} else {
+		if (!sirfport->is_marco)
+			wr_regl(port, ureg->sirfsoc_int_en_reg,
+				rd_regl(port, ureg->sirfsoc_int_en_reg) &
+				~uint_en->sirfsoc_txfifo_empty_en);
+		else
+			wr_regl(port, SIRFUART_INT_EN_CLR,
+				uint_en->sirfsoc_txfifo_empty_en);
+	}
 }
 }
 
 
-void sirfsoc_uart_start_tx(struct uart_port *port)
+static void sirfsoc_uart_tx_with_dma(struct sirfsoc_uart_port *sirfport)
+{
+	struct uart_port *port = &sirfport->port;
+	struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg;
+	struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en;
+	struct circ_buf *xmit = &port->state->xmit;
+	unsigned long tran_size;
+	unsigned long tran_start;
+	unsigned long pio_tx_size;
+
+	tran_size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
+	tran_start = (unsigned long)(xmit->buf + xmit->tail);
+	if (uart_circ_empty(xmit) || uart_tx_stopped(port) ||
+			!tran_size)
+		return;
+	if (sirfport->tx_dma_state == TX_DMA_PAUSE) {
+		dmaengine_resume(sirfport->tx_dma_chan);
+		return;
+	}
+	if (sirfport->tx_dma_state == TX_DMA_RUNNING)
+		return;
+	if (!sirfport->is_marco)
+		wr_regl(port, ureg->sirfsoc_int_en_reg,
+				rd_regl(port, ureg->sirfsoc_int_en_reg)&
+				~(uint_en->sirfsoc_txfifo_empty_en));
+	else
+		wr_regl(port, SIRFUART_INT_EN_CLR,
+				uint_en->sirfsoc_txfifo_empty_en);
+	/*
+	 * DMA requires buffer address and buffer length are both aligned with
+	 * 4 bytes, so we use PIO for
+	 * 1. if address is not aligned with 4bytes, use PIO for the first 1~3
+	 * bytes, and move to DMA for the left part aligned with 4bytes
+	 * 2. if buffer length is not aligned with 4bytes, use DMA for aligned
+	 * part first, move to PIO for the left 1~3 bytes
+	 */
+	if (tran_size < 4 || BYTES_TO_ALIGN(tran_start)) {
+		wr_regl(port, ureg->sirfsoc_tx_fifo_op, SIRFUART_FIFO_STOP);
+		wr_regl(port, ureg->sirfsoc_tx_dma_io_ctrl,
+			rd_regl(port, ureg->sirfsoc_tx_dma_io_ctrl)|
+			SIRFUART_IO_MODE);
+		if (BYTES_TO_ALIGN(tran_start)) {
+			pio_tx_size = sirfsoc_uart_pio_tx_chars(sirfport,
+				BYTES_TO_ALIGN(tran_start));
+			tran_size -= pio_tx_size;
+		}
+		if (tran_size < 4)
+			sirfsoc_uart_pio_tx_chars(sirfport, tran_size);
+		if (!sirfport->is_marco)
+			wr_regl(port, ureg->sirfsoc_int_en_reg,
+				rd_regl(port, ureg->sirfsoc_int_en_reg)|
+				uint_en->sirfsoc_txfifo_empty_en);
+		else
+			wr_regl(port, ureg->sirfsoc_int_en_reg,
+				uint_en->sirfsoc_txfifo_empty_en);
+		wr_regl(port, ureg->sirfsoc_tx_fifo_op, SIRFUART_FIFO_START);
+	} else {
+		/* tx transfer mode switch into dma mode */
+		wr_regl(port, ureg->sirfsoc_tx_fifo_op, SIRFUART_FIFO_STOP);
+		wr_regl(port, ureg->sirfsoc_tx_dma_io_ctrl,
+			rd_regl(port, ureg->sirfsoc_tx_dma_io_ctrl)&
+			~SIRFUART_IO_MODE);
+		wr_regl(port, ureg->sirfsoc_tx_fifo_op, SIRFUART_FIFO_START);
+		tran_size &= ~(0x3);
+
+		sirfport->tx_dma_addr = dma_map_single(port->dev,
+			xmit->buf + xmit->tail,
+			tran_size, DMA_TO_DEVICE);
+		sirfport->tx_dma_desc = dmaengine_prep_slave_single(
+			sirfport->tx_dma_chan, sirfport->tx_dma_addr,
+			tran_size, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT);
+		if (!sirfport->tx_dma_desc) {
+			dev_err(port->dev, "DMA prep slave single fail\n");
+			return;
+		}
+		sirfport->tx_dma_desc->callback =
+			sirfsoc_uart_tx_dma_complete_callback;
+		sirfport->tx_dma_desc->callback_param = (void *)sirfport;
+		sirfport->transfer_size = tran_size;
+
+		dmaengine_submit(sirfport->tx_dma_desc);
+		dma_async_issue_pending(sirfport->tx_dma_chan);
+		sirfport->tx_dma_state = TX_DMA_RUNNING;
+	}
+}
+
+static void sirfsoc_uart_start_tx(struct uart_port *port)
 {
 {
 	struct sirfsoc_uart_port *sirfport = to_sirfport(port);
 	struct sirfsoc_uart_port *sirfport = to_sirfport(port);
-	unsigned long regv;
-	sirfsoc_uart_pio_tx_chars(sirfport, 1);
-	wr_regl(port, SIRFUART_TX_FIFO_OP, SIRFUART_TX_FIFO_START);
-	regv = rd_regl(port, SIRFUART_INT_EN);
-	wr_regl(port, SIRFUART_INT_EN, regv | SIRFUART_TX_INT_EN);
+	struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg;
+	struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en;
+	if (IS_DMA_CHAN_VALID(sirfport->tx_dma_no))
+		sirfsoc_uart_tx_with_dma(sirfport);
+	else {
+		sirfsoc_uart_pio_tx_chars(sirfport, 1);
+		wr_regl(port, ureg->sirfsoc_tx_fifo_op, SIRFUART_FIFO_START);
+		if (!sirfport->is_marco)
+			wr_regl(port, ureg->sirfsoc_int_en_reg,
+					rd_regl(port, ureg->sirfsoc_int_en_reg)|
+					uint_en->sirfsoc_txfifo_empty_en);
+		else
+			wr_regl(port, ureg->sirfsoc_int_en_reg,
+					uint_en->sirfsoc_txfifo_empty_en);
+	}
 }
 }
 
 
 static void sirfsoc_uart_stop_rx(struct uart_port *port)
 static void sirfsoc_uart_stop_rx(struct uart_port *port)
 {
 {
-	unsigned long regv;
-	wr_regl(port, SIRFUART_RX_FIFO_OP, 0);
-	regv = rd_regl(port, SIRFUART_INT_EN);
-	wr_regl(port, SIRFUART_INT_EN, regv & ~SIRFUART_RX_IO_INT_EN);
+	struct sirfsoc_uart_port *sirfport = to_sirfport(port);
+	struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg;
+	struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en;
+
+	wr_regl(port, ureg->sirfsoc_rx_fifo_op, 0);
+	if (IS_DMA_CHAN_VALID(sirfport->rx_dma_no)) {
+		if (!sirfport->is_marco)
+			wr_regl(port, ureg->sirfsoc_int_en_reg,
+				rd_regl(port, ureg->sirfsoc_int_en_reg) &
+				~(SIRFUART_RX_DMA_INT_EN(port, uint_en) |
+				uint_en->sirfsoc_rx_done_en));
+		else
+			wr_regl(port, SIRFUART_INT_EN_CLR,
+					SIRFUART_RX_DMA_INT_EN(port, uint_en)|
+					uint_en->sirfsoc_rx_done_en);
+		dmaengine_terminate_all(sirfport->rx_dma_chan);
+	} else {
+		if (!sirfport->is_marco)
+			wr_regl(port, ureg->sirfsoc_int_en_reg,
+				rd_regl(port, ureg->sirfsoc_int_en_reg)&
+				~(SIRFUART_RX_IO_INT_EN(port, uint_en)));
+		else
+			wr_regl(port, SIRFUART_INT_EN_CLR,
+					SIRFUART_RX_IO_INT_EN(port, uint_en));
+	}
 }
 }
 
 
 static void sirfsoc_uart_disable_ms(struct uart_port *port)
 static void sirfsoc_uart_disable_ms(struct uart_port *port)
 {
 {
 	struct sirfsoc_uart_port *sirfport = to_sirfport(port);
 	struct sirfsoc_uart_port *sirfport = to_sirfport(port);
-	unsigned long reg;
-	sirfport->ms_enabled = 0;
+	struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg;
+	struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en;
+
 	if (!sirfport->hw_flow_ctrl)
 	if (!sirfport->hw_flow_ctrl)
 		return;
 		return;
-	reg = rd_regl(port, SIRFUART_AFC_CTRL);
-	wr_regl(port, SIRFUART_AFC_CTRL, reg & ~0x3FF);
-	reg = rd_regl(port, SIRFUART_INT_EN);
-	wr_regl(port, SIRFUART_INT_EN, reg & ~SIRFUART_CTS_INT_EN);
+	sirfport->ms_enabled = false;
+	if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) {
+		wr_regl(port, ureg->sirfsoc_afc_ctrl,
+				rd_regl(port, ureg->sirfsoc_afc_ctrl) & ~0x3FF);
+		if (!sirfport->is_marco)
+			wr_regl(port, ureg->sirfsoc_int_en_reg,
+					rd_regl(port, ureg->sirfsoc_int_en_reg)&
+					~uint_en->sirfsoc_cts_en);
+		else
+			wr_regl(port, SIRFUART_INT_EN_CLR,
+					uint_en->sirfsoc_cts_en);
+	} else
+		disable_irq(gpio_to_irq(sirfport->cts_gpio));
+}
+
+static irqreturn_t sirfsoc_uart_usp_cts_handler(int irq, void *dev_id)
+{
+	struct sirfsoc_uart_port *sirfport = (struct sirfsoc_uart_port *)dev_id;
+	struct uart_port *port = &sirfport->port;
+	if (gpio_is_valid(sirfport->cts_gpio) && sirfport->ms_enabled)
+		uart_handle_cts_change(port,
+				!gpio_get_value(sirfport->cts_gpio));
+	return IRQ_HANDLED;
 }
 }
 
 
 static void sirfsoc_uart_enable_ms(struct uart_port *port)
 static void sirfsoc_uart_enable_ms(struct uart_port *port)
 {
 {
 	struct sirfsoc_uart_port *sirfport = to_sirfport(port);
 	struct sirfsoc_uart_port *sirfport = to_sirfport(port);
-	unsigned long reg;
-	unsigned long flg;
+	struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg;
+	struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en;
+
 	if (!sirfport->hw_flow_ctrl)
 	if (!sirfport->hw_flow_ctrl)
 		return;
 		return;
-	flg = SIRFUART_AFC_RX_EN | SIRFUART_AFC_TX_EN;
-	reg = rd_regl(port, SIRFUART_AFC_CTRL);
-	wr_regl(port, SIRFUART_AFC_CTRL, reg | flg);
-	reg = rd_regl(port, SIRFUART_INT_EN);
-	wr_regl(port, SIRFUART_INT_EN, reg | SIRFUART_CTS_INT_EN);
-	uart_handle_cts_change(port,
-		!(rd_regl(port, SIRFUART_AFC_CTRL) & SIRFUART_CTS_IN_STATUS));
-	sirfport->ms_enabled = 1;
+	sirfport->ms_enabled = true;
+	if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) {
+		wr_regl(port, ureg->sirfsoc_afc_ctrl,
+				rd_regl(port, ureg->sirfsoc_afc_ctrl) |
+				SIRFUART_AFC_TX_EN | SIRFUART_AFC_RX_EN);
+		if (!sirfport->is_marco)
+			wr_regl(port, ureg->sirfsoc_int_en_reg,
+					rd_regl(port, ureg->sirfsoc_int_en_reg)
+					| uint_en->sirfsoc_cts_en);
+		else
+			wr_regl(port, ureg->sirfsoc_int_en_reg,
+					uint_en->sirfsoc_cts_en);
+	} else
+		enable_irq(gpio_to_irq(sirfport->cts_gpio));
 }
 }
 
 
 static void sirfsoc_uart_break_ctl(struct uart_port *port, int break_state)
 static void sirfsoc_uart_break_ctl(struct uart_port *port, int break_state)
 {
 {
-	unsigned long ulcon = rd_regl(port, SIRFUART_LINE_CTRL);
-	if (break_state)
-		ulcon |= SIRFUART_SET_BREAK;
-	else
-		ulcon &= ~SIRFUART_SET_BREAK;
-	wr_regl(port, SIRFUART_LINE_CTRL, ulcon);
+	struct sirfsoc_uart_port *sirfport = to_sirfport(port);
+	struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg;
+	if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) {
+		unsigned long ulcon = rd_regl(port, ureg->sirfsoc_line_ctrl);
+		if (break_state)
+			ulcon |= SIRFUART_SET_BREAK;
+		else
+			ulcon &= ~SIRFUART_SET_BREAK;
+		wr_regl(port, ureg->sirfsoc_line_ctrl, ulcon);
+	}
 }
 }
 
 
 static unsigned int
 static unsigned int
 sirfsoc_uart_pio_rx_chars(struct uart_port *port, unsigned int max_rx_count)
 sirfsoc_uart_pio_rx_chars(struct uart_port *port, unsigned int max_rx_count)
 {
 {
+	struct sirfsoc_uart_port *sirfport = to_sirfport(port);
+	struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg;
+	struct sirfsoc_fifo_status *ufifo_st = &sirfport->uart_reg->fifo_status;
 	unsigned int ch, rx_count = 0;
 	unsigned int ch, rx_count = 0;
-
-	while (!(rd_regl(port, SIRFUART_RX_FIFO_STATUS) &
-					SIRFUART_FIFOEMPTY_MASK(port))) {
-		ch = rd_regl(port, SIRFUART_RX_FIFO_DATA) | SIRFUART_DUMMY_READ;
+	struct tty_struct *tty;
+	tty = tty_port_tty_get(&port->state->port);
+	if (!tty)
+		return -ENODEV;
+	while (!(rd_regl(port, ureg->sirfsoc_rx_fifo_status) &
+					ufifo_st->ff_empty(port->line))) {
+		ch = rd_regl(port, ureg->sirfsoc_rx_fifo_data) |
+			SIRFUART_DUMMY_READ;
 		if (unlikely(uart_handle_sysrq_char(port, ch)))
 		if (unlikely(uart_handle_sysrq_char(port, ch)))
 			continue;
 			continue;
 		uart_insert_char(port, 0, 0, ch, TTY_NORMAL);
 		uart_insert_char(port, 0, 0, ch, TTY_NORMAL);
@@ -218,8 +426,12 @@ sirfsoc_uart_pio_rx_chars(struct uart_port *port, unsigned int max_rx_count)
 			break;
 			break;
 	}
 	}
 
 
+	sirfport->rx_io_count += rx_count;
 	port->icount.rx += rx_count;
 	port->icount.rx += rx_count;
+
+	spin_unlock(&port->lock);
 	tty_flip_buffer_push(&port->state->port);
 	tty_flip_buffer_push(&port->state->port);
+	spin_lock(&port->lock);
 
 
 	return rx_count;
 	return rx_count;
 }
 }
@@ -228,13 +440,16 @@ static unsigned int
 sirfsoc_uart_pio_tx_chars(struct sirfsoc_uart_port *sirfport, int count)
 sirfsoc_uart_pio_tx_chars(struct sirfsoc_uart_port *sirfport, int count)
 {
 {
 	struct uart_port *port = &sirfport->port;
 	struct uart_port *port = &sirfport->port;
+	struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg;
+	struct sirfsoc_fifo_status *ufifo_st = &sirfport->uart_reg->fifo_status;
 	struct circ_buf *xmit = &port->state->xmit;
 	struct circ_buf *xmit = &port->state->xmit;
 	unsigned int num_tx = 0;
 	unsigned int num_tx = 0;
 	while (!uart_circ_empty(xmit) &&
 	while (!uart_circ_empty(xmit) &&
-		!(rd_regl(port, SIRFUART_TX_FIFO_STATUS) &
-					SIRFUART_FIFOFULL_MASK(port)) &&
+		!(rd_regl(port, ureg->sirfsoc_tx_fifo_status) &
+					ufifo_st->ff_full(port->line)) &&
 		count--) {
 		count--) {
-		wr_regl(port, SIRFUART_TX_FIFO_DATA, xmit->buf[xmit->tail]);
+		wr_regl(port, ureg->sirfsoc_tx_fifo_data,
+				xmit->buf[xmit->tail]);
 		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
 		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
 		port->icount.tx++;
 		port->icount.tx++;
 		num_tx++;
 		num_tx++;
@@ -244,6 +459,166 @@ sirfsoc_uart_pio_tx_chars(struct sirfsoc_uart_port *sirfport, int count)
 	return num_tx;
 	return num_tx;
 }
 }
 
 
+static void sirfsoc_uart_tx_dma_complete_callback(void *param)
+{
+	struct sirfsoc_uart_port *sirfport = (struct sirfsoc_uart_port *)param;
+	struct uart_port *port = &sirfport->port;
+	struct circ_buf *xmit = &port->state->xmit;
+	unsigned long flags;
+
+	xmit->tail = (xmit->tail + sirfport->transfer_size) &
+				(UART_XMIT_SIZE - 1);
+	port->icount.tx += sirfport->transfer_size;
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(port);
+	if (sirfport->tx_dma_addr)
+		dma_unmap_single(port->dev, sirfport->tx_dma_addr,
+				sirfport->transfer_size, DMA_TO_DEVICE);
+	spin_lock_irqsave(&sirfport->tx_lock, flags);
+	sirfport->tx_dma_state = TX_DMA_IDLE;
+	sirfsoc_uart_tx_with_dma(sirfport);
+	spin_unlock_irqrestore(&sirfport->tx_lock, flags);
+}
+
+static void sirfsoc_uart_insert_rx_buf_to_tty(
+		struct sirfsoc_uart_port *sirfport, int count)
+{
+	struct uart_port *port = &sirfport->port;
+	struct tty_port *tport = &port->state->port;
+	int inserted;
+
+	inserted = tty_insert_flip_string(tport,
+		sirfport->rx_dma_items[sirfport->rx_completed].xmit.buf, count);
+	port->icount.rx += inserted;
+	tty_flip_buffer_push(tport);
+}
+
+static void sirfsoc_rx_submit_one_dma_desc(struct uart_port *port, int index)
+{
+	struct sirfsoc_uart_port *sirfport = to_sirfport(port);
+
+	sirfport->rx_dma_items[index].xmit.tail =
+		sirfport->rx_dma_items[index].xmit.head = 0;
+	sirfport->rx_dma_items[index].desc =
+		dmaengine_prep_slave_single(sirfport->rx_dma_chan,
+		sirfport->rx_dma_items[index].dma_addr, SIRFSOC_RX_DMA_BUF_SIZE,
+		DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT);
+	if (!sirfport->rx_dma_items[index].desc) {
+		dev_err(port->dev, "DMA slave single fail\n");
+		return;
+	}
+	sirfport->rx_dma_items[index].desc->callback =
+		sirfsoc_uart_rx_dma_complete_callback;
+	sirfport->rx_dma_items[index].desc->callback_param = sirfport;
+	sirfport->rx_dma_items[index].cookie =
+		dmaengine_submit(sirfport->rx_dma_items[index].desc);
+	dma_async_issue_pending(sirfport->rx_dma_chan);
+}
+
+static void sirfsoc_rx_tmo_process_tl(unsigned long param)
+{
+	struct sirfsoc_uart_port *sirfport = (struct sirfsoc_uart_port *)param;
+	struct uart_port *port = &sirfport->port;
+	struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg;
+	struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en;
+	struct sirfsoc_int_status *uint_st = &sirfport->uart_reg->uart_int_st;
+	unsigned int count;
+	unsigned long flags;
+
+	spin_lock_irqsave(&sirfport->rx_lock, flags);
+	while (sirfport->rx_completed != sirfport->rx_issued) {
+		sirfsoc_uart_insert_rx_buf_to_tty(sirfport,
+					SIRFSOC_RX_DMA_BUF_SIZE);
+		sirfsoc_rx_submit_one_dma_desc(port, sirfport->rx_completed++);
+		sirfport->rx_completed %= SIRFSOC_RX_LOOP_BUF_CNT;
+	}
+	count = CIRC_CNT(sirfport->rx_dma_items[sirfport->rx_issued].xmit.head,
+		sirfport->rx_dma_items[sirfport->rx_issued].xmit.tail,
+		SIRFSOC_RX_DMA_BUF_SIZE);
+	if (count > 0)
+		sirfsoc_uart_insert_rx_buf_to_tty(sirfport, count);
+	wr_regl(port, ureg->sirfsoc_rx_dma_io_ctrl,
+			rd_regl(port, ureg->sirfsoc_rx_dma_io_ctrl) |
+			SIRFUART_IO_MODE);
+	sirfsoc_uart_pio_rx_chars(port, 4 - sirfport->rx_io_count);
+	spin_unlock_irqrestore(&sirfport->rx_lock, flags);
+	if (sirfport->rx_io_count == 4) {
+		spin_lock_irqsave(&sirfport->rx_lock, flags);
+		sirfport->rx_io_count = 0;
+		wr_regl(port, ureg->sirfsoc_int_st_reg,
+				uint_st->sirfsoc_rx_done);
+		if (!sirfport->is_marco)
+			wr_regl(port, ureg->sirfsoc_int_en_reg,
+				rd_regl(port, ureg->sirfsoc_int_en_reg) &
+				~(uint_en->sirfsoc_rx_done_en));
+		else
+			wr_regl(port, SIRFUART_INT_EN_CLR,
+					uint_en->sirfsoc_rx_done_en);
+		spin_unlock_irqrestore(&sirfport->rx_lock, flags);
+
+		sirfsoc_uart_start_next_rx_dma(port);
+	} else {
+		spin_lock_irqsave(&sirfport->rx_lock, flags);
+		wr_regl(port, ureg->sirfsoc_int_st_reg,
+				uint_st->sirfsoc_rx_done);
+		if (!sirfport->is_marco)
+			wr_regl(port, ureg->sirfsoc_int_en_reg,
+				rd_regl(port, ureg->sirfsoc_int_en_reg) |
+				(uint_en->sirfsoc_rx_done_en));
+		else
+			wr_regl(port, ureg->sirfsoc_int_en_reg,
+					uint_en->sirfsoc_rx_done_en);
+		spin_unlock_irqrestore(&sirfport->rx_lock, flags);
+	}
+}
+
+static void sirfsoc_uart_handle_rx_tmo(struct sirfsoc_uart_port *sirfport)
+{
+	struct uart_port *port = &sirfport->port;
+	struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg;
+	struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en;
+	struct dma_tx_state tx_state;
+	spin_lock(&sirfport->rx_lock);
+
+	dmaengine_tx_status(sirfport->rx_dma_chan,
+		sirfport->rx_dma_items[sirfport->rx_issued].cookie, &tx_state);
+	dmaengine_terminate_all(sirfport->rx_dma_chan);
+	sirfport->rx_dma_items[sirfport->rx_issued].xmit.head =
+		SIRFSOC_RX_DMA_BUF_SIZE - tx_state.residue;
+	if (!sirfport->is_marco)
+		wr_regl(port, ureg->sirfsoc_int_en_reg,
+			rd_regl(port, ureg->sirfsoc_int_en_reg) &
+			~(uint_en->sirfsoc_rx_timeout_en));
+	else
+		wr_regl(port, SIRFUART_INT_EN_CLR,
+				uint_en->sirfsoc_rx_timeout_en);
+	spin_unlock(&sirfport->rx_lock);
+	tasklet_schedule(&sirfport->rx_tmo_process_tasklet);
+}
+
+static void sirfsoc_uart_handle_rx_done(struct sirfsoc_uart_port *sirfport)
+{
+	struct uart_port *port = &sirfport->port;
+	struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg;
+	struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en;
+	struct sirfsoc_int_status *uint_st = &sirfport->uart_reg->uart_int_st;
+
+	sirfsoc_uart_pio_rx_chars(port, 4 - sirfport->rx_io_count);
+	if (sirfport->rx_io_count == 4) {
+		sirfport->rx_io_count = 0;
+		if (!sirfport->is_marco)
+			wr_regl(port, ureg->sirfsoc_int_en_reg,
+				rd_regl(port, ureg->sirfsoc_int_en_reg) &
+				~(uint_en->sirfsoc_rx_done_en));
+		else
+			wr_regl(port, SIRFUART_INT_EN_CLR,
+					uint_en->sirfsoc_rx_done_en);
+		wr_regl(port, ureg->sirfsoc_int_st_reg,
+				uint_st->sirfsoc_rx_timeout);
+		sirfsoc_uart_start_next_rx_dma(port);
+	}
+}
+
 static irqreturn_t sirfsoc_uart_isr(int irq, void *dev_id)
 static irqreturn_t sirfsoc_uart_isr(int irq, void *dev_id)
 {
 {
 	unsigned long intr_status;
 	unsigned long intr_status;
@@ -251,79 +626,191 @@ static irqreturn_t sirfsoc_uart_isr(int irq, void *dev_id)
 	unsigned long flag = TTY_NORMAL;
 	unsigned long flag = TTY_NORMAL;
 	struct sirfsoc_uart_port *sirfport = (struct sirfsoc_uart_port *)dev_id;
 	struct sirfsoc_uart_port *sirfport = (struct sirfsoc_uart_port *)dev_id;
 	struct uart_port *port = &sirfport->port;
 	struct uart_port *port = &sirfport->port;
+	struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg;
+	struct sirfsoc_fifo_status *ufifo_st = &sirfport->uart_reg->fifo_status;
+	struct sirfsoc_int_status *uint_st = &sirfport->uart_reg->uart_int_st;
+	struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en;
 	struct uart_state *state = port->state;
 	struct uart_state *state = port->state;
 	struct circ_buf *xmit = &port->state->xmit;
 	struct circ_buf *xmit = &port->state->xmit;
 	spin_lock(&port->lock);
 	spin_lock(&port->lock);
-	intr_status = rd_regl(port, SIRFUART_INT_STATUS);
-	wr_regl(port, SIRFUART_INT_STATUS, intr_status);
-	intr_status &= rd_regl(port, SIRFUART_INT_EN);
-	if (unlikely(intr_status & (SIRFUART_ERR_INT_STAT))) {
-		if (intr_status & SIRFUART_RXD_BREAK) {
+	intr_status = rd_regl(port, ureg->sirfsoc_int_st_reg);
+	wr_regl(port, ureg->sirfsoc_int_st_reg, intr_status);
+	intr_status &= rd_regl(port, ureg->sirfsoc_int_en_reg);
+	if (unlikely(intr_status & (SIRFUART_ERR_INT_STAT(port, uint_st)))) {
+		if (intr_status & uint_st->sirfsoc_rxd_brk) {
+			port->icount.brk++;
 			if (uart_handle_break(port))
 			if (uart_handle_break(port))
 				goto recv_char;
 				goto recv_char;
-			uart_insert_char(port, intr_status,
-					SIRFUART_RX_OFLOW, 0, TTY_BREAK);
-			spin_unlock(&port->lock);
-			return IRQ_HANDLED;
 		}
 		}
-		if (intr_status & SIRFUART_RX_OFLOW)
+		if (intr_status & uint_st->sirfsoc_rx_oflow)
 			port->icount.overrun++;
 			port->icount.overrun++;
-		if (intr_status & SIRFUART_FRM_ERR) {
+		if (intr_status & uint_st->sirfsoc_frm_err) {
 			port->icount.frame++;
 			port->icount.frame++;
 			flag = TTY_FRAME;
 			flag = TTY_FRAME;
 		}
 		}
-		if (intr_status & SIRFUART_PARITY_ERR)
+		if (intr_status & uint_st->sirfsoc_parity_err)
 			flag = TTY_PARITY;
 			flag = TTY_PARITY;
-		wr_regl(port, SIRFUART_RX_FIFO_OP, SIRFUART_RX_FIFO_RESET);
-		wr_regl(port, SIRFUART_RX_FIFO_OP, 0);
-		wr_regl(port, SIRFUART_RX_FIFO_OP, SIRFUART_RX_FIFO_START);
+		wr_regl(port, ureg->sirfsoc_rx_fifo_op, SIRFUART_FIFO_RESET);
+		wr_regl(port, ureg->sirfsoc_rx_fifo_op, 0);
+		wr_regl(port, ureg->sirfsoc_rx_fifo_op, SIRFUART_FIFO_START);
 		intr_status &= port->read_status_mask;
 		intr_status &= port->read_status_mask;
 		uart_insert_char(port, intr_status,
 		uart_insert_char(port, intr_status,
-					SIRFUART_RX_OFLOW_INT, 0, flag);
+					uint_en->sirfsoc_rx_oflow_en, 0, flag);
+		tty_flip_buffer_push(&state->port);
 	}
 	}
 recv_char:
 recv_char:
-	if (intr_status & SIRFUART_CTS_INT_EN) {
-		cts_status = !(rd_regl(port, SIRFUART_AFC_CTRL) &
-							SIRFUART_CTS_IN_STATUS);
-		if (cts_status != 0) {
-			uart_handle_cts_change(port, 1);
-		} else {
-			uart_handle_cts_change(port, 0);
-			wake_up_interruptible(&state->port.delta_msr_wait);
-		}
+	if ((sirfport->uart_reg->uart_type == SIRF_REAL_UART) &&
+			(intr_status & SIRFUART_CTS_INT_ST(uint_st)) &&
+			!sirfport->tx_dma_state) {
+		cts_status = rd_regl(port, ureg->sirfsoc_afc_ctrl) &
+					SIRFUART_AFC_CTS_STATUS;
+		if (cts_status != 0)
+			cts_status = 0;
+		else
+			cts_status = 1;
+		uart_handle_cts_change(port, cts_status);
+		wake_up_interruptible(&state->port.delta_msr_wait);
 	}
 	}
-	if (intr_status & SIRFUART_RX_IO_INT_EN)
-		sirfsoc_uart_pio_rx_chars(port, SIRFSOC_UART_IO_RX_MAX_CNT);
-	if (intr_status & SIRFUART_TX_INT_EN) {
-		if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
-			spin_unlock(&port->lock);
-			return IRQ_HANDLED;
-		} else {
-			sirfsoc_uart_pio_tx_chars(sirfport,
+	if (IS_DMA_CHAN_VALID(sirfport->rx_dma_no)) {
+		if (intr_status & uint_st->sirfsoc_rx_timeout)
+			sirfsoc_uart_handle_rx_tmo(sirfport);
+		if (intr_status & uint_st->sirfsoc_rx_done)
+			sirfsoc_uart_handle_rx_done(sirfport);
+	} else {
+		if (intr_status & SIRFUART_RX_IO_INT_ST(uint_st))
+			sirfsoc_uart_pio_rx_chars(port,
+					SIRFSOC_UART_IO_RX_MAX_CNT);
+	}
+	if (intr_status & uint_st->sirfsoc_txfifo_empty) {
+		if (IS_DMA_CHAN_VALID(sirfport->tx_dma_no))
+			sirfsoc_uart_tx_with_dma(sirfport);
+		else {
+			if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
+				spin_unlock(&port->lock);
+				return IRQ_HANDLED;
+			} else {
+				sirfsoc_uart_pio_tx_chars(sirfport,
 					SIRFSOC_UART_IO_TX_REASONABLE_CNT);
 					SIRFSOC_UART_IO_TX_REASONABLE_CNT);
-			if ((uart_circ_empty(xmit)) &&
-				(rd_regl(port, SIRFUART_TX_FIFO_STATUS) &
-						SIRFUART_FIFOEMPTY_MASK(port)))
-				sirfsoc_uart_stop_tx(port);
+				if ((uart_circ_empty(xmit)) &&
+				(rd_regl(port, ureg->sirfsoc_tx_fifo_status) &
+				ufifo_st->ff_empty(port->line)))
+					sirfsoc_uart_stop_tx(port);
+			}
 		}
 		}
 	}
 	}
 	spin_unlock(&port->lock);
 	spin_unlock(&port->lock);
 	return IRQ_HANDLED;
 	return IRQ_HANDLED;
 }
 }
 
 
+static void sirfsoc_uart_rx_dma_complete_tl(unsigned long param)
+{
+	struct sirfsoc_uart_port *sirfport = (struct sirfsoc_uart_port *)param;
+	struct uart_port *port = &sirfport->port;
+	unsigned long flags;
+	spin_lock_irqsave(&sirfport->rx_lock, flags);
+	while (sirfport->rx_completed != sirfport->rx_issued) {
+		sirfsoc_uart_insert_rx_buf_to_tty(sirfport,
+					SIRFSOC_RX_DMA_BUF_SIZE);
+		sirfsoc_rx_submit_one_dma_desc(port, sirfport->rx_completed++);
+		sirfport->rx_completed %= SIRFSOC_RX_LOOP_BUF_CNT;
+	}
+	spin_unlock_irqrestore(&sirfport->rx_lock, flags);
+}
+
+static void sirfsoc_uart_rx_dma_complete_callback(void *param)
+{
+	struct sirfsoc_uart_port *sirfport = (struct sirfsoc_uart_port *)param;
+	spin_lock(&sirfport->rx_lock);
+	sirfport->rx_issued++;
+	sirfport->rx_issued %= SIRFSOC_RX_LOOP_BUF_CNT;
+	spin_unlock(&sirfport->rx_lock);
+	tasklet_schedule(&sirfport->rx_dma_complete_tasklet);
+}
+
+/* submit rx dma task into dmaengine */
+static void sirfsoc_uart_start_next_rx_dma(struct uart_port *port)
+{
+	struct sirfsoc_uart_port *sirfport = to_sirfport(port);
+	struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg;
+	struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en;
+	unsigned long flags;
+	int i;
+	spin_lock_irqsave(&sirfport->rx_lock, flags);
+	sirfport->rx_io_count = 0;
+	wr_regl(port, ureg->sirfsoc_rx_dma_io_ctrl,
+		rd_regl(port, ureg->sirfsoc_rx_dma_io_ctrl) &
+		~SIRFUART_IO_MODE);
+	spin_unlock_irqrestore(&sirfport->rx_lock, flags);
+	for (i = 0; i < SIRFSOC_RX_LOOP_BUF_CNT; i++)
+		sirfsoc_rx_submit_one_dma_desc(port, i);
+	sirfport->rx_completed = sirfport->rx_issued = 0;
+	spin_lock_irqsave(&sirfport->rx_lock, flags);
+	if (!sirfport->is_marco)
+		wr_regl(port, ureg->sirfsoc_int_en_reg,
+				rd_regl(port, ureg->sirfsoc_int_en_reg) |
+				SIRFUART_RX_DMA_INT_EN(port, uint_en));
+	else
+		wr_regl(port, ureg->sirfsoc_int_en_reg,
+			SIRFUART_RX_DMA_INT_EN(port, uint_en));
+	spin_unlock_irqrestore(&sirfport->rx_lock, flags);
+}
+
 static void sirfsoc_uart_start_rx(struct uart_port *port)
 static void sirfsoc_uart_start_rx(struct uart_port *port)
 {
 {
-	unsigned long regv;
-	regv = rd_regl(port, SIRFUART_INT_EN);
-	wr_regl(port, SIRFUART_INT_EN, regv | SIRFUART_RX_IO_INT_EN);
-	wr_regl(port, SIRFUART_RX_FIFO_OP, SIRFUART_RX_FIFO_RESET);
-	wr_regl(port, SIRFUART_RX_FIFO_OP, 0);
-	wr_regl(port, SIRFUART_RX_FIFO_OP, SIRFUART_RX_FIFO_START);
+	struct sirfsoc_uart_port *sirfport = to_sirfport(port);
+	struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg;
+	struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en;
+
+	sirfport->rx_io_count = 0;
+	wr_regl(port, ureg->sirfsoc_rx_fifo_op, SIRFUART_FIFO_RESET);
+	wr_regl(port, ureg->sirfsoc_rx_fifo_op, 0);
+	wr_regl(port, ureg->sirfsoc_rx_fifo_op, SIRFUART_FIFO_START);
+	if (IS_DMA_CHAN_VALID(sirfport->rx_dma_no))
+		sirfsoc_uart_start_next_rx_dma(port);
+	else {
+		if (!sirfport->is_marco)
+			wr_regl(port, ureg->sirfsoc_int_en_reg,
+				rd_regl(port, ureg->sirfsoc_int_en_reg) |
+				SIRFUART_RX_IO_INT_EN(port, uint_en));
+		else
+			wr_regl(port, ureg->sirfsoc_int_en_reg,
+				SIRFUART_RX_IO_INT_EN(port, uint_en));
+	}
+}
+
+static unsigned int
+sirfsoc_usp_calc_sample_div(unsigned long set_rate,
+		unsigned long ioclk_rate, unsigned long *sample_reg)
+{
+	unsigned long min_delta = ~0UL;
+	unsigned short sample_div;
+	unsigned long ioclk_div = 0;
+	unsigned long temp_delta;
+
+	for (sample_div = SIRF_MIN_SAMPLE_DIV;
+			sample_div <= SIRF_MAX_SAMPLE_DIV; sample_div++) {
+		temp_delta = ioclk_rate -
+		(ioclk_rate + (set_rate * sample_div) / 2)
+		/ (set_rate * sample_div) * set_rate * sample_div;
+
+		temp_delta = (temp_delta > 0) ? temp_delta : -temp_delta;
+		if (temp_delta < min_delta) {
+			ioclk_div = (2 * ioclk_rate /
+				(set_rate * sample_div) + 1) / 2 - 1;
+			if (ioclk_div > SIRF_IOCLK_DIV_MAX)
+				continue;
+			min_delta = temp_delta;
+			*sample_reg = sample_div;
+			if (!temp_delta)
+				break;
+		}
+	}
+	return ioclk_div;
 }
 }
 
 
 static unsigned int
 static unsigned int
-sirfsoc_calc_sample_div(unsigned long baud_rate,
-			unsigned long ioclk_rate, unsigned long *setted_baud)
+sirfsoc_uart_calc_sample_div(unsigned long baud_rate,
+			unsigned long ioclk_rate, unsigned long *set_baud)
 {
 {
 	unsigned long min_delta = ~0UL;
 	unsigned long min_delta = ~0UL;
 	unsigned short sample_div;
 	unsigned short sample_div;
@@ -346,7 +833,7 @@ sirfsoc_calc_sample_div(unsigned long baud_rate,
 			regv = regv & (~SIRF_SAMPLE_DIV_MASK);
 			regv = regv & (~SIRF_SAMPLE_DIV_MASK);
 			regv = regv | (sample_div << SIRF_SAMPLE_DIV_SHIFT);
 			regv = regv | (sample_div << SIRF_SAMPLE_DIV_SHIFT);
 			min_delta = temp_delta;
 			min_delta = temp_delta;
-			*setted_baud = baud_tmp;
+			*set_baud = baud_tmp;
 		}
 		}
 	}
 	}
 	return regv;
 	return regv;
@@ -357,63 +844,93 @@ static void sirfsoc_uart_set_termios(struct uart_port *port,
 				       struct ktermios *old)
 				       struct ktermios *old)
 {
 {
 	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_int_en *uint_en = &sirfport->uart_reg->uart_int_en;
 	unsigned long	config_reg = 0;
 	unsigned long	config_reg = 0;
 	unsigned long	baud_rate;
 	unsigned long	baud_rate;
-	unsigned long	setted_baud;
+	unsigned long	set_baud;
 	unsigned long	flags;
 	unsigned long	flags;
 	unsigned long	ic;
 	unsigned long	ic;
 	unsigned int	clk_div_reg = 0;
 	unsigned int	clk_div_reg = 0;
-	unsigned long	temp_reg_val;
+	unsigned long	txfifo_op_reg, ioclk_rate;
 	unsigned long	rx_time_out;
 	unsigned long	rx_time_out;
 	int		threshold_div;
 	int		threshold_div;
-	int		temp;
+	u32		data_bit_len, stop_bit_len, len_val;
+	unsigned long	sample_div_reg = 0xf;
+	ioclk_rate	= port->uartclk;
 
 
 	switch (termios->c_cflag & CSIZE) {
 	switch (termios->c_cflag & CSIZE) {
 	default:
 	default:
 	case CS8:
 	case CS8:
+		data_bit_len = 8;
 		config_reg |= SIRFUART_DATA_BIT_LEN_8;
 		config_reg |= SIRFUART_DATA_BIT_LEN_8;
 		break;
 		break;
 	case CS7:
 	case CS7:
+		data_bit_len = 7;
 		config_reg |= SIRFUART_DATA_BIT_LEN_7;
 		config_reg |= SIRFUART_DATA_BIT_LEN_7;
 		break;
 		break;
 	case CS6:
 	case CS6:
+		data_bit_len = 6;
 		config_reg |= SIRFUART_DATA_BIT_LEN_6;
 		config_reg |= SIRFUART_DATA_BIT_LEN_6;
 		break;
 		break;
 	case CS5:
 	case CS5:
+		data_bit_len = 5;
 		config_reg |= SIRFUART_DATA_BIT_LEN_5;
 		config_reg |= SIRFUART_DATA_BIT_LEN_5;
 		break;
 		break;
 	}
 	}
-	if (termios->c_cflag & CSTOPB)
+	if (termios->c_cflag & CSTOPB) {
 		config_reg |= SIRFUART_STOP_BIT_LEN_2;
 		config_reg |= SIRFUART_STOP_BIT_LEN_2;
-	baud_rate = uart_get_baud_rate(port, termios, old, 0, 4000000);
+		stop_bit_len = 2;
+	} else
+		stop_bit_len = 1;
+
 	spin_lock_irqsave(&port->lock, flags);
 	spin_lock_irqsave(&port->lock, flags);
-	port->read_status_mask = SIRFUART_RX_OFLOW_INT;
+	port->read_status_mask = uint_en->sirfsoc_rx_oflow_en;
 	port->ignore_status_mask = 0;
 	port->ignore_status_mask = 0;
-	/* read flags */
-	if (termios->c_iflag & INPCK)
-		port->read_status_mask |=
-			SIRFUART_FRM_ERR_INT | SIRFUART_PARITY_ERR_INT;
+	if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) {
+		if (termios->c_iflag & INPCK)
+			port->read_status_mask |= uint_en->sirfsoc_frm_err_en |
+				uint_en->sirfsoc_parity_err_en;
+	} else {
+		if (termios->c_iflag & INPCK)
+			port->read_status_mask |= uint_en->sirfsoc_frm_err_en;
+	}
 	if (termios->c_iflag & (BRKINT | PARMRK))
 	if (termios->c_iflag & (BRKINT | PARMRK))
-		port->read_status_mask |= SIRFUART_RXD_BREAK_INT;
-	/* ignore flags */
-	if (termios->c_iflag & IGNPAR)
+			port->read_status_mask |= uint_en->sirfsoc_rxd_brk_en;
+	if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) {
+		if (termios->c_iflag & IGNPAR)
+			port->ignore_status_mask |=
+				uint_en->sirfsoc_frm_err_en |
+				uint_en->sirfsoc_parity_err_en;
+		if (termios->c_cflag & PARENB) {
+			if (termios->c_cflag & CMSPAR) {
+				if (termios->c_cflag & PARODD)
+					config_reg |= SIRFUART_STICK_BIT_MARK;
+				else
+					config_reg |= SIRFUART_STICK_BIT_SPACE;
+			} else if (termios->c_cflag & PARODD) {
+				config_reg |= SIRFUART_STICK_BIT_ODD;
+			} else {
+				config_reg |= SIRFUART_STICK_BIT_EVEN;
+			}
+		}
+	} else {
+		if (termios->c_iflag & IGNPAR)
+			port->ignore_status_mask |=
+				uint_en->sirfsoc_frm_err_en;
+		if (termios->c_cflag & PARENB)
+			dev_warn(port->dev,
+					"USP-UART not support parity err\n");
+	}
+	if (termios->c_iflag & IGNBRK) {
 		port->ignore_status_mask |=
 		port->ignore_status_mask |=
-			SIRFUART_FRM_ERR_INT | SIRFUART_PARITY_ERR_INT;
+			uint_en->sirfsoc_rxd_brk_en;
+		if (termios->c_iflag & IGNPAR)
+			port->ignore_status_mask |=
+				uint_en->sirfsoc_rx_oflow_en;
+	}
 	if ((termios->c_cflag & CREAD) == 0)
 	if ((termios->c_cflag & CREAD) == 0)
 		port->ignore_status_mask |= SIRFUART_DUMMY_READ;
 		port->ignore_status_mask |= SIRFUART_DUMMY_READ;
-	/* enable parity if PARENB is set*/
-	if (termios->c_cflag & PARENB) {
-		if (termios->c_cflag & CMSPAR) {
-			if (termios->c_cflag & PARODD)
-				config_reg |= SIRFUART_STICK_BIT_MARK;
-			else
-				config_reg |= SIRFUART_STICK_BIT_SPACE;
-		} else if (termios->c_cflag & PARODD) {
-			config_reg |= SIRFUART_STICK_BIT_ODD;
-		} else {
-			config_reg |= SIRFUART_STICK_BIT_EVEN;
-		}
-	}
 	/* Hardware Flow Control Settings */
 	/* Hardware Flow Control Settings */
 	if (UART_ENABLE_MS(port, termios->c_cflag)) {
 	if (UART_ENABLE_MS(port, termios->c_cflag)) {
 		if (!sirfport->ms_enabled)
 		if (!sirfport->ms_enabled)
@@ -422,75 +939,184 @@ static void sirfsoc_uart_set_termios(struct uart_port *port,
 		if (sirfport->ms_enabled)
 		if (sirfport->ms_enabled)
 			sirfsoc_uart_disable_ms(port);
 			sirfsoc_uart_disable_ms(port);
 	}
 	}
-
-	if (port->uartclk == 150000000) {
-		/* common rate: fast calculation */
+	baud_rate = uart_get_baud_rate(port, termios, old, 0, 4000000);
+	if (ioclk_rate == 150000000) {
 		for (ic = 0; ic < SIRF_BAUD_RATE_SUPPORT_NR; ic++)
 		for (ic = 0; ic < SIRF_BAUD_RATE_SUPPORT_NR; ic++)
 			if (baud_rate == baudrate_to_regv[ic].baud_rate)
 			if (baud_rate == baudrate_to_regv[ic].baud_rate)
 				clk_div_reg = baudrate_to_regv[ic].reg_val;
 				clk_div_reg = baudrate_to_regv[ic].reg_val;
 	}
 	}
-
-	setted_baud = baud_rate;
-	/* arbitary rate setting */
-	if (unlikely(clk_div_reg == 0))
-		clk_div_reg = sirfsoc_calc_sample_div(baud_rate, port->uartclk,
-								&setted_baud);
-	wr_regl(port, SIRFUART_DIVISOR, clk_div_reg);
-
+	set_baud = baud_rate;
+	if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) {
+		if (unlikely(clk_div_reg == 0))
+			clk_div_reg = sirfsoc_uart_calc_sample_div(baud_rate,
+					ioclk_rate, &set_baud);
+		wr_regl(port, ureg->sirfsoc_divisor, clk_div_reg);
+	} else {
+		clk_div_reg = sirfsoc_usp_calc_sample_div(baud_rate,
+				ioclk_rate, &sample_div_reg);
+		sample_div_reg--;
+		set_baud = ((ioclk_rate / (clk_div_reg+1) - 1) /
+				(sample_div_reg + 1));
+		/* setting usp mode 2 */
+		len_val = ((1 << SIRFSOC_USP_MODE2_RXD_DELAY_OFFSET) |
+				(1 << SIRFSOC_USP_MODE2_TXD_DELAY_OFFSET));
+		len_val |= ((clk_div_reg & SIRFSOC_USP_MODE2_CLK_DIVISOR_MASK)
+				<< SIRFSOC_USP_MODE2_CLK_DIVISOR_OFFSET);
+		wr_regl(port, ureg->sirfsoc_mode2, len_val);
+	}
 	if (tty_termios_baud_rate(termios))
 	if (tty_termios_baud_rate(termios))
-		tty_termios_encode_baud_rate(termios, setted_baud, setted_baud);
-
-	/* set receive timeout */
-	rx_time_out = SIRFSOC_UART_RX_TIMEOUT(baud_rate, 20000);
-	rx_time_out = (rx_time_out > 0xFFFF) ? 0xFFFF : rx_time_out;
-	config_reg |= SIRFUART_RECV_TIMEOUT(rx_time_out);
-	temp_reg_val = rd_regl(port, SIRFUART_TX_FIFO_OP);
-	wr_regl(port, SIRFUART_RX_FIFO_OP, 0);
-	wr_regl(port, SIRFUART_TX_FIFO_OP,
-				temp_reg_val & ~SIRFUART_TX_FIFO_START);
-	wr_regl(port, SIRFUART_TX_DMA_IO_CTRL, SIRFUART_TX_MODE_IO);
-	wr_regl(port, SIRFUART_RX_DMA_IO_CTRL, SIRFUART_RX_MODE_IO);
-	wr_regl(port, SIRFUART_LINE_CTRL, config_reg);
-
+		tty_termios_encode_baud_rate(termios, set_baud, set_baud);
+	/* set receive timeout && data bits len */
+	rx_time_out = SIRFSOC_UART_RX_TIMEOUT(set_baud, 20000);
+	rx_time_out = SIRFUART_RECV_TIMEOUT_VALUE(rx_time_out);
+	txfifo_op_reg = rd_regl(port, ureg->sirfsoc_tx_fifo_op);
+	wr_regl(port, ureg->sirfsoc_rx_fifo_op, SIRFUART_FIFO_STOP);
+	wr_regl(port, ureg->sirfsoc_tx_fifo_op,
+			(txfifo_op_reg & ~SIRFUART_FIFO_START));
+	if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) {
+		config_reg |= SIRFUART_RECV_TIMEOUT(port, rx_time_out);
+		wr_regl(port, ureg->sirfsoc_line_ctrl, config_reg);
+	} else {
+		/*tx frame ctrl*/
+		len_val = (data_bit_len - 1) << SIRFSOC_USP_TX_DATA_LEN_OFFSET;
+		len_val |= (data_bit_len + 1 + stop_bit_len - 1) <<
+				SIRFSOC_USP_TX_FRAME_LEN_OFFSET;
+		len_val |= ((data_bit_len - 1) <<
+				SIRFSOC_USP_TX_SHIFTER_LEN_OFFSET);
+		len_val |= (((clk_div_reg & 0xc00) >> 10) <<
+				SIRFSOC_USP_TX_CLK_DIVISOR_OFFSET);
+		wr_regl(port, ureg->sirfsoc_tx_frame_ctrl, len_val);
+		/*rx frame ctrl*/
+		len_val = (data_bit_len - 1) << SIRFSOC_USP_RX_DATA_LEN_OFFSET;
+		len_val |= (data_bit_len + 1 + stop_bit_len - 1) <<
+				SIRFSOC_USP_RX_FRAME_LEN_OFFSET;
+		len_val |= (data_bit_len - 1) <<
+				SIRFSOC_USP_RX_SHIFTER_LEN_OFFSET;
+		len_val |= (((clk_div_reg & 0xf000) >> 12) <<
+				SIRFSOC_USP_RX_CLK_DIVISOR_OFFSET);
+		wr_regl(port, ureg->sirfsoc_rx_frame_ctrl, len_val);
+		/*async param*/
+		wr_regl(port, ureg->sirfsoc_async_param_reg,
+			(SIRFUART_RECV_TIMEOUT(port, rx_time_out)) |
+			(sample_div_reg & SIRFSOC_USP_ASYNC_DIV2_MASK) <<
+			SIRFSOC_USP_ASYNC_DIV2_OFFSET);
+	}
+	if (IS_DMA_CHAN_VALID(sirfport->tx_dma_no))
+		wr_regl(port, ureg->sirfsoc_tx_dma_io_ctrl, SIRFUART_DMA_MODE);
+	else
+		wr_regl(port, ureg->sirfsoc_tx_dma_io_ctrl, SIRFUART_IO_MODE);
+	if (IS_DMA_CHAN_VALID(sirfport->rx_dma_no))
+		wr_regl(port, ureg->sirfsoc_rx_dma_io_ctrl, SIRFUART_DMA_MODE);
+	else
+		wr_regl(port, ureg->sirfsoc_rx_dma_io_ctrl, SIRFUART_IO_MODE);
 	/* Reset Rx/Tx FIFO Threshold level for proper baudrate */
 	/* Reset Rx/Tx FIFO Threshold level for proper baudrate */
-	if (baud_rate < 1000000)
+	if (set_baud < 1000000)
 		threshold_div = 1;
 		threshold_div = 1;
 	else
 	else
 		threshold_div = 2;
 		threshold_div = 2;
-	temp = port->line == 1 ? 16 : 64;
-	wr_regl(port, SIRFUART_TX_FIFO_CTRL, temp / threshold_div);
-	wr_regl(port, SIRFUART_RX_FIFO_CTRL, temp / threshold_div);
-	temp_reg_val |= SIRFUART_TX_FIFO_START;
-	wr_regl(port, SIRFUART_TX_FIFO_OP, temp_reg_val);
-	uart_update_timeout(port, termios->c_cflag, baud_rate);
+	wr_regl(port, ureg->sirfsoc_tx_fifo_ctrl,
+				SIRFUART_FIFO_THD(port) / threshold_div);
+	wr_regl(port, ureg->sirfsoc_rx_fifo_ctrl,
+				SIRFUART_FIFO_THD(port) / threshold_div);
+	txfifo_op_reg |= SIRFUART_FIFO_START;
+	wr_regl(port, ureg->sirfsoc_tx_fifo_op, txfifo_op_reg);
+	uart_update_timeout(port, termios->c_cflag, set_baud);
 	sirfsoc_uart_start_rx(port);
 	sirfsoc_uart_start_rx(port);
-	wr_regl(port, SIRFUART_TX_RX_EN, SIRFUART_TX_EN | SIRFUART_RX_EN);
+	wr_regl(port, ureg->sirfsoc_tx_rx_en, SIRFUART_TX_EN | SIRFUART_RX_EN);
 	spin_unlock_irqrestore(&port->lock, flags);
 	spin_unlock_irqrestore(&port->lock, flags);
 }
 }
 
 
-static void startup_uart_controller(struct uart_port *port)
+static unsigned int sirfsoc_uart_init_tx_dma(struct uart_port *port)
 {
 {
-	unsigned long temp_regv;
-	int temp;
-	temp_regv = rd_regl(port, SIRFUART_TX_DMA_IO_CTRL);
-	wr_regl(port, SIRFUART_TX_DMA_IO_CTRL, temp_regv | SIRFUART_TX_MODE_IO);
-	temp_regv = rd_regl(port, SIRFUART_RX_DMA_IO_CTRL);
-	wr_regl(port, SIRFUART_RX_DMA_IO_CTRL, temp_regv | SIRFUART_RX_MODE_IO);
-	wr_regl(port, SIRFUART_TX_DMA_IO_LEN, 0);
-	wr_regl(port, SIRFUART_RX_DMA_IO_LEN, 0);
-	wr_regl(port, SIRFUART_TX_RX_EN, SIRFUART_RX_EN | SIRFUART_TX_EN);
-	wr_regl(port, SIRFUART_TX_FIFO_OP, SIRFUART_TX_FIFO_RESET);
-	wr_regl(port, SIRFUART_TX_FIFO_OP, 0);
-	wr_regl(port, SIRFUART_RX_FIFO_OP, SIRFUART_RX_FIFO_RESET);
-	wr_regl(port, SIRFUART_RX_FIFO_OP, 0);
-	temp = port->line == 1 ? 16 : 64;
-	wr_regl(port, SIRFUART_TX_FIFO_CTRL, temp);
-	wr_regl(port, SIRFUART_RX_FIFO_CTRL, temp);
+	struct sirfsoc_uart_port *sirfport = to_sirfport(port);
+	dma_cap_mask_t dma_mask;
+	struct dma_slave_config tx_slv_cfg = {
+		.dst_maxburst = 2,
+	};
+
+	dma_cap_zero(dma_mask);
+	dma_cap_set(DMA_SLAVE, dma_mask);
+	sirfport->tx_dma_chan = dma_request_channel(dma_mask,
+		(dma_filter_fn)sirfsoc_dma_filter_id,
+		(void *)sirfport->tx_dma_no);
+	if (!sirfport->tx_dma_chan) {
+		dev_err(port->dev, "Uart Request Dma Channel Fail %d\n",
+					sirfport->tx_dma_no);
+		return  -EPROBE_DEFER;
+	}
+	dmaengine_slave_config(sirfport->tx_dma_chan, &tx_slv_cfg);
+
+	return 0;
+}
+
+static unsigned int sirfsoc_uart_init_rx_dma(struct uart_port *port)
+{
+	struct sirfsoc_uart_port *sirfport = to_sirfport(port);
+	dma_cap_mask_t dma_mask;
+	int ret;
+	int i, j;
+	struct dma_slave_config slv_cfg = {
+		.src_maxburst = 2,
+	};
+
+	dma_cap_zero(dma_mask);
+	dma_cap_set(DMA_SLAVE, dma_mask);
+	sirfport->rx_dma_chan = dma_request_channel(dma_mask,
+					(dma_filter_fn)sirfsoc_dma_filter_id,
+					(void *)sirfport->rx_dma_no);
+	if (!sirfport->rx_dma_chan) {
+		dev_err(port->dev, "Uart Request Dma Channel Fail %d\n",
+				sirfport->rx_dma_no);
+		ret = -EPROBE_DEFER;
+		goto request_err;
+	}
+	for (i = 0; i < SIRFSOC_RX_LOOP_BUF_CNT; i++) {
+		sirfport->rx_dma_items[i].xmit.buf =
+			dma_alloc_coherent(port->dev, SIRFSOC_RX_DMA_BUF_SIZE,
+			&sirfport->rx_dma_items[i].dma_addr, GFP_KERNEL);
+		if (!sirfport->rx_dma_items[i].xmit.buf) {
+			dev_err(port->dev, "Uart alloc bufa failed\n");
+			ret = -ENOMEM;
+			goto alloc_coherent_err;
+		}
+		sirfport->rx_dma_items[i].xmit.head =
+			sirfport->rx_dma_items[i].xmit.tail = 0;
+	}
+	dmaengine_slave_config(sirfport->rx_dma_chan, &slv_cfg);
+
+	return 0;
+alloc_coherent_err:
+	for (j = 0; j < i; j++)
+		dma_free_coherent(port->dev, SIRFSOC_RX_DMA_BUF_SIZE,
+				sirfport->rx_dma_items[j].xmit.buf,
+				sirfport->rx_dma_items[j].dma_addr);
+	dma_release_channel(sirfport->rx_dma_chan);
+request_err:
+	return ret;
+}
+
+static void sirfsoc_uart_uninit_tx_dma(struct sirfsoc_uart_port *sirfport)
+{
+	dmaengine_terminate_all(sirfport->tx_dma_chan);
+	dma_release_channel(sirfport->tx_dma_chan);
+}
+
+static void sirfsoc_uart_uninit_rx_dma(struct sirfsoc_uart_port *sirfport)
+{
+	int i;
+	struct uart_port *port = &sirfport->port;
+	dmaengine_terminate_all(sirfport->rx_dma_chan);
+	dma_release_channel(sirfport->rx_dma_chan);
+	for (i = 0; i < SIRFSOC_RX_LOOP_BUF_CNT; i++)
+		dma_free_coherent(port->dev, SIRFSOC_RX_DMA_BUF_SIZE,
+				sirfport->rx_dma_items[i].xmit.buf,
+				sirfport->rx_dma_items[i].dma_addr);
 }
 }
 
 
 static int sirfsoc_uart_startup(struct uart_port *port)
 static int sirfsoc_uart_startup(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;
 	unsigned int index			= port->line;
 	unsigned int index			= port->line;
 	int ret;
 	int ret;
 	set_irq_flags(port->irq, IRQF_VALID | IRQF_NOAUTOEN);
 	set_irq_flags(port->irq, IRQF_VALID | IRQF_NOAUTOEN);
@@ -504,8 +1130,64 @@ static int sirfsoc_uart_startup(struct uart_port *port)
 							index, port->irq);
 							index, port->irq);
 		goto irq_err;
 		goto irq_err;
 	}
 	}
-	startup_uart_controller(port);
+
+	/* initial hardware settings */
+	wr_regl(port, ureg->sirfsoc_tx_dma_io_ctrl,
+		rd_regl(port, ureg->sirfsoc_tx_dma_io_ctrl) |
+		SIRFUART_IO_MODE);
+	wr_regl(port, ureg->sirfsoc_rx_dma_io_ctrl,
+		rd_regl(port, ureg->sirfsoc_rx_dma_io_ctrl) |
+		SIRFUART_IO_MODE);
+	wr_regl(port, ureg->sirfsoc_tx_dma_io_len, 0);
+	wr_regl(port, ureg->sirfsoc_rx_dma_io_len, 0);
+	wr_regl(port, ureg->sirfsoc_tx_rx_en, SIRFUART_RX_EN | SIRFUART_TX_EN);
+	if (sirfport->uart_reg->uart_type == SIRF_USP_UART)
+		wr_regl(port, ureg->sirfsoc_mode1,
+			SIRFSOC_USP_ENDIAN_CTRL_LSBF |
+			SIRFSOC_USP_EN);
+	wr_regl(port, ureg->sirfsoc_tx_fifo_op, SIRFUART_FIFO_RESET);
+	wr_regl(port, ureg->sirfsoc_tx_fifo_op, 0);
+	wr_regl(port, ureg->sirfsoc_rx_fifo_op, SIRFUART_FIFO_RESET);
+	wr_regl(port, ureg->sirfsoc_rx_fifo_op, 0);
+	wr_regl(port, ureg->sirfsoc_tx_fifo_ctrl, SIRFUART_FIFO_THD(port));
+	wr_regl(port, ureg->sirfsoc_rx_fifo_ctrl, SIRFUART_FIFO_THD(port));
+
+	if (IS_DMA_CHAN_VALID(sirfport->rx_dma_no)) {
+		ret = sirfsoc_uart_init_rx_dma(port);
+		if (ret)
+			goto init_rx_err;
+		wr_regl(port, ureg->sirfsoc_rx_fifo_level_chk,
+				SIRFUART_RX_FIFO_CHK_SC(port->line, 0x4) |
+				SIRFUART_RX_FIFO_CHK_LC(port->line, 0xe) |
+				SIRFUART_RX_FIFO_CHK_HC(port->line, 0x1b));
+	}
+	if (IS_DMA_CHAN_VALID(sirfport->tx_dma_no)) {
+		sirfsoc_uart_init_tx_dma(port);
+		sirfport->tx_dma_state = TX_DMA_IDLE;
+		wr_regl(port, ureg->sirfsoc_tx_fifo_level_chk,
+				SIRFUART_TX_FIFO_CHK_SC(port->line, 0x1b) |
+				SIRFUART_TX_FIFO_CHK_LC(port->line, 0xe) |
+				SIRFUART_TX_FIFO_CHK_HC(port->line, 0x4));
+	}
+	sirfport->ms_enabled = false;
+	if (sirfport->uart_reg->uart_type == SIRF_USP_UART &&
+		sirfport->hw_flow_ctrl) {
+		set_irq_flags(gpio_to_irq(sirfport->cts_gpio),
+			IRQF_VALID | IRQF_NOAUTOEN);
+		ret = request_irq(gpio_to_irq(sirfport->cts_gpio),
+			sirfsoc_uart_usp_cts_handler, IRQF_TRIGGER_FALLING |
+			IRQF_TRIGGER_RISING, "usp_cts_irq", sirfport);
+		if (ret != 0) {
+			dev_err(port->dev, "UART-USP:request gpio irq fail\n");
+			goto init_rx_err;
+		}
+	}
+
 	enable_irq(port->irq);
 	enable_irq(port->irq);
+
+	return 0;
+init_rx_err:
+	free_irq(port->irq, sirfport);
 irq_err:
 irq_err:
 	return ret;
 	return ret;
 }
 }
@@ -513,11 +1195,25 @@ irq_err:
 static void sirfsoc_uart_shutdown(struct uart_port *port)
 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);
-	wr_regl(port, SIRFUART_INT_EN, 0);
+	struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg;
+	if (!sirfport->is_marco)
+		wr_regl(port, ureg->sirfsoc_int_en_reg, 0);
+	else
+		wr_regl(port, SIRFUART_INT_EN_CLR, ~0UL);
+
 	free_irq(port->irq, sirfport);
 	free_irq(port->irq, sirfport);
-	if (sirfport->ms_enabled) {
+	if (sirfport->ms_enabled)
 		sirfsoc_uart_disable_ms(port);
 		sirfsoc_uart_disable_ms(port);
-		sirfport->ms_enabled = 0;
+	if (sirfport->uart_reg->uart_type == SIRF_USP_UART &&
+			sirfport->hw_flow_ctrl) {
+		gpio_set_value(sirfport->rts_gpio, 1);
+		free_irq(gpio_to_irq(sirfport->cts_gpio), sirfport);
+	}
+	if (IS_DMA_CHAN_VALID(sirfport->rx_dma_no))
+		sirfsoc_uart_uninit_rx_dma(sirfport);
+	if (IS_DMA_CHAN_VALID(sirfport->tx_dma_no)) {
+		sirfsoc_uart_uninit_tx_dma(sirfport);
+		sirfport->tx_dma_state = TX_DMA_IDLE;
 	}
 	}
 }
 }
 
 
@@ -528,9 +1224,11 @@ static const char *sirfsoc_uart_type(struct uart_port *port)
 
 
 static int sirfsoc_uart_request_port(struct uart_port *port)
 static int sirfsoc_uart_request_port(struct uart_port *port)
 {
 {
+	struct sirfsoc_uart_port *sirfport = to_sirfport(port);
+	struct sirfsoc_uart_param *uart_param = &sirfport->uart_reg->uart_param;
 	void *ret;
 	void *ret;
 	ret = request_mem_region(port->mapbase,
 	ret = request_mem_region(port->mapbase,
-				SIRFUART_MAP_SIZE, SIRFUART_PORT_NAME);
+		SIRFUART_MAP_SIZE, uart_param->port_name);
 	return ret ? 0 : -EBUSY;
 	return ret ? 0 : -EBUSY;
 }
 }
 
 
@@ -566,32 +1264,45 @@ static struct uart_ops sirfsoc_uart_ops = {
 };
 };
 
 
 #ifdef CONFIG_SERIAL_SIRFSOC_CONSOLE
 #ifdef CONFIG_SERIAL_SIRFSOC_CONSOLE
-static int __init sirfsoc_uart_console_setup(struct console *co, char *options)
+static int __init
+sirfsoc_uart_console_setup(struct console *co, char *options)
 {
 {
 	unsigned int baud = 115200;
 	unsigned int baud = 115200;
 	unsigned int bits = 8;
 	unsigned int bits = 8;
 	unsigned int parity = 'n';
 	unsigned int parity = 'n';
 	unsigned int flow = 'n';
 	unsigned int flow = 'n';
 	struct uart_port *port = &sirfsoc_uart_ports[co->index].port;
 	struct uart_port *port = &sirfsoc_uart_ports[co->index].port;
-
+	struct sirfsoc_uart_port *sirfport = to_sirfport(port);
+	struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg;
 	if (co->index < 0 || co->index >= SIRFSOC_UART_NR)
 	if (co->index < 0 || co->index >= SIRFSOC_UART_NR)
 		return -EINVAL;
 		return -EINVAL;
 
 
 	if (!port->mapbase)
 	if (!port->mapbase)
 		return -ENODEV;
 		return -ENODEV;
 
 
+	/* enable usp in mode1 register */
+	if (sirfport->uart_reg->uart_type == SIRF_USP_UART)
+		wr_regl(port, ureg->sirfsoc_mode1, SIRFSOC_USP_EN |
+				SIRFSOC_USP_ENDIAN_CTRL_LSBF);
 	if (options)
 	if (options)
 		uart_parse_options(options, &baud, &parity, &bits, &flow);
 		uart_parse_options(options, &baud, &parity, &bits, &flow);
 	port->cons = co;
 	port->cons = co;
+
+	/* default console tx/rx transfer using io mode */
+	sirfport->rx_dma_no = UNVALID_DMA_CHAN;
+	sirfport->tx_dma_no = UNVALID_DMA_CHAN;
 	return uart_set_options(port, co, baud, parity, bits, flow);
 	return uart_set_options(port, co, baud, parity, bits, flow);
 }
 }
 
 
 static void sirfsoc_uart_console_putchar(struct uart_port *port, int ch)
 static void sirfsoc_uart_console_putchar(struct uart_port *port, int ch)
 {
 {
+	struct sirfsoc_uart_port *sirfport = to_sirfport(port);
+	struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg;
+	struct sirfsoc_fifo_status *ufifo_st = &sirfport->uart_reg->fifo_status;
 	while (rd_regl(port,
 	while (rd_regl(port,
-		SIRFUART_TX_FIFO_STATUS) & SIRFUART_FIFOFULL_MASK(port))
+		ureg->sirfsoc_tx_fifo_status) & ufifo_st->ff_full(port->line))
 		cpu_relax();
 		cpu_relax();
-	wr_regb(port, SIRFUART_TX_FIFO_DATA, ch);
+	wr_regb(port, ureg->sirfsoc_tx_fifo_data, ch);
 }
 }
 
 
 static void sirfsoc_uart_console_write(struct console *co, const char *s,
 static void sirfsoc_uart_console_write(struct console *co, const char *s,
@@ -633,27 +1344,99 @@ static struct uart_driver sirfsoc_uart_drv = {
 #endif
 #endif
 };
 };
 
 
-int sirfsoc_uart_probe(struct platform_device *pdev)
+static struct of_device_id sirfsoc_uart_ids[] = {
+	{ .compatible = "sirf,prima2-uart", .data = &sirfsoc_uart,},
+	{ .compatible = "sirf,marco-uart", .data = &sirfsoc_uart},
+	{ .compatible = "sirf,prima2-usp-uart", .data = &sirfsoc_usp},
+	{}
+};
+MODULE_DEVICE_TABLE(of, sirfsoc_uart_ids);
+
+static int sirfsoc_uart_probe(struct platform_device *pdev)
 {
 {
 	struct sirfsoc_uart_port *sirfport;
 	struct sirfsoc_uart_port *sirfport;
 	struct uart_port *port;
 	struct uart_port *port;
 	struct resource *res;
 	struct resource *res;
 	int ret;
 	int ret;
+	const struct of_device_id *match;
 
 
+	match = of_match_node(sirfsoc_uart_ids, pdev->dev.of_node);
 	if (of_property_read_u32(pdev->dev.of_node, "cell-index", &pdev->id)) {
 	if (of_property_read_u32(pdev->dev.of_node, "cell-index", &pdev->id)) {
 		dev_err(&pdev->dev,
 		dev_err(&pdev->dev,
 			"Unable to find cell-index in uart node.\n");
 			"Unable to find cell-index in uart node.\n");
 		ret = -EFAULT;
 		ret = -EFAULT;
 		goto err;
 		goto err;
 	}
 	}
-
+	if (of_device_is_compatible(pdev->dev.of_node, "sirf,prima2-usp-uart"))
+		pdev->id += ((struct sirfsoc_uart_register *)
+				match->data)->uart_param.register_uart_nr;
 	sirfport = &sirfsoc_uart_ports[pdev->id];
 	sirfport = &sirfsoc_uart_ports[pdev->id];
 	port = &sirfport->port;
 	port = &sirfport->port;
 	port->dev = &pdev->dev;
 	port->dev = &pdev->dev;
 	port->private_data = sirfport;
 	port->private_data = sirfport;
+	sirfport->uart_reg = (struct sirfsoc_uart_register *)match->data;
+
+	sirfport->hw_flow_ctrl = of_property_read_bool(pdev->dev.of_node,
+		"sirf,uart-has-rtscts");
+	if (of_device_is_compatible(pdev->dev.of_node, "sirf,prima2-uart")) {
+		sirfport->uart_reg->uart_type = SIRF_REAL_UART;
+		if (of_property_read_u32(pdev->dev.of_node,
+				"sirf,uart-dma-rx-channel",
+				&sirfport->rx_dma_no))
+			sirfport->rx_dma_no = UNVALID_DMA_CHAN;
+		if (of_property_read_u32(pdev->dev.of_node,
+				"sirf,uart-dma-tx-channel",
+				&sirfport->tx_dma_no))
+			sirfport->tx_dma_no = UNVALID_DMA_CHAN;
+	}
+	if (of_device_is_compatible(pdev->dev.of_node, "sirf,prima2-usp-uart")) {
+		sirfport->uart_reg->uart_type =	SIRF_USP_UART;
+		if (of_property_read_u32(pdev->dev.of_node,
+				"sirf,usp-dma-rx-channel",
+				&sirfport->rx_dma_no))
+			sirfport->rx_dma_no = UNVALID_DMA_CHAN;
+		if (of_property_read_u32(pdev->dev.of_node,
+				"sirf,usp-dma-tx-channel",
+				&sirfport->tx_dma_no))
+			sirfport->tx_dma_no = UNVALID_DMA_CHAN;
+		if (!sirfport->hw_flow_ctrl)
+			goto usp_no_flow_control;
+		if (of_find_property(pdev->dev.of_node, "cts-gpios", NULL))
+			sirfport->cts_gpio = of_get_named_gpio(
+					pdev->dev.of_node, "cts-gpios", 0);
+		else
+			sirfport->cts_gpio = -1;
+		if (of_find_property(pdev->dev.of_node, "rts-gpios", NULL))
+			sirfport->rts_gpio = of_get_named_gpio(
+					pdev->dev.of_node, "rts-gpios", 0);
+		else
+			sirfport->rts_gpio = -1;
 
 
-	if (of_find_property(pdev->dev.of_node, "hw_flow_ctrl", NULL))
-		sirfport->hw_flow_ctrl = 1;
+		if ((!gpio_is_valid(sirfport->cts_gpio) ||
+			 !gpio_is_valid(sirfport->rts_gpio))) {
+			ret = -EINVAL;
+			dev_err(&pdev->dev,
+				"Usp flow control must have cts and rts gpio");
+			goto err;
+		}
+		ret = devm_gpio_request(&pdev->dev, sirfport->cts_gpio,
+				"usp-cts-gpio");
+		if (ret) {
+			dev_err(&pdev->dev, "Unable request cts gpio");
+			goto err;
+		}
+		gpio_direction_input(sirfport->cts_gpio);
+		ret = devm_gpio_request(&pdev->dev, sirfport->rts_gpio,
+				"usp-rts-gpio");
+		if (ret) {
+			dev_err(&pdev->dev, "Unable request rts gpio");
+			goto err;
+		}
+		gpio_direction_output(sirfport->rts_gpio, 1);
+	}
+usp_no_flow_control:
+	if (of_device_is_compatible(pdev->dev.of_node, "sirf,marco-uart"))
+		sirfport->is_marco = true;
 
 
 	if (of_property_read_u32(pdev->dev.of_node,
 	if (of_property_read_u32(pdev->dev.of_node,
 			"fifosize",
 			"fifosize",
@@ -670,6 +1453,12 @@ int sirfsoc_uart_probe(struct platform_device *pdev)
 		ret = -EFAULT;
 		ret = -EFAULT;
 		goto err;
 		goto err;
 	}
 	}
+	spin_lock_init(&sirfport->rx_lock);
+	spin_lock_init(&sirfport->tx_lock);
+	tasklet_init(&sirfport->rx_dma_complete_tasklet,
+			sirfsoc_uart_rx_dma_complete_tl, (unsigned long)sirfport);
+	tasklet_init(&sirfport->rx_tmo_process_tasklet,
+			sirfsoc_rx_tmo_process_tl, (unsigned long)sirfport);
 	port->mapbase = res->start;
 	port->mapbase = res->start;
 	port->membase = devm_ioremap(&pdev->dev, res->start, resource_size(res));
 	port->membase = devm_ioremap(&pdev->dev, res->start, resource_size(res));
 	if (!port->membase) {
 	if (!port->membase) {
@@ -685,18 +1474,10 @@ int sirfsoc_uart_probe(struct platform_device *pdev)
 	}
 	}
 	port->irq = res->start;
 	port->irq = res->start;
 
 
-	if (sirfport->hw_flow_ctrl) {
-		sirfport->p = pinctrl_get_select_default(&pdev->dev);
-		if (IS_ERR(sirfport->p)) {
-			ret = PTR_ERR(sirfport->p);
-			goto err;
-		}
-	}
-
 	sirfport->clk = clk_get(&pdev->dev, NULL);
 	sirfport->clk = clk_get(&pdev->dev, NULL);
 	if (IS_ERR(sirfport->clk)) {
 	if (IS_ERR(sirfport->clk)) {
 		ret = PTR_ERR(sirfport->clk);
 		ret = PTR_ERR(sirfport->clk);
-		goto clk_err;
+		goto err;
 	}
 	}
 	clk_prepare_enable(sirfport->clk);
 	clk_prepare_enable(sirfport->clk);
 	port->uartclk = clk_get_rate(sirfport->clk);
 	port->uartclk = clk_get_rate(sirfport->clk);
@@ -716,10 +1497,6 @@ int sirfsoc_uart_probe(struct platform_device *pdev)
 port_err:
 port_err:
 	clk_disable_unprepare(sirfport->clk);
 	clk_disable_unprepare(sirfport->clk);
 	clk_put(sirfport->clk);
 	clk_put(sirfport->clk);
-clk_err:
-	platform_set_drvdata(pdev, NULL);
-	if (sirfport->hw_flow_ctrl)
-		pinctrl_put(sirfport->p);
 err:
 err:
 	return ret;
 	return ret;
 }
 }
@@ -728,9 +1505,6 @@ static int sirfsoc_uart_remove(struct platform_device *pdev)
 {
 {
 	struct sirfsoc_uart_port *sirfport = platform_get_drvdata(pdev);
 	struct sirfsoc_uart_port *sirfport = platform_get_drvdata(pdev);
 	struct uart_port *port = &sirfport->port;
 	struct uart_port *port = &sirfport->port;
-	platform_set_drvdata(pdev, NULL);
-	if (sirfport->hw_flow_ctrl)
-		pinctrl_put(sirfport->p);
 	clk_disable_unprepare(sirfport->clk);
 	clk_disable_unprepare(sirfport->clk);
 	clk_put(sirfport->clk);
 	clk_put(sirfport->clk);
 	uart_remove_one_port(&sirfsoc_uart_drv, port);
 	uart_remove_one_port(&sirfsoc_uart_drv, port);
@@ -754,13 +1528,6 @@ static int sirfsoc_uart_resume(struct platform_device *pdev)
 	return 0;
 	return 0;
 }
 }
 
 
-static struct of_device_id sirfsoc_uart_ids[] = {
-	{ .compatible = "sirf,prima2-uart", },
-	{ .compatible = "sirf,marco-uart", },
-	{}
-};
-MODULE_DEVICE_TABLE(of, sirfsoc_uart_ids);
-
 static struct platform_driver sirfsoc_uart_driver = {
 static struct platform_driver sirfsoc_uart_driver = {
 	.probe		= sirfsoc_uart_probe,
 	.probe		= sirfsoc_uart_probe,
 	.remove		= sirfsoc_uart_remove,
 	.remove		= sirfsoc_uart_remove,

+ 394 - 107
drivers/tty/serial/sirfsoc_uart.h

@@ -6,31 +6,260 @@
  * Licensed under GPLv2 or later.
  * Licensed under GPLv2 or later.
  */
  */
 #include <linux/bitops.h>
 #include <linux/bitops.h>
+struct sirfsoc_uart_param {
+	const char *uart_name;
+	const char *port_name;
+	u32 uart_nr;
+	u32 register_uart_nr;
+};
+
+struct sirfsoc_register {
+	/* hardware uart specific */
+	u32 sirfsoc_line_ctrl;
+	u32 sirfsoc_divisor;
+	/* uart - usp common */
+	u32 sirfsoc_tx_rx_en;
+	u32 sirfsoc_int_en_reg;
+	u32 sirfsoc_int_st_reg;
+	u32 sirfsoc_tx_dma_io_ctrl;
+	u32 sirfsoc_tx_dma_io_len;
+	u32 sirfsoc_tx_fifo_ctrl;
+	u32 sirfsoc_tx_fifo_level_chk;
+	u32 sirfsoc_tx_fifo_op;
+	u32 sirfsoc_tx_fifo_status;
+	u32 sirfsoc_tx_fifo_data;
+	u32 sirfsoc_rx_dma_io_ctrl;
+	u32 sirfsoc_rx_dma_io_len;
+	u32 sirfsoc_rx_fifo_ctrl;
+	u32 sirfsoc_rx_fifo_level_chk;
+	u32 sirfsoc_rx_fifo_op;
+	u32 sirfsoc_rx_fifo_status;
+	u32 sirfsoc_rx_fifo_data;
+	u32 sirfsoc_afc_ctrl;
+	u32 sirfsoc_swh_dma_io;
+	/* hardware usp specific */
+	u32 sirfsoc_mode1;
+	u32 sirfsoc_mode2;
+	u32 sirfsoc_tx_frame_ctrl;
+	u32 sirfsoc_rx_frame_ctrl;
+	u32 sirfsoc_async_param_reg;
+};
+
+typedef u32 (*fifo_full_mask)(int line);
+typedef u32 (*fifo_empty_mask)(int line);
+
+struct sirfsoc_fifo_status {
+	fifo_full_mask ff_full;
+	fifo_empty_mask ff_empty;
+};
 
 
-/* UART Register Offset Define */
-#define SIRFUART_LINE_CTRL			0x0040
-#define SIRFUART_TX_RX_EN			0x004c
-#define SIRFUART_DIVISOR			0x0050
-#define SIRFUART_INT_EN				0x0054
-#define SIRFUART_INT_STATUS			0x0058
-#define SIRFUART_TX_DMA_IO_CTRL			0x0100
-#define SIRFUART_TX_DMA_IO_LEN			0x0104
-#define SIRFUART_TX_FIFO_CTRL			0x0108
-#define SIRFUART_TX_FIFO_LEVEL_CHK		0x010C
-#define SIRFUART_TX_FIFO_OP			0x0110
-#define SIRFUART_TX_FIFO_STATUS			0x0114
-#define SIRFUART_TX_FIFO_DATA			0x0118
-#define SIRFUART_RX_DMA_IO_CTRL			0x0120
-#define SIRFUART_RX_DMA_IO_LEN			0x0124
-#define SIRFUART_RX_FIFO_CTRL			0x0128
-#define SIRFUART_RX_FIFO_LEVEL_CHK		0x012C
-#define SIRFUART_RX_FIFO_OP			0x0130
-#define SIRFUART_RX_FIFO_STATUS			0x0134
-#define SIRFUART_RX_FIFO_DATA			0x0138
-#define SIRFUART_AFC_CTRL			0x0140
-#define SIRFUART_SWH_DMA_IO			0x0148
-
-/* UART Line Control Register */
+struct sirfsoc_int_en {
+	u32 sirfsoc_rx_done_en;
+	u32 sirfsoc_tx_done_en;
+	u32 sirfsoc_rx_oflow_en;
+	u32 sirfsoc_tx_allout_en;
+	u32 sirfsoc_rx_io_dma_en;
+	u32 sirfsoc_tx_io_dma_en;
+	u32 sirfsoc_rxfifo_full_en;
+	u32 sirfsoc_txfifo_empty_en;
+	u32 sirfsoc_rxfifo_thd_en;
+	u32 sirfsoc_txfifo_thd_en;
+	u32 sirfsoc_frm_err_en;
+	u32 sirfsoc_rxd_brk_en;
+	u32 sirfsoc_rx_timeout_en;
+	u32 sirfsoc_parity_err_en;
+	u32 sirfsoc_cts_en;
+	u32 sirfsoc_rts_en;
+};
+
+struct sirfsoc_int_status {
+	u32 sirfsoc_rx_done;
+	u32 sirfsoc_tx_done;
+	u32 sirfsoc_rx_oflow;
+	u32 sirfsoc_tx_allout;
+	u32 sirfsoc_rx_io_dma;
+	u32 sirfsoc_tx_io_dma;
+	u32 sirfsoc_rxfifo_full;
+	u32 sirfsoc_txfifo_empty;
+	u32 sirfsoc_rxfifo_thd;
+	u32 sirfsoc_txfifo_thd;
+	u32 sirfsoc_frm_err;
+	u32 sirfsoc_rxd_brk;
+	u32 sirfsoc_rx_timeout;
+	u32 sirfsoc_parity_err;
+	u32 sirfsoc_cts;
+	u32 sirfsoc_rts;
+};
+
+enum sirfsoc_uart_type {
+	SIRF_REAL_UART,
+	SIRF_USP_UART,
+};
+
+struct sirfsoc_uart_register {
+	struct sirfsoc_register uart_reg;
+	struct sirfsoc_int_en uart_int_en;
+	struct sirfsoc_int_status uart_int_st;
+	struct sirfsoc_fifo_status fifo_status;
+	struct sirfsoc_uart_param uart_param;
+	enum sirfsoc_uart_type uart_type;
+};
+
+u32 usp_ff_full(int line)
+{
+	return 0x80;
+}
+u32 usp_ff_empty(int line)
+{
+	return 0x100;
+}
+u32 uart_ff_full(int line)
+{
+	return (line == 1) ? (0x20) : (0x80);
+}
+u32 uart_ff_empty(int line)
+{
+	return (line == 1) ? (0x40) : (0x100);
+}
+struct sirfsoc_uart_register sirfsoc_usp = {
+	.uart_reg = {
+		.sirfsoc_mode1		= 0x0000,
+		.sirfsoc_mode2		= 0x0004,
+		.sirfsoc_tx_frame_ctrl	= 0x0008,
+		.sirfsoc_rx_frame_ctrl	= 0x000c,
+		.sirfsoc_tx_rx_en	= 0x0010,
+		.sirfsoc_int_en_reg	= 0x0014,
+		.sirfsoc_int_st_reg	= 0x0018,
+		.sirfsoc_async_param_reg = 0x0024,
+		.sirfsoc_tx_dma_io_ctrl	= 0x0100,
+		.sirfsoc_tx_dma_io_len	= 0x0104,
+		.sirfsoc_tx_fifo_ctrl	= 0x0108,
+		.sirfsoc_tx_fifo_level_chk = 0x010c,
+		.sirfsoc_tx_fifo_op	= 0x0110,
+		.sirfsoc_tx_fifo_status	= 0x0114,
+		.sirfsoc_tx_fifo_data	= 0x0118,
+		.sirfsoc_rx_dma_io_ctrl	= 0x0120,
+		.sirfsoc_rx_dma_io_len	= 0x0124,
+		.sirfsoc_rx_fifo_ctrl	= 0x0128,
+		.sirfsoc_rx_fifo_level_chk = 0x012c,
+		.sirfsoc_rx_fifo_op	= 0x0130,
+		.sirfsoc_rx_fifo_status	= 0x0134,
+		.sirfsoc_rx_fifo_data	= 0x0138,
+	},
+	.uart_int_en = {
+		.sirfsoc_rx_done_en	= BIT(0),
+		.sirfsoc_tx_done_en	= BIT(1),
+		.sirfsoc_rx_oflow_en	= BIT(2),
+		.sirfsoc_tx_allout_en	= BIT(3),
+		.sirfsoc_rx_io_dma_en	= BIT(4),
+		.sirfsoc_tx_io_dma_en	= BIT(5),
+		.sirfsoc_rxfifo_full_en	= BIT(6),
+		.sirfsoc_txfifo_empty_en = BIT(7),
+		.sirfsoc_rxfifo_thd_en	= BIT(8),
+		.sirfsoc_txfifo_thd_en	= BIT(9),
+		.sirfsoc_frm_err_en	= BIT(10),
+		.sirfsoc_rx_timeout_en	= BIT(11),
+		.sirfsoc_rxd_brk_en	= BIT(15),
+	},
+	.uart_int_st = {
+		.sirfsoc_rx_done	= BIT(0),
+		.sirfsoc_tx_done	= BIT(1),
+		.sirfsoc_rx_oflow	= BIT(2),
+		.sirfsoc_tx_allout	= BIT(3),
+		.sirfsoc_rx_io_dma	= BIT(4),
+		.sirfsoc_tx_io_dma	= BIT(5),
+		.sirfsoc_rxfifo_full	= BIT(6),
+		.sirfsoc_txfifo_empty	= BIT(7),
+		.sirfsoc_rxfifo_thd	= BIT(8),
+		.sirfsoc_txfifo_thd	= BIT(9),
+		.sirfsoc_frm_err	= BIT(10),
+		.sirfsoc_rx_timeout	= BIT(11),
+		.sirfsoc_rxd_brk	= BIT(15),
+	},
+	.fifo_status = {
+		.ff_full		= usp_ff_full,
+		.ff_empty		= usp_ff_empty,
+	},
+	.uart_param = {
+		.uart_name = "ttySiRF",
+		.port_name = "sirfsoc-uart",
+		.uart_nr = 2,
+		.register_uart_nr = 3,
+	},
+};
+
+struct sirfsoc_uart_register sirfsoc_uart = {
+	.uart_reg = {
+		.sirfsoc_line_ctrl	= 0x0040,
+		.sirfsoc_tx_rx_en	= 0x004c,
+		.sirfsoc_divisor	= 0x0050,
+		.sirfsoc_int_en_reg	= 0x0054,
+		.sirfsoc_int_st_reg	= 0x0058,
+		.sirfsoc_tx_dma_io_ctrl	= 0x0100,
+		.sirfsoc_tx_dma_io_len	= 0x0104,
+		.sirfsoc_tx_fifo_ctrl	= 0x0108,
+		.sirfsoc_tx_fifo_level_chk = 0x010c,
+		.sirfsoc_tx_fifo_op	= 0x0110,
+		.sirfsoc_tx_fifo_status	= 0x0114,
+		.sirfsoc_tx_fifo_data	= 0x0118,
+		.sirfsoc_rx_dma_io_ctrl	= 0x0120,
+		.sirfsoc_rx_dma_io_len	= 0x0124,
+		.sirfsoc_rx_fifo_ctrl	= 0x0128,
+		.sirfsoc_rx_fifo_level_chk = 0x012c,
+		.sirfsoc_rx_fifo_op	= 0x0130,
+		.sirfsoc_rx_fifo_status	= 0x0134,
+		.sirfsoc_rx_fifo_data	= 0x0138,
+		.sirfsoc_afc_ctrl	= 0x0140,
+		.sirfsoc_swh_dma_io	= 0x0148,
+	},
+	.uart_int_en = {
+		.sirfsoc_rx_done_en	= BIT(0),
+		.sirfsoc_tx_done_en	= BIT(1),
+		.sirfsoc_rx_oflow_en	= BIT(2),
+		.sirfsoc_tx_allout_en	= BIT(3),
+		.sirfsoc_rx_io_dma_en	= BIT(4),
+		.sirfsoc_tx_io_dma_en	= BIT(5),
+		.sirfsoc_rxfifo_full_en	= BIT(6),
+		.sirfsoc_txfifo_empty_en = BIT(7),
+		.sirfsoc_rxfifo_thd_en	= BIT(8),
+		.sirfsoc_txfifo_thd_en	= BIT(9),
+		.sirfsoc_frm_err_en	= BIT(10),
+		.sirfsoc_rxd_brk_en	= BIT(11),
+		.sirfsoc_rx_timeout_en	= BIT(12),
+		.sirfsoc_parity_err_en	= BIT(13),
+		.sirfsoc_cts_en		= BIT(14),
+		.sirfsoc_rts_en		= BIT(15),
+	},
+	.uart_int_st = {
+		.sirfsoc_rx_done	= BIT(0),
+		.sirfsoc_tx_done	= BIT(1),
+		.sirfsoc_rx_oflow	= BIT(2),
+		.sirfsoc_tx_allout	= BIT(3),
+		.sirfsoc_rx_io_dma	= BIT(4),
+		.sirfsoc_tx_io_dma	= BIT(5),
+		.sirfsoc_rxfifo_full	= BIT(6),
+		.sirfsoc_txfifo_empty	= BIT(7),
+		.sirfsoc_rxfifo_thd	= BIT(8),
+		.sirfsoc_txfifo_thd	= BIT(9),
+		.sirfsoc_frm_err	= BIT(10),
+		.sirfsoc_rxd_brk	= BIT(11),
+		.sirfsoc_rx_timeout	= BIT(12),
+		.sirfsoc_parity_err	= BIT(13),
+		.sirfsoc_cts		= BIT(14),
+		.sirfsoc_rts		= BIT(15),
+	},
+	.fifo_status = {
+		.ff_full		= uart_ff_full,
+		.ff_empty		= uart_ff_empty,
+	},
+	.uart_param = {
+		.uart_name = "ttySiRF",
+		.port_name = "sirfsoc_uart",
+		.uart_nr = 3,
+		.register_uart_nr = 0,
+	},
+};
+/* uart io ctrl */
 #define SIRFUART_DATA_BIT_LEN_MASK		0x3
 #define SIRFUART_DATA_BIT_LEN_MASK		0x3
 #define SIRFUART_DATA_BIT_LEN_5			BIT(0)
 #define SIRFUART_DATA_BIT_LEN_5			BIT(0)
 #define SIRFUART_DATA_BIT_LEN_6			1
 #define SIRFUART_DATA_BIT_LEN_6			1
@@ -50,96 +279,93 @@
 #define SIRFUART_LOOP_BACK			BIT(7)
 #define SIRFUART_LOOP_BACK			BIT(7)
 #define SIRFUART_PARITY_MASK			(7 << 3)
 #define SIRFUART_PARITY_MASK			(7 << 3)
 #define SIRFUART_DUMMY_READ			BIT(16)
 #define SIRFUART_DUMMY_READ			BIT(16)
-
-#define SIRFSOC_UART_RX_TIMEOUT(br, to)	(((br) * (((to) + 999) / 1000)) / 1000)
-#define SIRFUART_RECV_TIMEOUT_MASK	(0xFFFF << 16)
-#define SIRFUART_RECV_TIMEOUT(x)	(((x) & 0xFFFF) << 16)
-
-/* UART Auto Flow Control */
-#define SIRFUART_AFC_RX_THD_MASK		0x000000FF
+#define SIRFUART_AFC_CTRL_RX_THD		0x70
 #define SIRFUART_AFC_RX_EN			BIT(8)
 #define SIRFUART_AFC_RX_EN			BIT(8)
 #define SIRFUART_AFC_TX_EN			BIT(9)
 #define SIRFUART_AFC_TX_EN			BIT(9)
-#define SIRFUART_CTS_CTRL			BIT(10)
-#define SIRFUART_RTS_CTRL			BIT(11)
-#define SIRFUART_CTS_IN_STATUS			BIT(12)
-#define SIRFUART_RTS_OUT_STATUS			BIT(13)
-
-/* UART Interrupt Enable Register */
-#define SIRFUART_RX_DONE_INT			BIT(0)
-#define SIRFUART_TX_DONE_INT			BIT(1)
-#define SIRFUART_RX_OFLOW_INT			BIT(2)
-#define SIRFUART_TX_ALLOUT_INT			BIT(3)
-#define SIRFUART_RX_IO_DMA_INT			BIT(4)
-#define SIRFUART_TX_IO_DMA_INT			BIT(5)
-#define SIRFUART_RXFIFO_FULL_INT		BIT(6)
-#define SIRFUART_TXFIFO_EMPTY_INT		BIT(7)
-#define SIRFUART_RXFIFO_THD_INT			BIT(8)
-#define SIRFUART_TXFIFO_THD_INT			BIT(9)
-#define SIRFUART_FRM_ERR_INT			BIT(10)
-#define SIRFUART_RXD_BREAK_INT			BIT(11)
-#define SIRFUART_RX_TIMEOUT_INT			BIT(12)
-#define SIRFUART_PARITY_ERR_INT			BIT(13)
-#define SIRFUART_CTS_INT_EN			BIT(14)
-#define SIRFUART_RTS_INT_EN			BIT(15)
-
-/* UART Interrupt Status Register */
-#define SIRFUART_RX_DONE			BIT(0)
-#define SIRFUART_TX_DONE			BIT(1)
-#define SIRFUART_RX_OFLOW			BIT(2)
-#define SIRFUART_TX_ALL_EMPTY			BIT(3)
-#define SIRFUART_DMA_IO_RX_DONE			BIT(4)
-#define SIRFUART_DMA_IO_TX_DONE			BIT(5)
-#define SIRFUART_RXFIFO_FULL			BIT(6)
-#define SIRFUART_TXFIFO_EMPTY			BIT(7)
-#define SIRFUART_RXFIFO_THD_REACH		BIT(8)
-#define SIRFUART_TXFIFO_THD_REACH		BIT(9)
-#define SIRFUART_FRM_ERR			BIT(10)
-#define SIRFUART_RXD_BREAK			BIT(11)
-#define SIRFUART_RX_TIMEOUT			BIT(12)
-#define SIRFUART_PARITY_ERR			BIT(13)
-#define SIRFUART_CTS_CHANGE			BIT(14)
-#define SIRFUART_RTS_CHANGE			BIT(15)
-#define SIRFUART_PLUG_IN			BIT(16)
-
-#define SIRFUART_ERR_INT_STAT					\
-				(SIRFUART_RX_OFLOW |		\
-				SIRFUART_FRM_ERR |		\
-				SIRFUART_RXD_BREAK |		\
-				SIRFUART_PARITY_ERR)
-#define SIRFUART_ERR_INT_EN					\
-				(SIRFUART_RX_OFLOW_INT |	\
-				SIRFUART_FRM_ERR_INT |		\
-				SIRFUART_RXD_BREAK_INT |	\
-				SIRFUART_PARITY_ERR_INT)
-#define SIRFUART_TX_INT_EN	SIRFUART_TXFIFO_EMPTY_INT
-#define SIRFUART_RX_IO_INT_EN					\
-				(SIRFUART_RX_TIMEOUT_INT |	\
-				SIRFUART_RXFIFO_THD_INT |	\
-				SIRFUART_RXFIFO_FULL_INT |	\
-				SIRFUART_ERR_INT_EN)
-
+#define SIRFUART_AFC_CTS_CTRL			BIT(10)
+#define SIRFUART_AFC_RTS_CTRL			BIT(11)
+#define	SIRFUART_AFC_CTS_STATUS			BIT(12)
+#define	SIRFUART_AFC_RTS_STATUS			BIT(13)
 /* UART FIFO Register */
 /* UART FIFO Register */
-#define SIRFUART_TX_FIFO_STOP			0x0
-#define SIRFUART_TX_FIFO_RESET			0x1
-#define SIRFUART_TX_FIFO_START			0x2
-#define SIRFUART_RX_FIFO_STOP			0x0
-#define SIRFUART_RX_FIFO_RESET			0x1
-#define SIRFUART_RX_FIFO_START			0x2
-#define SIRFUART_TX_MODE_DMA			0
-#define SIRFUART_TX_MODE_IO			1
-#define SIRFUART_RX_MODE_DMA			0
-#define SIRFUART_RX_MODE_IO			1
-
-#define SIRFUART_RX_EN				0x1
-#define SIRFUART_TX_EN				0x2
+#define SIRFUART_FIFO_STOP			0x0
+#define SIRFUART_FIFO_RESET			BIT(0)
+#define SIRFUART_FIFO_START			BIT(1)
 
 
+#define SIRFUART_RX_EN				BIT(0)
+#define SIRFUART_TX_EN				BIT(1)
+
+#define SIRFUART_IO_MODE			BIT(0)
+#define SIRFUART_DMA_MODE			0x0
+
+/* Macro Specific*/
+#define SIRFUART_INT_EN_CLR                    0x0060
+/* Baud Rate Calculation */
+#define SIRF_MIN_SAMPLE_DIV			0xf
+#define SIRF_MAX_SAMPLE_DIV			0x3f
+#define SIRF_IOCLK_DIV_MAX			0xffff
+#define SIRF_SAMPLE_DIV_SHIFT			16
+#define SIRF_IOCLK_DIV_MASK			0xffff
+#define SIRF_SAMPLE_DIV_MASK			0x3f0000
+#define SIRF_BAUD_RATE_SUPPORT_NR		18
+
+/* USP SPEC */
+#define SIRFSOC_USP_ENDIAN_CTRL_LSBF		BIT(4)
+#define SIRFSOC_USP_EN				BIT(5)
+#define SIRFSOC_USP_MODE2_RXD_DELAY_OFFSET	0
+#define SIRFSOC_USP_MODE2_TXD_DELAY_OFFSET	8
+#define SIRFSOC_USP_MODE2_CLK_DIVISOR_MASK	0x3ff
+#define SIRFSOC_USP_MODE2_CLK_DIVISOR_OFFSET	21
+#define SIRFSOC_USP_TX_DATA_LEN_OFFSET		0
+#define SIRFSOC_USP_TX_SYNC_LEN_OFFSET		8
+#define SIRFSOC_USP_TX_FRAME_LEN_OFFSET		16
+#define SIRFSOC_USP_TX_SHIFTER_LEN_OFFSET	24
+#define SIRFSOC_USP_TX_CLK_DIVISOR_OFFSET	30
+#define SIRFSOC_USP_RX_DATA_LEN_OFFSET		0
+#define SIRFSOC_USP_RX_FRAME_LEN_OFFSET		8
+#define SIRFSOC_USP_RX_SHIFTER_LEN_OFFSET	16
+#define SIRFSOC_USP_RX_CLK_DIVISOR_OFFSET	24
+#define SIRFSOC_USP_ASYNC_DIV2_MASK		0x3f
+#define SIRFSOC_USP_ASYNC_DIV2_OFFSET		16
+
+/* USP-UART Common */
+#define SIRFSOC_UART_RX_TIMEOUT(br, to)	(((br) * (((to) + 999) / 1000)) / 1000)
+#define SIRFUART_RECV_TIMEOUT_VALUE(x)	\
+				(((x) > 0xFFFF) ? 0xFFFF : ((x) & 0xFFFF))
+#define SIRFUART_RECV_TIMEOUT(port, x)	\
+		(((port)->line > 2) ? (x & 0xFFFF) : ((x) & 0xFFFF) << 16)
+
+#define SIRFUART_FIFO_THD(port)		((port->line) == 1 ? 16 : 64)
+#define SIRFUART_ERR_INT_STAT(port, unit_st)			\
+				(uint_st->sirfsoc_rx_oflow |		\
+				uint_st->sirfsoc_frm_err |		\
+				uint_st->sirfsoc_rxd_brk |		\
+		((port->line > 2) ? 0 : uint_st->sirfsoc_parity_err))
+#define SIRFUART_RX_IO_INT_EN(port, uint_en)				\
+				(uint_en->sirfsoc_rx_timeout_en |\
+				 uint_en->sirfsoc_rxfifo_thd_en |\
+				 uint_en->sirfsoc_rxfifo_full_en |\
+				 uint_en->sirfsoc_frm_err_en |\
+				 uint_en->sirfsoc_rx_oflow_en |\
+				 uint_en->sirfsoc_rxd_brk_en |\
+		((port->line > 2) ? 0 : uint_en->sirfsoc_parity_err_en))
+#define SIRFUART_RX_IO_INT_ST(uint_st)				\
+				(uint_st->sirfsoc_rx_timeout |\
+				 uint_st->sirfsoc_rxfifo_thd |\
+				 uint_st->sirfsoc_rxfifo_full)
+#define SIRFUART_CTS_INT_ST(uint_st)	(uint_st->sirfsoc_cts)
+#define SIRFUART_RX_DMA_INT_EN(port, uint_en)				\
+				(uint_en->sirfsoc_rx_timeout_en |\
+				 uint_en->sirfsoc_frm_err_en |\
+				 uint_en->sirfsoc_rx_oflow_en |\
+				 uint_en->sirfsoc_rxd_brk_en |\
+		((port->line > 2) ? 0 : uint_en->sirfsoc_parity_err_en))
 /* Generic Definitions */
 /* Generic Definitions */
 #define SIRFSOC_UART_NAME			"ttySiRF"
 #define SIRFSOC_UART_NAME			"ttySiRF"
 #define SIRFSOC_UART_MAJOR			0
 #define SIRFSOC_UART_MAJOR			0
 #define SIRFSOC_UART_MINOR			0
 #define SIRFSOC_UART_MINOR			0
 #define SIRFUART_PORT_NAME			"sirfsoc-uart"
 #define SIRFUART_PORT_NAME			"sirfsoc-uart"
 #define SIRFUART_MAP_SIZE			0x200
 #define SIRFUART_MAP_SIZE			0x200
-#define SIRFSOC_UART_NR				5
+#define SIRFSOC_UART_NR				6
 #define SIRFSOC_PORT_TYPE			0xa5
 #define SIRFSOC_PORT_TYPE			0xa5
 
 
 /* Baud Rate Calculation */
 /* Baud Rate Calculation */
@@ -151,19 +377,80 @@
 #define SIRF_SAMPLE_DIV_MASK			0x3f0000
 #define SIRF_SAMPLE_DIV_MASK			0x3f0000
 #define SIRF_BAUD_RATE_SUPPORT_NR		18
 #define SIRF_BAUD_RATE_SUPPORT_NR		18
 
 
+/* Uart Common Use Macro*/
+#define SIRFSOC_RX_DMA_BUF_SIZE	256
+#define BYTES_TO_ALIGN(dma_addr)	((unsigned long)(dma_addr) & 0x3)
+#define LOOP_DMA_BUFA_FILL	1
+#define LOOP_DMA_BUFB_FILL	2
+#define TX_TRAN_PIO		1
+#define TX_TRAN_DMA		2
+/* Uart Fifo Level Chk */
+#define SIRFUART_TX_FIFO_SC_OFFSET	0
+#define SIRFUART_TX_FIFO_LC_OFFSET	10
+#define SIRFUART_TX_FIFO_HC_OFFSET	20
+#define SIRFUART_TX_FIFO_CHK_SC(line, value) ((((line) == 1) ? (value & 0x3) :\
+				(value & 0x1f)) << SIRFUART_TX_FIFO_SC_OFFSET)
+#define SIRFUART_TX_FIFO_CHK_LC(line, value) ((((line) == 1) ? (value & 0x3) :\
+				(value & 0x1f)) << SIRFUART_TX_FIFO_LC_OFFSET)
+#define SIRFUART_TX_FIFO_CHK_HC(line, value) ((((line) == 1) ? (value & 0x3) :\
+				(value & 0x1f)) << SIRFUART_TX_FIFO_HC_OFFSET)
+
+#define SIRFUART_RX_FIFO_CHK_SC SIRFUART_TX_FIFO_CHK_SC
+#define	SIRFUART_RX_FIFO_CHK_LC SIRFUART_TX_FIFO_CHK_LC
+#define SIRFUART_RX_FIFO_CHK_HC SIRFUART_TX_FIFO_CHK_HC
+/* Indicate how many buffers used */
+#define SIRFSOC_RX_LOOP_BUF_CNT		2
+
+/* Indicate if DMA channel valid */
+#define IS_DMA_CHAN_VALID(x)	((x) != -1)
+#define UNVALID_DMA_CHAN	-1
 /* For Fast Baud Rate Calculation */
 /* For Fast Baud Rate Calculation */
 struct sirfsoc_baudrate_to_regv {
 struct sirfsoc_baudrate_to_regv {
 	unsigned int baud_rate;
 	unsigned int baud_rate;
 	unsigned int reg_val;
 	unsigned int reg_val;
 };
 };
 
 
+enum sirfsoc_tx_state {
+	TX_DMA_IDLE,
+	TX_DMA_RUNNING,
+	TX_DMA_PAUSE,
+};
+
+struct sirfsoc_loop_buffer {
+	struct circ_buf			xmit;
+	dma_cookie_t			cookie;
+	struct dma_async_tx_descriptor	*desc;
+	dma_addr_t			dma_addr;
+};
+
 struct sirfsoc_uart_port {
 struct sirfsoc_uart_port {
-	unsigned char			hw_flow_ctrl;
-	unsigned char			ms_enabled;
+	bool				hw_flow_ctrl;
+	bool				ms_enabled;
 
 
 	struct uart_port		port;
 	struct uart_port		port;
-	struct pinctrl			*p;
 	struct clk			*clk;
 	struct clk			*clk;
+	/* for SiRFmarco, there are SET/CLR for UART_INT_EN */
+	bool				is_marco;
+	struct sirfsoc_uart_register	*uart_reg;
+	int				rx_dma_no;
+	int				tx_dma_no;
+	struct dma_chan			*rx_dma_chan;
+	struct dma_chan			*tx_dma_chan;
+	dma_addr_t			tx_dma_addr;
+	struct dma_async_tx_descriptor	*tx_dma_desc;
+	spinlock_t			rx_lock;
+	spinlock_t			tx_lock;
+	struct tasklet_struct		rx_dma_complete_tasklet;
+	struct tasklet_struct		rx_tmo_process_tasklet;
+	unsigned int			rx_io_count;
+	unsigned long			transfer_size;
+	enum sirfsoc_tx_state		tx_dma_state;
+	unsigned int			cts_gpio;
+	unsigned int			rts_gpio;
+
+	struct sirfsoc_loop_buffer	rx_dma_items[SIRFSOC_RX_LOOP_BUF_CNT];
+	int				rx_completed;
+	int				rx_issued;
 };
 };
 
 
 /* Hardware Flow Control */
 /* Hardware Flow Control */

+ 932 - 0
drivers/tty/serial/st-asc.c

@@ -0,0 +1,932 @@
+/*
+ * st-asc.c: ST Asynchronous serial controller (ASC) driver
+ *
+ * Copyright (C) 2003-2013 STMicroelectronics (R&D) Limited
+ *
+ * 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.
+ *
+ */
+
+#if defined(CONFIG_SERIAL_ST_ASC_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/module.h>
+#include <linux/serial.h>
+#include <linux/console.h>
+#include <linux/sysrq.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/pm_runtime.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/serial_core.h>
+#include <linux/clk.h>
+
+#define DRIVER_NAME "st-asc"
+#define ASC_SERIAL_NAME "ttyAS"
+#define ASC_FIFO_SIZE 16
+#define ASC_MAX_PORTS 8
+
+struct asc_port {
+	struct uart_port port;
+	struct clk *clk;
+	unsigned int hw_flow_control:1;
+	unsigned int force_m1:1;
+};
+
+static struct asc_port asc_ports[ASC_MAX_PORTS];
+static struct uart_driver asc_uart_driver;
+
+/*---- UART Register definitions ------------------------------*/
+
+/* Register offsets */
+
+#define ASC_BAUDRATE			0x00
+#define ASC_TXBUF			0x04
+#define ASC_RXBUF			0x08
+#define ASC_CTL				0x0C
+#define ASC_INTEN			0x10
+#define ASC_STA				0x14
+#define ASC_GUARDTIME			0x18
+#define ASC_TIMEOUT			0x1C
+#define ASC_TXRESET			0x20
+#define ASC_RXRESET			0x24
+#define ASC_RETRIES			0x28
+
+/* ASC_RXBUF */
+#define ASC_RXBUF_PE			0x100
+#define ASC_RXBUF_FE			0x200
+/**
+ * Some of status comes from higher bits of the character and some come from
+ * the status register. Combining both of them in to single status using dummy
+ * bits.
+ */
+#define ASC_RXBUF_DUMMY_RX		0x10000
+#define ASC_RXBUF_DUMMY_BE		0x20000
+#define ASC_RXBUF_DUMMY_OE		0x40000
+
+/* ASC_CTL */
+
+#define ASC_CTL_MODE_MSK		0x0007
+#define  ASC_CTL_MODE_8BIT		0x0001
+#define  ASC_CTL_MODE_7BIT_PAR		0x0003
+#define  ASC_CTL_MODE_9BIT		0x0004
+#define  ASC_CTL_MODE_8BIT_WKUP		0x0005
+#define  ASC_CTL_MODE_8BIT_PAR		0x0007
+#define ASC_CTL_STOP_MSK		0x0018
+#define  ASC_CTL_STOP_HALFBIT		0x0000
+#define  ASC_CTL_STOP_1BIT		0x0008
+#define  ASC_CTL_STOP_1_HALFBIT		0x0010
+#define  ASC_CTL_STOP_2BIT		0x0018
+#define ASC_CTL_PARITYODD		0x0020
+#define ASC_CTL_LOOPBACK		0x0040
+#define ASC_CTL_RUN			0x0080
+#define ASC_CTL_RXENABLE		0x0100
+#define ASC_CTL_SCENABLE		0x0200
+#define ASC_CTL_FIFOENABLE		0x0400
+#define ASC_CTL_CTSENABLE		0x0800
+#define ASC_CTL_BAUDMODE		0x1000
+
+/* ASC_GUARDTIME */
+
+#define ASC_GUARDTIME_MSK		0x00FF
+
+/* ASC_INTEN */
+
+#define ASC_INTEN_RBE			0x0001
+#define ASC_INTEN_TE			0x0002
+#define ASC_INTEN_THE			0x0004
+#define ASC_INTEN_PE			0x0008
+#define ASC_INTEN_FE			0x0010
+#define ASC_INTEN_OE			0x0020
+#define ASC_INTEN_TNE			0x0040
+#define ASC_INTEN_TOI			0x0080
+#define ASC_INTEN_RHF			0x0100
+
+/* ASC_RETRIES */
+
+#define ASC_RETRIES_MSK			0x00FF
+
+/* ASC_RXBUF */
+
+#define ASC_RXBUF_MSK			0x03FF
+
+/* ASC_STA */
+
+#define ASC_STA_RBF			0x0001
+#define ASC_STA_TE			0x0002
+#define ASC_STA_THE			0x0004
+#define ASC_STA_PE			0x0008
+#define ASC_STA_FE			0x0010
+#define ASC_STA_OE			0x0020
+#define ASC_STA_TNE			0x0040
+#define ASC_STA_TOI			0x0080
+#define ASC_STA_RHF			0x0100
+#define ASC_STA_TF			0x0200
+#define ASC_STA_NKD			0x0400
+
+/* ASC_TIMEOUT */
+
+#define ASC_TIMEOUT_MSK			0x00FF
+
+/* ASC_TXBUF */
+
+#define ASC_TXBUF_MSK			0x01FF
+
+/*---- Inline function definitions ---------------------------*/
+
+static inline struct asc_port *to_asc_port(struct uart_port *port)
+{
+	return container_of(port, struct asc_port, port);
+}
+
+static inline u32 asc_in(struct uart_port *port, u32 offset)
+{
+	return readl(port->membase + offset);
+}
+
+static inline void asc_out(struct uart_port *port, u32 offset, u32 value)
+{
+	writel(value, port->membase + offset);
+}
+
+/*
+ * Some simple utility functions to enable and disable interrupts.
+ * Note that these need to be called with interrupts disabled.
+ */
+static inline void asc_disable_tx_interrupts(struct uart_port *port)
+{
+	u32 intenable = asc_in(port, ASC_INTEN) & ~ASC_INTEN_THE;
+	asc_out(port, ASC_INTEN, intenable);
+	(void)asc_in(port, ASC_INTEN);	/* Defeat bus write posting */
+}
+
+static inline void asc_enable_tx_interrupts(struct uart_port *port)
+{
+	u32 intenable = asc_in(port, ASC_INTEN) | ASC_INTEN_THE;
+	asc_out(port, ASC_INTEN, intenable);
+}
+
+static inline void asc_disable_rx_interrupts(struct uart_port *port)
+{
+	u32 intenable = asc_in(port, ASC_INTEN) & ~ASC_INTEN_RBE;
+	asc_out(port, ASC_INTEN, intenable);
+	(void)asc_in(port, ASC_INTEN);	/* Defeat bus write posting */
+}
+
+static inline void asc_enable_rx_interrupts(struct uart_port *port)
+{
+	u32 intenable = asc_in(port, ASC_INTEN) | ASC_INTEN_RBE;
+	asc_out(port, ASC_INTEN, intenable);
+}
+
+static inline u32 asc_txfifo_is_empty(struct uart_port *port)
+{
+	return asc_in(port, ASC_STA) & ASC_STA_TE;
+}
+
+static inline int asc_txfifo_is_full(struct uart_port *port)
+{
+	return asc_in(port, ASC_STA) & ASC_STA_TF;
+}
+
+static inline const char *asc_port_name(struct uart_port *port)
+{
+	return to_platform_device(port->dev)->name;
+}
+
+/*----------------------------------------------------------------------*/
+
+/*
+ * This section contains code to support the use of the ASC as a
+ * generic serial port.
+ */
+
+static inline unsigned asc_hw_txroom(struct uart_port *port)
+{
+	u32 status = asc_in(port, ASC_STA);
+
+	if (status & ASC_STA_THE)
+		return port->fifosize / 2;
+	else if (!(status & ASC_STA_TF))
+		return 1;
+
+	return 0;
+}
+
+/*
+ * Start transmitting chars.
+ * This is called from both interrupt and task level.
+ * Either way interrupts are disabled.
+ */
+static void asc_transmit_chars(struct uart_port *port)
+{
+	struct circ_buf *xmit = &port->state->xmit;
+	int txroom;
+	unsigned char c;
+
+	txroom = asc_hw_txroom(port);
+
+	if ((txroom != 0) && port->x_char) {
+		c = port->x_char;
+		port->x_char = 0;
+		asc_out(port, ASC_TXBUF, c);
+		port->icount.tx++;
+		txroom = asc_hw_txroom(port);
+	}
+
+	if (uart_tx_stopped(port)) {
+		/*
+		 * We should try and stop the hardware here, but I
+		 * don't think the ASC has any way to do that.
+		 */
+		asc_disable_tx_interrupts(port);
+		return;
+	}
+
+	if (uart_circ_empty(xmit)) {
+		asc_disable_tx_interrupts(port);
+		return;
+	}
+
+	if (txroom == 0)
+		return;
+
+	do {
+		c = xmit->buf[xmit->tail];
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		asc_out(port, ASC_TXBUF, c);
+		port->icount.tx++;
+		txroom--;
+	} while ((txroom > 0) && (!uart_circ_empty(xmit)));
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(port);
+
+	if (uart_circ_empty(xmit))
+		asc_disable_tx_interrupts(port);
+}
+
+static void asc_receive_chars(struct uart_port *port)
+{
+	struct tty_port *tport = &port->state->port;
+	unsigned long status;
+	unsigned long c = 0;
+	char flag;
+
+	if (port->irq_wake)
+		pm_wakeup_event(tport->tty->dev, 0);
+
+	while ((status = asc_in(port, ASC_STA)) & ASC_STA_RBF) {
+		c = asc_in(port, ASC_RXBUF) | ASC_RXBUF_DUMMY_RX;
+		flag = TTY_NORMAL;
+		port->icount.rx++;
+
+		if ((c & (ASC_RXBUF_FE | ASC_RXBUF_PE)) ||
+			status & ASC_STA_OE) {
+
+			if (c & ASC_RXBUF_FE) {
+				if (c == ASC_RXBUF_FE) {
+					port->icount.brk++;
+					if (uart_handle_break(port))
+						continue;
+					c |= ASC_RXBUF_DUMMY_BE;
+				} else {
+					port->icount.frame++;
+				}
+			} else if (c & ASC_RXBUF_PE) {
+				port->icount.parity++;
+			}
+			/*
+			 * Reading any data from the RX FIFO clears the
+			 * overflow error condition.
+			 */
+			if (status & ASC_STA_OE) {
+				port->icount.overrun++;
+				c |= ASC_RXBUF_DUMMY_OE;
+			}
+
+			c &= port->read_status_mask;
+
+			if (c & ASC_RXBUF_DUMMY_BE)
+				flag = TTY_BREAK;
+			else if (c & ASC_RXBUF_PE)
+				flag = TTY_PARITY;
+			else if (c & ASC_RXBUF_FE)
+				flag = TTY_FRAME;
+		}
+
+		if (uart_handle_sysrq_char(port, c))
+			continue;
+
+		uart_insert_char(port, c, ASC_RXBUF_DUMMY_OE, c & 0xff, flag);
+	}
+
+	/* Tell the rest of the system the news. New characters! */
+	tty_flip_buffer_push(tport);
+}
+
+static irqreturn_t asc_interrupt(int irq, void *ptr)
+{
+	struct uart_port *port = ptr;
+	u32 status;
+
+	spin_lock(&port->lock);
+
+	status = asc_in(port, ASC_STA);
+
+	if (status & ASC_STA_RBF) {
+		/* Receive FIFO not empty */
+		asc_receive_chars(port);
+	}
+
+	if ((status & ASC_STA_THE) &&
+	    (asc_in(port, ASC_INTEN) & ASC_INTEN_THE)) {
+		/* Transmitter FIFO at least half empty */
+		asc_transmit_chars(port);
+	}
+
+	spin_unlock(&port->lock);
+
+	return IRQ_HANDLED;
+}
+
+/*----------------------------------------------------------------------*/
+
+/*
+ * UART Functions
+ */
+
+static unsigned int asc_tx_empty(struct uart_port *port)
+{
+	return asc_txfifo_is_empty(port) ? TIOCSER_TEMT : 0;
+}
+
+static void asc_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	/*
+	 * This routine is used for seting signals of: DTR, DCD, CTS/RTS
+	 * We use ASC's hardware for CTS/RTS, so don't need any for that.
+	 * Some boards have DTR and DCD implemented using PIO pins,
+	 * code to do this should be hooked in here.
+	 */
+}
+
+static unsigned int asc_get_mctrl(struct uart_port *port)
+{
+	/*
+	 * This routine is used for geting signals of: DTR, DCD, DSR, RI,
+	 * and CTS/RTS
+	 */
+	return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS;
+}
+
+/* There are probably characters waiting to be transmitted. */
+static void asc_start_tx(struct uart_port *port)
+{
+	struct circ_buf *xmit = &port->state->xmit;
+
+	if (!uart_circ_empty(xmit))
+		asc_enable_tx_interrupts(port);
+}
+
+/* Transmit stop */
+static void asc_stop_tx(struct uart_port *port)
+{
+	asc_disable_tx_interrupts(port);
+}
+
+/* Receive stop */
+static void asc_stop_rx(struct uart_port *port)
+{
+	asc_disable_rx_interrupts(port);
+}
+
+/* Force modem status interrupts on */
+static void asc_enable_ms(struct uart_port *port)
+{
+	/* Nothing here yet .. */
+}
+
+/* Handle breaks - ignored by us */
+static void asc_break_ctl(struct uart_port *port, int break_state)
+{
+	/* Nothing here yet .. */
+}
+
+/*
+ * Enable port for reception.
+ */
+static int asc_startup(struct uart_port *port)
+{
+	if (request_irq(port->irq, asc_interrupt, IRQF_NO_SUSPEND,
+			asc_port_name(port), port)) {
+		dev_err(port->dev, "cannot allocate irq.\n");
+		return -ENODEV;
+	}
+
+	asc_transmit_chars(port);
+	asc_enable_rx_interrupts(port);
+
+	return 0;
+}
+
+static void asc_shutdown(struct uart_port *port)
+{
+	asc_disable_tx_interrupts(port);
+	asc_disable_rx_interrupts(port);
+	free_irq(port->irq, port);
+}
+
+static void asc_pm(struct uart_port *port, unsigned int state,
+		unsigned int oldstate)
+{
+	struct asc_port *ascport = to_asc_port(port);
+	unsigned long flags = 0;
+	u32 ctl;
+
+	switch (state) {
+	case UART_PM_STATE_ON:
+		clk_prepare_enable(ascport->clk);
+		break;
+	case UART_PM_STATE_OFF:
+		/*
+		 * Disable the ASC baud rate generator, which is as close as
+		 * we can come to turning it off. Note this is not called with
+		 * the port spinlock held.
+		 */
+		spin_lock_irqsave(&port->lock, flags);
+		ctl = asc_in(port, ASC_CTL) & ~ASC_CTL_RUN;
+		asc_out(port, ASC_CTL, ctl);
+		spin_unlock_irqrestore(&port->lock, flags);
+		clk_disable_unprepare(ascport->clk);
+		break;
+	}
+}
+
+static void asc_set_termios(struct uart_port *port, struct ktermios *termios,
+			    struct ktermios *old)
+{
+	struct asc_port *ascport = to_asc_port(port);
+	unsigned int baud;
+	u32 ctrl_val;
+	tcflag_t cflag;
+	unsigned long flags;
+
+	/* Update termios to reflect hardware capabilities */
+	termios->c_cflag &= ~(CMSPAR |
+			 (ascport->hw_flow_control ? 0 : CRTSCTS));
+
+	port->uartclk = clk_get_rate(ascport->clk);
+
+	baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
+	cflag = termios->c_cflag;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	/* read control register */
+	ctrl_val = asc_in(port, ASC_CTL);
+
+	/* stop serial port and reset value */
+	asc_out(port, ASC_CTL, (ctrl_val & ~ASC_CTL_RUN));
+	ctrl_val = ASC_CTL_RXENABLE | ASC_CTL_FIFOENABLE;
+
+	/* reset fifo rx & tx */
+	asc_out(port, ASC_TXRESET, 1);
+	asc_out(port, ASC_RXRESET, 1);
+
+	/* set character length */
+	if ((cflag & CSIZE) == CS7) {
+		ctrl_val |= ASC_CTL_MODE_7BIT_PAR;
+	} else {
+		ctrl_val |= (cflag & PARENB) ?  ASC_CTL_MODE_8BIT_PAR :
+						ASC_CTL_MODE_8BIT;
+	}
+
+	/* set stop bit */
+	ctrl_val |= (cflag & CSTOPB) ? ASC_CTL_STOP_2BIT : ASC_CTL_STOP_1BIT;
+
+	/* odd parity */
+	if (cflag & PARODD)
+		ctrl_val |= ASC_CTL_PARITYODD;
+
+	/* hardware flow control */
+	if ((cflag & CRTSCTS))
+		ctrl_val |= ASC_CTL_CTSENABLE;
+
+	if ((baud < 19200) && !ascport->force_m1) {
+		asc_out(port, ASC_BAUDRATE, (port->uartclk / (16 * baud)));
+	} else {
+		/*
+		 * MODE 1: recommended for high bit rates (above 19.2K)
+		 *
+		 *                   baudrate * 16 * 2^16
+		 * ASCBaudRate =   ------------------------
+		 *                          inputclock
+		 *
+		 * However to keep the maths inside 32bits we divide top and
+		 * bottom by 64. The +1 is to avoid a divide by zero if the
+		 * input clock rate is something unexpected.
+		 */
+		u32 counter = (baud * 16384) / ((port->uartclk / 64) + 1);
+		asc_out(port, ASC_BAUDRATE, counter);
+		ctrl_val |= ASC_CTL_BAUDMODE;
+	}
+
+	uart_update_timeout(port, cflag, baud);
+
+	ascport->port.read_status_mask = ASC_RXBUF_DUMMY_OE;
+	if (termios->c_iflag & INPCK)
+		ascport->port.read_status_mask |= ASC_RXBUF_FE | ASC_RXBUF_PE;
+	if (termios->c_iflag & (BRKINT | PARMRK))
+		ascport->port.read_status_mask |= ASC_RXBUF_DUMMY_BE;
+
+	/*
+	 * Characters to ignore
+	 */
+	ascport->port.ignore_status_mask = 0;
+	if (termios->c_iflag & IGNPAR)
+		ascport->port.ignore_status_mask |= ASC_RXBUF_FE | ASC_RXBUF_PE;
+	if (termios->c_iflag & IGNBRK) {
+		ascport->port.ignore_status_mask |= ASC_RXBUF_DUMMY_BE;
+		/*
+		 * If we're ignoring parity and break indicators,
+		 * ignore overruns too (for real raw support).
+		 */
+		if (termios->c_iflag & IGNPAR)
+			ascport->port.ignore_status_mask |= ASC_RXBUF_DUMMY_OE;
+	}
+
+	/*
+	 * Ignore all characters if CREAD is not set.
+	 */
+	if (!(termios->c_cflag & CREAD))
+		ascport->port.ignore_status_mask |= ASC_RXBUF_DUMMY_RX;
+
+	/* Set the timeout */
+	asc_out(port, ASC_TIMEOUT, 20);
+
+	/* write final value and enable port */
+	asc_out(port, ASC_CTL, (ctrl_val | ASC_CTL_RUN));
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static const char *asc_type(struct uart_port *port)
+{
+	return (port->type == PORT_ASC) ? DRIVER_NAME : NULL;
+}
+
+static void asc_release_port(struct uart_port *port)
+{
+}
+
+static int asc_request_port(struct uart_port *port)
+{
+	return 0;
+}
+
+/*
+ * Called when the port is opened, and UPF_BOOT_AUTOCONF flag is set
+ * Set type field if successful
+ */
+static void asc_config_port(struct uart_port *port, int flags)
+{
+	if ((flags & UART_CONFIG_TYPE))
+		port->type = PORT_ASC;
+}
+
+static int
+asc_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+	/* No user changeable parameters */
+	return -EINVAL;
+}
+
+#ifdef CONFIG_CONSOLE_POLL
+/*
+ * Console polling routines for writing and reading from the uart while
+ * in an interrupt or debug context (i.e. kgdb).
+ */
+
+static int asc_get_poll_char(struct uart_port *port)
+{
+	if (!(asc_in(port, ASC_STA) & ASC_STA_RBF))
+		return NO_POLL_CHAR;
+
+	return asc_in(port, ASC_RXBUF);
+}
+
+static void asc_put_poll_char(struct uart_port *port, unsigned char c)
+{
+	while (asc_txfifo_is_full(port))
+		cpu_relax();
+	asc_out(port, ASC_TXBUF, c);
+}
+
+#endif /* CONFIG_CONSOLE_POLL */
+
+/*---------------------------------------------------------------------*/
+
+static struct uart_ops asc_uart_ops = {
+	.tx_empty	= asc_tx_empty,
+	.set_mctrl	= asc_set_mctrl,
+	.get_mctrl	= asc_get_mctrl,
+	.start_tx	= asc_start_tx,
+	.stop_tx	= asc_stop_tx,
+	.stop_rx	= asc_stop_rx,
+	.enable_ms	= asc_enable_ms,
+	.break_ctl	= asc_break_ctl,
+	.startup	= asc_startup,
+	.shutdown	= asc_shutdown,
+	.set_termios	= asc_set_termios,
+	.type		= asc_type,
+	.release_port	= asc_release_port,
+	.request_port	= asc_request_port,
+	.config_port	= asc_config_port,
+	.verify_port	= asc_verify_port,
+	.pm		= asc_pm,
+#ifdef CONFIG_CONSOLE_POLL
+	.poll_get_char = asc_get_poll_char,
+	.poll_put_char = asc_put_poll_char,
+#endif /* CONFIG_CONSOLE_POLL */
+};
+
+static int asc_init_port(struct asc_port *ascport,
+			  struct platform_device *pdev)
+{
+	struct uart_port *port = &ascport->port;
+	struct resource *res;
+
+	port->iotype	= UPIO_MEM;
+	port->flags	= UPF_BOOT_AUTOCONF;
+	port->ops	= &asc_uart_ops;
+	port->fifosize	= ASC_FIFO_SIZE;
+	port->dev	= &pdev->dev;
+	port->irq	= platform_get_irq(pdev, 0);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	port->membase = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(port->membase))
+		return PTR_ERR(port->membase);
+	port->mapbase = res->start;
+
+	spin_lock_init(&port->lock);
+
+	ascport->clk = devm_clk_get(&pdev->dev, NULL);
+
+	if (WARN_ON(IS_ERR(ascport->clk)))
+		return -EINVAL;
+	/* ensure that clk rate is correct by enabling the clk */
+	clk_prepare_enable(ascport->clk);
+	ascport->port.uartclk = clk_get_rate(ascport->clk);
+	WARN_ON(ascport->port.uartclk == 0);
+	clk_disable_unprepare(ascport->clk);
+
+	return 0;
+}
+
+static struct asc_port *asc_of_get_asc_port(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	int id;
+
+	if (!np)
+		return NULL;
+
+	id = of_alias_get_id(np, ASC_SERIAL_NAME);
+
+	if (id < 0)
+		id = 0;
+
+	if (WARN_ON(id >= ASC_MAX_PORTS))
+		return NULL;
+
+	asc_ports[id].hw_flow_control = of_property_read_bool(np,
+							"st,hw-flow-control");
+	asc_ports[id].force_m1 =  of_property_read_bool(np, "st,force_m1");
+	asc_ports[id].port.line = id;
+	return &asc_ports[id];
+}
+
+#ifdef CONFIG_OF
+static struct of_device_id asc_match[] = {
+	{ .compatible = "st,asc", },
+	{},
+};
+
+MODULE_DEVICE_TABLE(of, asc_match);
+#endif
+
+static int asc_serial_probe(struct platform_device *pdev)
+{
+	int ret;
+	struct asc_port *ascport;
+
+	ascport = asc_of_get_asc_port(pdev);
+	if (!ascport)
+		return -ENODEV;
+
+	ret = asc_init_port(ascport, pdev);
+	if (ret)
+		return ret;
+
+	ret = uart_add_one_port(&asc_uart_driver, &ascport->port);
+	if (ret)
+		return ret;
+
+	platform_set_drvdata(pdev, &ascport->port);
+
+	return 0;
+}
+
+static int asc_serial_remove(struct platform_device *pdev)
+{
+	struct uart_port *port = platform_get_drvdata(pdev);
+
+	return uart_remove_one_port(&asc_uart_driver, port);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int asc_serial_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct uart_port *port = platform_get_drvdata(pdev);
+
+	return uart_suspend_port(&asc_uart_driver, port);
+}
+
+static int asc_serial_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct uart_port *port = platform_get_drvdata(pdev);
+
+	return uart_resume_port(&asc_uart_driver, port);
+}
+
+#endif /* CONFIG_PM_SLEEP */
+
+/*----------------------------------------------------------------------*/
+
+#ifdef CONFIG_SERIAL_ST_ASC_CONSOLE
+static void asc_console_putchar(struct uart_port *port, int ch)
+{
+	unsigned int timeout = 1000000;
+
+	/* Wait for upto 1 second in case flow control is stopping us. */
+	while (--timeout && asc_txfifo_is_full(port))
+		udelay(1);
+
+	asc_out(port, ASC_TXBUF, ch);
+}
+
+/*
+ *  Print a string to the serial port trying not to disturb
+ *  any possible real use of the port...
+ */
+
+static void asc_console_write(struct console *co, const char *s, unsigned count)
+{
+	struct uart_port *port = &asc_ports[co->index].port;
+	unsigned long flags;
+	unsigned long timeout = 1000000;
+	int locked = 1;
+	u32 intenable;
+
+	local_irq_save(flags);
+	if (port->sysrq)
+		locked = 0; /* asc_interrupt has already claimed the lock */
+	else if (oops_in_progress)
+		locked = spin_trylock(&port->lock);
+	else
+		spin_lock(&port->lock);
+
+	/*
+	 * Disable interrupts so we don't get the IRQ line bouncing
+	 * up and down while interrupts are disabled.
+	 */
+	intenable = asc_in(port, ASC_INTEN);
+	asc_out(port, ASC_INTEN, 0);
+	(void)asc_in(port, ASC_INTEN);	/* Defeat bus write posting */
+
+	uart_console_write(port, s, count, asc_console_putchar);
+
+	while (--timeout && !asc_txfifo_is_empty(port))
+		udelay(1);
+
+	asc_out(port, ASC_INTEN, intenable);
+
+	if (locked)
+		spin_unlock(&port->lock);
+	local_irq_restore(flags);
+}
+
+static int asc_console_setup(struct console *co, char *options)
+{
+	struct asc_port *ascport;
+	int baud = 9600;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+
+	if (co->index >= ASC_MAX_PORTS)
+		return -ENODEV;
+
+	ascport = &asc_ports[co->index];
+
+	/*
+	 * This driver does not support early console initialization
+	 * (use ARM early printk support instead), so we only expect
+	 * this to be called during the uart port registration when the
+	 * driver gets probed and the port should be mapped at that point.
+	 */
+	BUG_ON(ascport->port.mapbase == 0 || ascport->port.membase == NULL);
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+	return uart_set_options(&ascport->port, co, baud, parity, bits, flow);
+}
+
+static struct console asc_console = {
+	.name		= ASC_SERIAL_NAME,
+	.device		= uart_console_device,
+	.write		= asc_console_write,
+	.setup		= asc_console_setup,
+	.flags		= CON_PRINTBUFFER,
+	.index		= -1,
+	.data		= &asc_uart_driver,
+};
+
+#define ASC_SERIAL_CONSOLE (&asc_console)
+
+#else
+#define ASC_SERIAL_CONSOLE NULL
+#endif /* CONFIG_SERIAL_ST_ASC_CONSOLE */
+
+static struct uart_driver asc_uart_driver = {
+	.owner		= THIS_MODULE,
+	.driver_name	= DRIVER_NAME,
+	.dev_name	= ASC_SERIAL_NAME,
+	.major		= 0,
+	.minor		= 0,
+	.nr		= ASC_MAX_PORTS,
+	.cons		= ASC_SERIAL_CONSOLE,
+};
+
+static const struct dev_pm_ops asc_serial_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(asc_serial_suspend, asc_serial_resume)
+};
+
+static struct platform_driver asc_serial_driver = {
+	.probe		= asc_serial_probe,
+	.remove		= asc_serial_remove,
+	.driver	= {
+		.name	= DRIVER_NAME,
+		.pm	= &asc_serial_pm_ops,
+		.owner	= THIS_MODULE,
+		.of_match_table = of_match_ptr(asc_match),
+	},
+};
+
+static int __init asc_init(void)
+{
+	int ret;
+	static char banner[] __initdata =
+		KERN_INFO "STMicroelectronics ASC driver initialized\n";
+
+	printk(banner);
+
+	ret = uart_register_driver(&asc_uart_driver);
+	if (ret)
+		return ret;
+
+	ret = platform_driver_register(&asc_serial_driver);
+	if (ret)
+		uart_unregister_driver(&asc_uart_driver);
+
+	return ret;
+}
+
+static void __exit asc_exit(void)
+{
+	platform_driver_unregister(&asc_serial_driver);
+	uart_unregister_driver(&asc_uart_driver);
+}
+
+module_init(asc_init);
+module_exit(asc_exit);
+
+MODULE_ALIAS("platform:" DRIVER_NAME);
+MODULE_AUTHOR("STMicroelectronics (R&D) Limited");
+MODULE_DESCRIPTION("STMicroelectronics ASC serial port driver");
+MODULE_LICENSE("GPL");

+ 2 - 2
drivers/tty/serial/timbuart.c

@@ -162,7 +162,7 @@ static void timbuart_handle_tx_port(struct uart_port *port, u32 isr, u32 *ier)
 	dev_dbg(port->dev, "%s - leaving\n", __func__);
 	dev_dbg(port->dev, "%s - leaving\n", __func__);
 }
 }
 
 
-void timbuart_handle_rx_port(struct uart_port *port, u32 isr, u32 *ier)
+static void timbuart_handle_rx_port(struct uart_port *port, u32 isr, u32 *ier)
 {
 {
 	if (isr & RXFLAGS) {
 	if (isr & RXFLAGS) {
 		/* Some RX status is set */
 		/* Some RX status is set */
@@ -184,7 +184,7 @@ void timbuart_handle_rx_port(struct uart_port *port, u32 isr, u32 *ier)
 	dev_dbg(port->dev, "%s - leaving\n", __func__);
 	dev_dbg(port->dev, "%s - leaving\n", __func__);
 }
 }
 
 
-void timbuart_tasklet(unsigned long arg)
+static void timbuart_tasklet(unsigned long arg)
 {
 {
 	struct timbuart_port *uart = (struct timbuart_port *)arg;
 	struct timbuart_port *uart = (struct timbuart_port *)arg;
 	u32 isr, ier = 0;
 	u32 isr, ier = 0;

+ 1 - 1
drivers/tty/serial/vr41xx_siu.c

@@ -705,7 +705,7 @@ static int siu_init_ports(struct platform_device *pdev)
 {
 {
 	struct uart_port *port;
 	struct uart_port *port;
 	struct resource *res;
 	struct resource *res;
-	int *type = pdev->dev.platform_data;
+	int *type = dev_get_platdata(&pdev->dev);
 	int i;
 	int i;
 
 
 	if (!type)
 	if (!type)

+ 2 - 1
drivers/tty/serial/vt8500_serial.c

@@ -170,7 +170,9 @@ static void handle_rx(struct uart_port *port)
 			tty_insert_flip_char(tport, c, flag);
 			tty_insert_flip_char(tport, c, flag);
 	}
 	}
 
 
+	spin_unlock(&port->lock);
 	tty_flip_buffer_push(tport);
 	tty_flip_buffer_push(tport);
+	spin_lock(&port->lock);
 }
 }
 
 
 static void handle_tx(struct uart_port *port)
 static void handle_tx(struct uart_port *port)
@@ -630,7 +632,6 @@ static int vt8500_serial_remove(struct platform_device *pdev)
 {
 {
 	struct vt8500_port *vt8500_port = platform_get_drvdata(pdev);
 	struct vt8500_port *vt8500_port = platform_get_drvdata(pdev);
 
 
-	platform_set_drvdata(pdev, NULL);
 	clk_disable_unprepare(vt8500_port->clk);
 	clk_disable_unprepare(vt8500_port->clk);
 	uart_remove_one_port(&vt8500_uart_driver, &vt8500_port->uart);
 	uart_remove_one_port(&vt8500_uart_driver, &vt8500_port->uart);
 
 

Some files were not shown because too many files changed in this diff